diff --git a/.gitignore b/.gitignore index 1774c281..0df2db42 100755 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,11 @@ bin/ lib/ # Because apparently nobody has any self control *.orig + +*.suo +*.sdf +tools/MayaExporter/MayaExporter/x64/Debug/ +tools/MayaExporter/x64/Debug/ + +tools/MayaExporter/MayaExporter/Debug/ +tools/MayaExporter/MayaExporter/GeneratedFiles/ diff --git a/assets b/assets index e8174f63..e4dc9529 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e8174f630fc3242e15ada1346b42c72f44cbc854 +Subproject commit e4dc9529f2178d373808ddf487165fa50a641a78 diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 6e2d679e..b8625b73 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -10,7 +10,8 @@ #include "../Core/Ray.h" #include "../Core/AABB.h" -#include "../Rendering/RawModel.h" +#include "Rendering/RawModelCustom.h" +//#include "Rendering/RawModelAssimp.h" #include "../Core/Transform.h" #include "../Core/Entity.h" #include "../Core/EntityWrapper.h" diff --git a/include/Engine/Core/EComponentDeleted.h b/include/Engine/Core/EComponentDeleted.h new file mode 100644 index 00000000..6d9c468d --- /dev/null +++ b/include/Engine/Core/EComponentDeleted.h @@ -0,0 +1,21 @@ +#ifndef EComponentDeleted_h__ +#define EComponentDeleted_h__ + +#include "../Common.h" +#include "Event.h" +#include "Entity.h" + +namespace Events +{ + +struct ComponentDeleted : Event +{ + EntityID Entity; + std::string ComponentType; + // True if the component was deleted as a result of the entity it was attached to being deleted + bool Cascaded; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EEntityDeleted.h b/include/Engine/Core/EEntityDeleted.h new file mode 100644 index 00000000..80e20e17 --- /dev/null +++ b/include/Engine/Core/EEntityDeleted.h @@ -0,0 +1,19 @@ +#ifndef EEntityDeleted_h__ +#define EEntityDeleted_h__ + +#include "Event.h" +#include "Entity.h" + +namespace Events +{ + +struct EntityDeleted : Event +{ + EntityID DeletedEntity; + // True if the entity deletion was triggered because the entity's parent was deleted before it + bool Cascaded; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EPlayerDamage.h b/include/Engine/Core/EPlayerDamage.h index e0f2acd7..a7e135ce 100644 --- a/include/Engine/Core/EPlayerDamage.h +++ b/include/Engine/Core/EPlayerDamage.h @@ -2,15 +2,15 @@ #define EPlayerDamage_h__ #include "EventBroker.h" -#include "../Core/Entity.h" +#include "../Core/EntityWrapper.h" namespace Events { struct PlayerDamage : Event { - double DamageAmount; - EntityID PlayerDamagedID; + EntityWrapper Player; + double Damage; }; } diff --git a/include/Engine/Core/EPlayerSpawned.h b/include/Engine/Core/EPlayerSpawned.h new file mode 100644 index 00000000..fcfaef61 --- /dev/null +++ b/include/Engine/Core/EPlayerSpawned.h @@ -0,0 +1,20 @@ +#ifndef EPlayerSpawned_h__ +#define EPlayerSpawned_h__ + +#include "Core/Event.h" +#include "Core/EntityWrapper.h" + +namespace Events +{ + +struct PlayerSpawned : Event +{ + int PlayerID; + EntityWrapper Player; + EntityWrapper Spawner; + std::string PlayerName; +}; + +} + +#endif \ No newline at end of file diff --git a/include/Engine/Core/EShoot.h b/include/Engine/Core/EShoot.h index fd54122f..76821a24 100644 --- a/include/Engine/Core/EShoot.h +++ b/include/Engine/Core/EShoot.h @@ -2,16 +2,14 @@ #define EShoot_h__ #include "EventBroker.h" -#include "../Core/Entity.h" -#include "Engine/GLM.h" +#include "../Core/EntityWrapper.h" namespace Events { struct Shoot : Event { - //ID for who made the shot - EntityID shooter; + EntityWrapper Player; }; } diff --git a/include/Engine/Core/EntityWrapper.h b/include/Engine/Core/EntityWrapper.h index 3ba4e087..b2e0ce4c 100644 --- a/include/Engine/Core/EntityWrapper.h +++ b/include/Engine/Core/EntityWrapper.h @@ -23,8 +23,12 @@ struct EntityWrapper static const EntityWrapper Invalid; - bool HasComponent(const std::string& componentName); + const std::string Name(); + bool HasComponent(const std::string& componentType); EntityWrapper Parent(); + EntityWrapper FirstChildByName(const std::string& name); + EntityWrapper FirstParentWithComponent(const std::string& componentType); + bool IsChildOf(EntityWrapper potentialParent); bool Valid(); ComponentWrapper operator[](const char* componentName); @@ -32,6 +36,9 @@ struct EntityWrapper bool operator!=(const EntityWrapper& e) const; explicit operator EntityID() const; operator bool(); + +private: + EntityWrapper firstChildByNameRecursive(const std::string& name, EntityID parent); }; namespace std diff --git a/include/Engine/Core/ResourceManager.h b/include/Engine/Core/ResourceManager.h index 15a551e7..10529819 100644 --- a/include/Engine/Core/ResourceManager.h +++ b/include/Engine/Core/ResourceManager.h @@ -32,10 +32,10 @@ public: }; struct FailedLoadingException : public std::exception { - virtual const char* what() const throw() - { - return "Resource is failed to load."; - } + FailedLoadingException(char const* const _Message) + : std::exception(_Message) + { } + FailedLoadingException() :std::exception("Resource failed to load.") { }; }; // Pretend that this is a pure virtual function that you have to implement diff --git a/include/Engine/Core/Transform.h b/include/Engine/Core/Transform.h index b004f047..bd381e8b 100644 --- a/include/Engine/Core/Transform.h +++ b/include/Engine/Core/Transform.h @@ -3,13 +3,20 @@ #include "../GLM.h" #include "World.h" +#include "EntityWrapper.h" namespace Transform { +glm::mat4 AbsoluteTransformation(EntityWrapper entity); +glm::vec3 AbsolutePosition(EntityWrapper entity); glm::vec3 AbsolutePosition(World* world, EntityID entity); +glm::vec3 AbsoluteOrientationEuler(EntityWrapper entity); +glm::quat AbsoluteOrientation(EntityWrapper entity); glm::quat AbsoluteOrientation(World* world, EntityID entity); +glm::vec3 AbsoluteScale(EntityWrapper entity); glm::vec3 AbsoluteScale(World* world, EntityID entity); +glm::mat4 ModelMatrix(EntityWrapper entity); glm::mat4 ModelMatrix(EntityID entity, World* world); glm::vec3 TransformPoint(const glm::vec3& point, const glm::mat4& matrix); diff --git a/include/Engine/Core/World.h b/include/Engine/Core/World.h index b201d4ac..35394b9f 100644 --- a/include/Engine/Core/World.h +++ b/include/Engine/Core/World.h @@ -5,11 +5,15 @@ #include "Entity.h" #include "ObjectPool.h" #include "ComponentPool.h" +#include "EventBroker.h" class World { public: World() = default; + World(EventBroker* eventBroker) + : m_EventBroker(eventBroker) + { } ~World(); // Create empty entity @@ -46,6 +50,7 @@ public: std::string GetName(EntityID entity) const; private: + EventBroker* m_EventBroker = nullptr; EntityID m_CurrentEntityID = 0; std::unordered_map m_EntityParents; @@ -55,6 +60,7 @@ private: std::unordered_map m_EntityNames; EntityID generateEntityID(); + void deleteEntityRecursive(EntityID entity, bool cascaded = false); }; #endif \ No newline at end of file diff --git a/include/Engine/Editor/EditorCameraInputController.h b/include/Engine/Editor/EditorCameraInputController.h new file mode 100644 index 00000000..66c7952a --- /dev/null +++ b/include/Engine/Editor/EditorCameraInputController.h @@ -0,0 +1,115 @@ +#ifndef EditorCameraInputController_h__ +#define EditorCameraInputController_h__ + +#include +#include "../Input/FirstPersonInputController.h" +#include "../Core/EMousePress.h" +#include "../Core/EMouseRelease.h" +#include "../Core/EMouseScroll.h" +#include "../Core/ConfigFile.h" + +template +class EditorCameraInputController : public FirstPersonInputController +{ +public: + EditorCameraInputController(EventBroker* eventBroker, unsigned int playerID) + : FirstPersonInputController(eventBroker, playerID) + { + EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &EditorCameraInputController::OnMousePress); + EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &EditorCameraInputController::OnMouseRelease); + EVENT_SUBSCRIBE_MEMBER(m_EMouseScroll, &EditorCameraInputController::OnMouseScroll); + + m_Config = ResourceManager::Load("Config.ini"); + m_SpeedMultiplier = m_Config->Get("Editor.CameraSpeed", 3.f); + } + + virtual const glm::vec3 Movement() const override { return m_Movement * m_SpeedMultiplier; } + + void Enable() { m_Enabled = true; } + void Disable() { m_Enabled = false; } + + virtual bool OnCommand(const Events::InputCommand& e) override + { + if (glm::abs(e.Value) > 0 && !m_MouseLocked) { + return false; + } + + ImGuiIO& io = ImGui::GetIO(); + if (glm::abs(e.Value) > 0 && (io.WantCaptureKeyboard || io.WantCaptureMouse)) { + return false; + } + + if (e.Command == "Jump") { + if (e.Value > 0) { + m_Movement.y = glm::max(e.Value, 1.f); + } else { + m_Movement.y = 0.f; + } + } + + if (e.Command == "Crouch") { + if (e.Value > 0) { + m_Movement.y = glm::min(-e.Value, -1.f); + } else { + m_Movement.y = 0.f; + } + } + + if (e.Command == "Sprint") { + if (e.Value > 0) { + m_SpeedMultiplier *= 2.f; + } else { + m_SpeedMultiplier /= 2.f; + } + } + + return FirstPersonInputController::OnCommand(e); + } + +protected: + ConfigFile* m_Config; + bool m_Enabled = false; + float m_SpeedMultiplier = 1.f; + + EventRelay m_EMousePress; + bool OnMousePress(const Events::MousePress& e) + { + if (!m_Enabled) { + return false; + } + + if (e.Button == GLFW_MOUSE_BUTTON_2) { + ImGuiIO& io = ImGui::GetIO(); + if (!io.WantCaptureMouse) { + LockMouse(); + } + } + return true; + } + EventRelay m_EMouseRelease; + bool OnMouseRelease(const Events::MouseRelease& e) + { + if (!m_Enabled) { + return false; + } + + if (e.Button == GLFW_MOUSE_BUTTON_2) { + UnlockMouse(); + } + return true; + } + EventRelay m_EMouseScroll; + bool OnMouseScroll(const Events::MouseScroll& e) + { + if (!m_Enabled) { + return false; + } + + m_SpeedMultiplier += e.DeltaY * (0.1f * m_SpeedMultiplier); + m_Config->Set("Editor.CameraSpeed", m_SpeedMultiplier); + m_Config->SaveToDisk(); + return true; + } +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Editor/EditorSystem.h b/include/Engine/Editor/EditorSystem.h index b354ddf3..2db24ba3 100644 --- a/include/Engine/Editor/EditorSystem.h +++ b/include/Engine/Editor/EditorSystem.h @@ -1,7 +1,6 @@ #include "../Core/System.h" #include "../Rendering/IRenderer.h" #include "../Rendering/Camera.h" -#include "../Rendering/DebugCameraInputController.h" #include "../Rendering/ESetCamera.h" #include "../Core/World.h" #include "../Core/SystemPipeline.h" @@ -10,8 +9,10 @@ #include "../Core/EntityFileParser.h" #include "../Core/EntityFileWriter.h" #include "../Core/EMousePress.h" +#include "../Input/EInputCommand.h" #include "EditorGUI.h" #include "EditorStats.h" +#include "EditorCameraInputController.h" class EditorSystem : public ImpureSystem { @@ -21,18 +22,24 @@ public: void Update(double dt); + void Enable(); + void Disable(); + private: IRenderer* m_Renderer; RenderFrame* m_RenderFrame; World* m_EditorWorld; SystemPipeline* m_EditorWorldSystemPipeline; - Camera* m_EditorCamera; - EntityWrapper m_Camera = EntityWrapper::Invalid; - DebugCameraInputController* m_DebugCameraInputController; + //Camera* m_EditorCamera; + EntityWrapper m_EditorCamera = EntityWrapper::Invalid; + EntityWrapper m_ActualCamera = EntityWrapper::Invalid; + EditorCameraInputController* m_EditorCameraInputController; EditorGUI* m_EditorGUI; EditorStats* m_EditorStats; // State + double m_LastTime = 0.f; + bool m_Enabled = true; EditorGUI::WidgetMode m_WidgetMode = EditorGUI::WidgetMode::Translate; EntityWrapper m_Widget = EntityWrapper::Invalid; EntityWrapper m_CurrentSelection = EntityWrapper::Invalid; @@ -56,4 +63,8 @@ private: bool OnMousePress(const Events::MousePress& e); EventRelay m_EWidgetDelta; bool OnWidgetDelta(const Events::WidgetDelta& e); + EventRelay m_EInputCommand; + bool OnInputCommand(const Events::InputCommand& e); + EventRelay m_ESetCamera; + bool OnSetCamera(const Events::SetCamera& e); }; \ No newline at end of file diff --git a/include/Engine/Input/EInputCommand.h b/include/Engine/Input/EInputCommand.h index 9ec897d3..d4608b55 100644 --- a/include/Engine/Input/EInputCommand.h +++ b/include/Engine/Input/EInputCommand.h @@ -2,6 +2,7 @@ #define Events_InputCommand_h__ #include "Core/EventBroker.h" +#include "Core/EntityWrapper.h" namespace Events { @@ -9,7 +10,8 @@ namespace Events struct InputCommand : Event { /** Numerical ID of the player. */ - unsigned int PlayerID; + int PlayerID; + EntityWrapper Player; /** The command that was sent. */ std::string Command; /** The value of the command. */ diff --git a/include/Engine/Input/FirstPersonInputController.h b/include/Engine/Input/FirstPersonInputController.h index d8584494..426670c4 100644 --- a/include/Engine/Input/FirstPersonInputController.h +++ b/include/Engine/Input/FirstPersonInputController.h @@ -9,62 +9,119 @@ template class FirstPersonInputController : public InputController { public: - FirstPersonInputController(EventBroker* eventBroker, unsigned int playerID) - : InputController(eventBroker) - , m_PlayerID(playerID) - { - EVENT_SUBSCRIBE_MEMBER(m_ELockMouse, &FirstPersonInputController::OnLockMouse); - EVENT_SUBSCRIBE_MEMBER(m_EUnlockMouse, &FirstPersonInputController::OnUnlockMouse); - } + FirstPersonInputController(EventBroker* eventBroker, int playerID); - const glm::quat Orientation() const { return m_Orientation; } + virtual const glm::vec3 Movement() const { return m_Movement; } + virtual const glm::vec3 Rotation() const { return m_Rotation; } + virtual bool Jumping() const { return m_Jumping; } + virtual bool Crouching() const { return m_Crouching; } - void LockMouse() - { - Events::LockMouse e; - m_EventBroker->Publish(e); - m_MouseLocked = true; - } + void LockMouse(); + void UnlockMouse(); + virtual bool OnCommand(const Events::InputCommand& e) override; + virtual void Reset(); - void UnlockMouse() - { - Events::UnlockMouse e; - m_EventBroker->Publish(e); - m_MouseLocked = false; - } +protected: + const int m_PlayerID; + bool m_MouseLocked = false; + glm::vec3 m_Rotation; + glm::vec3 m_Movement; + bool m_Jumping = false; + bool m_Crouching = false; + + EventRelay m_ELockMouse; + bool OnLockMouse(const Events::LockMouse& e); + EventRelay m_EUnlockMouse; + bool OnUnlockMouse(const Events::UnlockMouse& e); +}; - virtual bool OnCommand(const Events::InputCommand& e) override - { - if (m_PlayerID != e.PlayerID) { - return false; - } +template +FirstPersonInputController::FirstPersonInputController(EventBroker* eventBroker, int playerID) + : InputController(eventBroker) + , m_PlayerID(playerID) +{ + EVENT_SUBSCRIBE_MEMBER(m_ELockMouse, &FirstPersonInputController::OnLockMouse); + EVENT_SUBSCRIBE_MEMBER(m_EUnlockMouse, &FirstPersonInputController::OnUnlockMouse); +} - if (m_MouseLocked) { - if (e.Command == "Pitch") { - float val = glm::radians(e.Value); - m_Orientation = m_Orientation * glm::angleAxis(-val, glm::vec3(1, 0, 0)); - return true; - } +template +void FirstPersonInputController::Reset() +{ + m_Rotation = glm::vec3(0.f, 0.f, 0.f); + m_Jumping = false; +} - if (e.Command == "Yaw") { - float val = glm::radians(e.Value); - m_Orientation = glm::angleAxis(-val, glm::vec3(0, 1, 0)) * m_Orientation; - return true; - } - } +template +void FirstPersonInputController::LockMouse() +{ + Events::LockMouse e; + m_EventBroker->Publish(e); + m_MouseLocked = true; +} +template +void FirstPersonInputController::UnlockMouse() +{ + Events::UnlockMouse e; + m_EventBroker->Publish(e); + m_MouseLocked = false; +} + +template +bool FirstPersonInputController::OnCommand(const Events::InputCommand& e) +{ + if (m_PlayerID != e.PlayerID) { return false; } -protected: - const unsigned int m_PlayerID; - glm::quat m_Orientation; - bool m_MouseLocked = false; - - EventRelay m_ELockMouse; - bool OnLockMouse(const Events::LockMouse& e) { m_MouseLocked = true; return true; } - EventRelay m_EUnlockMouse; - bool OnUnlockMouse(const Events::UnlockMouse& e) { m_MouseLocked = false; return true; } -}; + if (e.Command == "Pitch") { + float val = glm::radians(e.Value); + m_Rotation.x += -val; + //m_Rotation.x = glm::clamp(m_Rotation.x, -glm::half_pi(), glm::half_pi()); + } + + if (e.Command == "Yaw") { + float val = glm::radians(e.Value); + m_Rotation.y += -val; + } + + if (e.Command == "Forward" || e.Command == "Right") { + if (e.Command == "Forward") { + float val = glm::clamp(e.Value, -1.f, 1.f); + m_Movement.z = -val; + } + if (e.Command == "Right") { + float val = glm::clamp(e.Value, -1.f, 1.f); + m_Movement.x = val; + } + if (glm::length2(m_Movement) > 0) { + m_Movement = glm::normalize(m_Movement); + } + } + + if (e.Command == "Jump") { + m_Jumping = e.Value > 0; + } + + if (e.Command == "Crouch") { + m_Crouching = e.Value > 0; + } + + return true; +} + +template +bool FirstPersonInputController::OnUnlockMouse(const Events::UnlockMouse& e) +{ + m_MouseLocked = false; + return true; +} + +template +bool FirstPersonInputController::OnLockMouse(const Events::LockMouse& e) +{ + m_MouseLocked = true; + return true; +} #endif \ No newline at end of file diff --git a/include/Engine/Network/Client.h b/include/Engine/Network/Client.h index 96511baa..4f1baa67 100644 --- a/include/Engine/Network/Client.h +++ b/include/Engine/Network/Client.h @@ -20,6 +20,7 @@ #include "Input/EInputCommand.h" #include "Core/EPlayerDamage.h" #include "Network/EInterpolate.h" +#include "Core/EPlayerSpawned.h" class Client : public Network { @@ -39,16 +40,17 @@ private: char readBuf[INPUTSIZE] = { 0 }; // Packet loss logic - unsigned int m_PacketID = 0; - unsigned int m_PreviousPacketID = 0; - unsigned int m_SendPacketID = 0; + PacketID m_PacketID = 0; + PacketID m_PreviousPacketID = 0; + PacketID m_SendPacketID = 0; // Game logic World* m_World; std::string m_PlayerName; - int m_PlayerID = -1; + PlayerID m_PlayerID = -1; EntityID m_ServerEntityID = std::numeric_limits::max(); bool m_IsConnected = false; + EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; // Server Client Lookup map // Assumes that root node for client and server is EntityID 0. @@ -57,31 +59,36 @@ private: std::unordered_map m_ClientIDToServerID; // Network logic - PlayerDefinition m_PlayerDefinitions[MAXCONNECTIONS]; + PlayerDefinition m_PlayerDefinitions[8]; SnapshotDefinitions m_NextSnapshot; double m_DurationOfPingTime; std::clock_t m_StartPingTime; + std::clock_t m_TimeSinceSentInputs; + unsigned int m_SendInputIntervalMs; std::vector m_InputCommandBuffer; // Private member functions void readFromServer(); - int receive(char* data, size_t length); + int receive(char* data); void send(Packet& packet); void connect(); void disconnect(); - void ping(); void parseMessageType(Packet& packet); void updateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType); void parseConnect(Packet& packet); void parsePlayerConnected(Packet& packet); void parsePing(); - void parseServerPing(); + void parseKick(); + void parsePlayersSpawned(Packet& packet); + void parseEntityDeletion(Packet& packet); + void parseComponentDeletion(Packet& packet); void InterpolateFields(Packet & packet, const ComponentInfo & componentInfo, const EntityID & entityID, const std::string & componentType); void parseSnapshot(Packet& packet); void identifyPacketLoss(); bool hasServerTimedOut(); EntityID createPlayer(); void sendInputCommands(); + void sendLocalPlayerTransform(); void becomePlayer(); // Mapping Logic // Returns if local EntityID exist in map @@ -89,13 +96,16 @@ private: // Returns if server EntityID exist in map bool serverClientMapsHasEntity(EntityID serverEntityID); void insertIntoServerClientMaps(EntityID serverEntityID, EntityID clientEntityID); + void deleteFromServerClientMaps(EntityID serverEntityID, EntityID clientEntityID); // Events EventBroker* m_EventBroker; EventRelay m_EInputCommand; bool OnInputCommand(const Events::InputCommand& e); - EventRelay m_EPlayeDamage; + EventRelay m_EPlayerDamage; bool OnPlayerDamage(const Events::PlayerDamage& e); + EventRelay m_EPlayerSpawned; + bool OnPlayerSpawned(const Events::PlayerSpawned& e); }; #endif diff --git a/include/Engine/Network/EPlayerDisconnected.h b/include/Engine/Network/EPlayerDisconnected.h new file mode 100644 index 00000000..278a7b1f --- /dev/null +++ b/include/Engine/Network/EPlayerDisconnected.h @@ -0,0 +1,18 @@ +#ifndef Events_PlayerDisconnected +#define Events_PlayerDisconnected + +#include "Core/EventBroker.h" +#include "Core/Entity.h" + +namespace Events +{ + +struct PlayerDisconnected : public Event +{ + unsigned int PlayerID; + EntityID Entity; +}; + +} + +#endif diff --git a/include/Engine/Network/MessageType.h b/include/Engine/Network/MessageType.h index f0026190..85f22649 100644 --- a/include/Engine/Network/MessageType.h +++ b/include/Engine/Network/MessageType.h @@ -5,16 +5,20 @@ // Used to determine what type of message was sent. enum class MessageType { - Connect, - Disconnect, - ClientPing, - ServerPing, - Message, - Snapshot, + Connect, + Disconnect, + Ping, + Message, + Snapshot, OnInputCommand, OnPlayerDamage, PlayerConnected, - BecomePlayer + BecomePlayer, + Kick, + OnPlayerSpawned, + EntityDeleted, + ComponentDeleted, + PlayerTransform }; #endif diff --git a/include/Engine/Network/Network.h b/include/Engine/Network/Network.h index 1464a96f..e1e64fc1 100644 --- a/include/Engine/Network/Network.h +++ b/include/Engine/Network/Network.h @@ -1,13 +1,20 @@ #ifndef Network_h__ #define Network_h__ +#include + #include "Core/World.h" #include "Core/EventBroker.h" #include "Network/Packet.h" +#include "Network/NetworkData.h" +#include "Core/ResourceManager.h" +#include "Core/ConfigFile.h" +#include +#include -#define MAXCONNECTIONS 8 -#define INPUTSIZE 4097 -#define TIMEOUTMS 15000 +#define INPUTSIZE 32000 +typedef unsigned int PlayerID; +typedef unsigned int PacketID; class Network { @@ -15,6 +22,17 @@ public: virtual ~Network() { }; virtual void Start(World* m_world, EventBroker *eventBroker) = 0; virtual void Update() = 0; +protected: + // For Debug + bool isReadingData = false; + NetworkData m_NetworkData; + unsigned int m_SaveDataIntervalMs = 1000; + std::clock_t m_SaveDataTimer; + unsigned int m_MaxConnections; + unsigned int m_TimeoutMs; + void saveToFile(); + void updateNetworkData(); + void initialize(); }; #endif \ No newline at end of file diff --git a/include/Engine/Network/NetworkData.h b/include/Engine/Network/NetworkData.h new file mode 100644 index 00000000..87f7a215 --- /dev/null +++ b/include/Engine/Network/NetworkData.h @@ -0,0 +1,18 @@ +#ifndef NetworkData_h__ +#define NetworkData_h__ +#include + +struct NetworkData { + unsigned int TotalTime = 0; + unsigned int TotalDataReceived = 0; + unsigned int TotalDataSent = 0; + unsigned int AmountOfMessagesReceived = 0; + unsigned int AmountOfMessagesSent = 0; + // Interval based + unsigned int DataReceivedThisInterval = 0; + unsigned int DataSentThisInterval = 0; + // pair: first=reveived, second=send + std::vector> BandwidthBytes; +}; + +#endif diff --git a/include/Engine/Network/Packet.h b/include/Engine/Network/Packet.h index 2891bf87..d38ddf58 100644 --- a/include/Engine/Network/Packet.h +++ b/include/Engine/Network/Packet.h @@ -55,12 +55,14 @@ public: char* Data() { return m_Data; }; unsigned int DataReadSize() { return m_ReturnDataOffset; } unsigned int MaxSize() { return m_MaxPacketSize; } + unsigned int HeaderSize() { return m_HeaderSize; } private: char* m_Data; unsigned int m_ReturnDataOffset = 0; int m_Offset = 0; unsigned int m_MaxPacketSize = 512; + unsigned int m_HeaderSize = 0; void resizeData(); }; diff --git a/include/Engine/Network/PlayerDefinition.h b/include/Engine/Network/PlayerDefinition.h index 4b8b8e6e..863948b3 100644 --- a/include/Engine/Network/PlayerDefinition.h +++ b/include/Engine/Network/PlayerDefinition.h @@ -1,9 +1,10 @@ #ifndef PlayerDefinition_h__ #define PlayerDefinition_h__ #include +#include "../Core/Entity.h" struct PlayerDefinition { - int EntityID = -1; + ::EntityID EntityID = EntityID_Invalid; std::string Name = ""; boost::asio::ip::udp::endpoint Endpoint; unsigned int PacketID; diff --git a/include/Engine/Network/Server.h b/include/Engine/Network/Server.h index 8aabceba..11f983a9 100644 --- a/include/Engine/Network/Server.h +++ b/include/Engine/Network/Server.h @@ -14,6 +14,10 @@ #include "../Network/Network.h" #include "Input/EInputCommand.h" #include "Core/EPlayerDamage.h" +#include "Network/EPlayerDisconnected.h" +#include "Core/EPlayerSpawned.h" +#include "Core/EEntityDeleted.h" +#include "Core/EComponentDeleted.h" class Server : public Network { @@ -29,8 +33,8 @@ private: boost::asio::ip::udp::socket m_Socket; // Sending messages to client logic - PlayerDefinition m_PlayerDefinitions[MAXCONNECTIONS]; - std::vector m_ConnectedUsers; + std::map m_ConnectedPlayers; + // HACK: Fix INPUTSIZE char readBuffer[INPUTSIZE] = { 0 }; int bytesRead = 0; // time for previouse message @@ -38,9 +42,10 @@ private: std::clock_t previousSnapshotMessage = std::clock(); std::clock_t timOutTimer = std::clock(); // How often we send messages (milliseconds) - int intervalMs = 1000; - int snapshotInterval = 50; + int pingIntervalMs; + int snapshotInterval; int checkTimeOutInterval = 100; + int m_NextPlayerID = 0; //Timers std::clock_t m_StartPingTime; @@ -50,32 +55,40 @@ private: EventBroker* m_EventBroker; // Packet loss logic - unsigned int m_PacketID = 0; - unsigned int m_PreviousPacketID = 0; + PacketID m_PacketID = 0; + PacketID m_PreviousPacketID = 0; // Private member functions - int receive(char* data, size_t length); + int receive(char* data); void readFromClients(); - void send(Packet& packet, int playerID); + void send(PlayerID player, Packet& packet); void send(Packet& packet); void broadcast(Packet& packet); void sendSnapshot(); + void addChildrenToPacket(Packet& packet, EntityID entityID); void sendPing(); void checkForTimeOuts(); - void disconnect(int i); + void disconnect(PlayerID playerID); void parseMessageType(Packet& packet); void parseOnInputCommand(Packet& packet); void parseOnPlayerDamage(Packet& packet); void parseConnect(Packet& packet); void parseDisconnect(); void parseClientPing(); - void parseServerPing(); + void parsePing(); void identifyPacketLoss(); - void createPlayer(); - int GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint); + void kick(PlayerID player); + PlayerID GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint); // Debug event EventRelay m_EInputCommand; bool OnInputCommand(const Events::InputCommand& e); + EventRelay m_EPlayerSpawned; + bool OnPlayerSpawned(const Events::PlayerSpawned& e); + EventRelay m_EEntityDeleted; + bool OnEntityDeleted(const Events::EntityDeleted& e); + EventRelay m_EComponentDeleted; + bool OnComponentDeleted(const Events::ComponentDeleted& e); + void parsePlayerTransform(Packet& packet); }; #endif diff --git a/include/Engine/Rendering/AnimationSystem.h b/include/Engine/Rendering/AnimationSystem.h new file mode 100644 index 00000000..fcdcbc92 --- /dev/null +++ b/include/Engine/Rendering/AnimationSystem.h @@ -0,0 +1,29 @@ +#ifndef AnimationSystem_h__ +#define AnimationSystem_h__ + +#include "GLM.h" + +#include "Common.h" +#include "Core/System.h" +#include "Core/ResourceManager.h" +#include "Rendering/Model.h" +#include "Rendering/EAnimationComplete.h" +#include "Rendering/Skeleton.h" + +class AnimationSystem : public PureSystem +{ +public: + AnimationSystem(World* world, EventBroker* eventBroker) + : System(world, eventBroker) + , PureSystem("Animation") + { + + } + ~AnimationSystem() { } + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& animationComponent, double dt) override; +private: + + +}; + +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/DebugCameraInputController.h b/include/Engine/Rendering/DebugCameraInputController.h deleted file mode 100644 index 5ce85b3f..00000000 --- a/include/Engine/Rendering/DebugCameraInputController.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef DebugCameraInputController_h__ -#define DebugCameraInputController_h__ - -#include -#include "../Input/FirstPersonInputController.h" -#include "../Core/EMousePress.h" -#include "../Core/EMouseRelease.h" - -template -class DebugCameraInputController : public FirstPersonInputController -{ -public: - DebugCameraInputController(EventBroker* eventBroker, unsigned int playerID) - : FirstPersonInputController(eventBroker, playerID) - { - EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &DebugCameraInputController::OnMousePress); - EVENT_SUBSCRIBE_MEMBER(m_EMouseRelease, &DebugCameraInputController::OnMouseRelease); - } - - void SetPosition(const glm::vec3 position) { m_Position = position; } - void SetOrientation(const glm::quat orientation) { m_Orientation = orientation; } - - const glm::vec3 Position() const { return m_Position; } - void SetBaseSpeed(float speed) { m_BaseSpeed = speed; } - - virtual bool OnCommand(const Events::InputCommand& e) override - { - ImGuiIO& io = ImGui::GetIO(); - - if (!io.WantCaptureKeyboard) { - if (e.Command == "Right") { - float value = std::max(-1.f, std::min(e.Value, 1.f)); - m_Velocity.x = value; - } - if (e.Command == "Forward") { - float value = std::max(-1.f, std::min(e.Value, 1.f)); - m_Velocity.z = -value; - } - if (e.Command == "Sprint") { - if (e.Value > 0.f) { - m_Speed = m_BaseSpeed * 2.f * (e.Value); - } else { - m_Speed = m_BaseSpeed; - } - } - } - - return FirstPersonInputController::OnCommand(e); - } - - void Update(double dt) - { - if (glm::length2(m_Velocity) > 0) { - m_Position += m_Orientation * (glm::normalize(m_Velocity) * m_Speed * (float)dt); - } - } - -protected: - glm::vec3 m_Position = glm::vec3(0, 0, 0); - glm::vec3 m_Velocity = glm::vec3(0, 0, 0); - float m_BaseSpeed = 2.0f; - float m_Speed = m_BaseSpeed; - EventRelay m_EMousePress; - bool OnMousePress(const Events::MousePress& e) - { - if (e.Button == GLFW_MOUSE_BUTTON_2) { - ImGuiIO& io = ImGui::GetIO(); - if (!io.WantCaptureMouse) { - LockMouse(); - } - } - return true; - } - EventRelay m_EMouseRelease; - bool OnMouseRelease(const Events::MouseRelease& e) - { - if (e.Button == GLFW_MOUSE_BUTTON_2) { - UnlockMouse(); - } - return true; - } -}; - -#endif \ No newline at end of file diff --git a/include/Engine/Rendering/EAnimationComplete.h b/include/Engine/Rendering/EAnimationComplete.h new file mode 100644 index 00000000..00519a38 --- /dev/null +++ b/include/Engine/Rendering/EAnimationComplete.h @@ -0,0 +1,18 @@ +#ifndef Events_AnimationComplete_h__ +#define Events_AnimationComplete_h__ + +#include "../Core/EventBroker.h" +#include "../Core/EntityWrapper.h" + +namespace Events +{ + +struct AnimationComplete : Event +{ + EntityWrapper Entity; + std::string Name; +}; + +} + +#endif diff --git a/include/Engine/Rendering/Model.h b/include/Engine/Rendering/Model.h index 280fe5af..f751a8cc 100644 --- a/include/Engine/Rendering/Model.h +++ b/include/Engine/Rendering/Model.h @@ -1,7 +1,8 @@ #ifndef Model_h__ #define Model_h__ -#include "RawModel.h" +#include "Rendering/RawModelCustom.h" +//#include "Rendering/RawModelAssimp.h" #include "../OpenGL.h" class Model : public ThreadUnsafeResource @@ -19,12 +20,11 @@ public: GLuint VAO; GLuint ElementBuffer; + RawModel* m_RawModel; private: - RawModel* m_RawModel; + GLuint VertexBuffer; - GLuint DiffuseVertexColorBuffer; - GLuint SpecularVertexColorBuffer; GLuint NormalBuffer; GLuint TangentNormalsBuffer; GLuint BiTangentNormalsBuffer; diff --git a/include/Engine/Rendering/ModelJob.h b/include/Engine/Rendering/ModelJob.h index a88530e7..df7f1aec 100644 --- a/include/Engine/Rendering/ModelJob.h +++ b/include/Engine/Rendering/ModelJob.h @@ -13,10 +13,11 @@ #include "Camera.h" #include "../Core/World.h" #include "../Core/Transform.h" +#include "Skeleton.h" struct ModelJob : RenderJob { - ModelJob(Model* model, Camera* camera, glm::mat4 matrix, ::RawModel::MaterialGroup matGroup, ComponentWrapper modelComponent, World* world) + ModelJob(Model* model, Camera* camera, glm::mat4 matrix, ::RawModel::MaterialGroup matGroup, ComponentWrapper modelComponent, World* world, glm::vec4 fillColor, float fillPercentage) : RenderJob() { Model = model; @@ -24,6 +25,10 @@ struct ModelJob : RenderJob DiffuseTexture = matGroup.Texture.get(); NormalTexture = matGroup.NormalMap.get(); SpecularTexture = matGroup.SpecularMap.get(); + IncandescenceTexture = matGroup.IncandescenceMap.get(); + DiffuseColor = matGroup.DiffuseColor; + SpecularColor = matGroup.SpecularColor; + IncandescenceColor = matGroup.IncandescenceColor; StartIndex = matGroup.StartIndex; EndIndex = matGroup.EndIndex; Matrix = matrix; @@ -33,6 +38,17 @@ struct ModelJob : RenderJob glm::vec3 worldpos = glm::vec3(camera->ViewMatrix() * glm::vec4(abspos, 1)); Depth = worldpos.z; World = world; + + + FillColor = fillColor; + FillPercentage = fillPercentage; + Skeleton = Model->m_RawModel->m_Skeleton; + + if (world->HasComponent(Entity, "Animation") && Skeleton != nullptr) { + auto animationComponent = world->GetComponent(Entity, "Animation"); + Animation = model->m_RawModel->m_Skeleton->GetAnimation(animationComponent["Name"]); + AnimationTime = (double)animationComponent["Time"]; + } }; unsigned int TextureID; @@ -43,13 +59,25 @@ struct ModelJob : RenderJob const Texture* DiffuseTexture; const Texture* NormalTexture; const Texture* SpecularTexture; + const Texture* IncandescenceTexture; float Shininess = 0.f; glm::vec4 Color; const ::Model* Model = nullptr; + ::Skeleton* Skeleton = nullptr; + const ::Skeleton::Animation* Animation = nullptr; + + float AnimationTime = 0.f; + + glm::vec4 DiffuseColor; + glm::vec4 SpecularColor; + glm::vec4 IncandescenceColor; unsigned int StartIndex = 0; unsigned int EndIndex = 0; World* World; + glm::vec4 FillColor = glm::vec4(0); + float FillPercentage = 0.0; + void CalculateHash() override { Hash = TextureID; diff --git a/include/Engine/Rendering/PickingPass.h b/include/Engine/Rendering/PickingPass.h index 5fc88982..da3ed31b 100644 --- a/include/Engine/Rendering/PickingPass.h +++ b/include/Engine/Rendering/PickingPass.h @@ -1,8 +1,6 @@ #ifndef PickingPass_h__ #define PickingPass_h__ - - #include "IRenderer.h" #include "PickingPassState.h" #include "FrameBuffer.h" @@ -10,8 +8,7 @@ #include "Util/UnorderedMapiVec2.h" #include "../Core/EventBroker.h" #include "../Core/World.h" - - +#include "Rendering/Skeleton.h" class PickingPass { diff --git a/include/Engine/Rendering/RawModel.h b/include/Engine/Rendering/RawModelAssimp.h similarity index 78% rename from include/Engine/Rendering/RawModel.h rename to include/Engine/Rendering/RawModelAssimp.h index 0477edb1..08df818d 100644 --- a/include/Engine/Rendering/RawModel.h +++ b/include/Engine/Rendering/RawModelAssimp.h @@ -1,5 +1,7 @@ -#ifndef RawModel_h__ -#define RawModel_h__ +#ifndef RawModelAssimp_h__ +#define RawModelAssimp_h__ + +#ifdef USING_ASSIMP_AS_IMPORTER #include #include @@ -18,29 +20,27 @@ #include "Texture.h" #include "Skeleton.h" -class RawModel : public Resource +#define RawModel RawModelAssimp + +class RawModelAssimp : public Resource { friend class ResourceManager; protected: - RawModel(std::string fileName); + RawModelAssimp(std::string fileName); public: - ~RawModel(); + ~RawModelAssimp(); struct Vertex { glm::vec3 Position; glm::vec3 Normal; glm::vec3 Tangent; - glm::vec3 BiTangent; + glm::vec3 BiNormal; glm::vec2 TextureCoords; - glm::vec4 DiffuseVertexColor; - glm::vec4 SpecularVertexColor; - glm::vec4 BoneIndices1; - glm::vec4 BoneIndices2; - glm::vec4 BoneWeights1; - glm::vec4 BoneWeights2; + glm::vec4 BoneIndices; + glm::vec4 BoneWeights; }; struct MaterialGroup @@ -68,8 +68,6 @@ private: std::vector BoneIndices; std::vector BoneWeights; std::vector Normals; - std::vector DiffuseVertexColor; - std::vector SpecularVertexColor; std::vector TangentNormals; std::vector BiTangentNormals; std::vector TextureCoords; @@ -78,3 +76,4 @@ private: }; #endif +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/RawModelCustom.h b/include/Engine/Rendering/RawModelCustom.h new file mode 100644 index 00000000..f1bc0e24 --- /dev/null +++ b/include/Engine/Rendering/RawModelCustom.h @@ -0,0 +1,102 @@ +#ifndef RawModelCustom_h__ +#define RawModelCustom_h__ + +#ifndef USING_ASSIMP_AS_IMPORTER + +#define RawModel RawModelCustom + +#include +#include +#include +#include +#include + +#include + +#include "../Common.h" +#include "../GLM.h" +#include "../Core/ResourceManager.h" +#include "Texture.h" +#include "Skeleton.h" + +#include "boost\endian\buffers.hpp" + + + +class RawModelCustom : public Resource +{ + friend class ResourceManager; + +protected: + RawModelCustom(std::string fileName); + +public: + ~RawModelCustom(); + + struct Vertex + { + glm::vec3 Position; + glm::vec3 Normal; + glm::vec3 Tangent; + glm::vec3 BiNormal; + glm::vec2 TextureCoords; + glm::vec4 BoneIndices; + glm::vec4 BoneWeights; + }; + + struct MaterialGroup + { + float SpecularExponent; + float ReflectionFactor; + glm::vec4 DiffuseColor{ 1.0f, 1.0f, 1.0f, 1.0f }; + glm::vec4 SpecularColor{ 1.0f, 1.0f, 1.0f, 1.0f }; + glm::vec4 IncandescenceColor{ 1.0f, 1.0f, 1.0f, 1.0f }; + unsigned int StartIndex; + unsigned int EndIndex; + //float Transparency; + std::string TexturePath; + std::shared_ptr<::Texture> Texture; + std::string NormalMapPath; + std::shared_ptr<::Texture> NormalMap; + std::string SpecularMapPath; + std::shared_ptr<::Texture> SpecularMap; + std::string IncandescenceMapPath; + std::shared_ptr<::Texture> IncandescenceMap; + }; + + std::vector MaterialGroups; + + std::vector m_Vertices; + std::vector m_Indices; + Skeleton* m_Skeleton = nullptr; + glm::mat4 m_Matrix; + +private: + + + void ReadMeshFile(std::string filePath); + void ReadMeshFileHeader(unsigned int& offset, char* fileData, unsigned int& fileByteSize); + void ReadMesh(unsigned int& offset, char* fileData, unsigned int& fileByteSize); + void ReadVertices(unsigned int& offset, char* fileData, unsigned int& fileByteSize); + void ReadIndices(unsigned int& offset, char* fileData, unsigned int& fileByteSize); + + void ReadMaterialFile(std::string filePath); + void ReadMaterials(unsigned int &offset, char* fileData, unsigned int& fileByteSize); + void ReadMaterialSingle(unsigned int &offset, char* fileData, unsigned int& fileByteSize); + + void ReadAnimationFile(std::string filePath); + void ReadAnimationBindPoses(unsigned int &offset, char* fileData, unsigned int& fileByteSize); + void ReadAnimationJoint(unsigned int &offset, char* fileData, unsigned int& fileByteSize); + void ReadAnimationClips(unsigned int &offset, char* fileData, unsigned int& fileByteSize, unsigned int numberOfClips); + void ReadAnimationClipSingle(unsigned int &offset, char* fileData, unsigned int& fileByteSize, unsigned int clipIndex); + void ReadAnimationKeyFrame(unsigned int &offset, char* fileData, unsigned int& fileByteSize, unsigned int numberOfJoints, Skeleton::Animation& animation); + + //void CreateSkeleton(std::vector> &boneInfo, std::map &boneNameMapping, aiNode* node, int parentID); +}; + +#else + +#include "RawModelAssimp.h" + +#endif +#endif \ No newline at end of file diff --git a/include/Engine/Rendering/RenderSystem.h b/include/Engine/Rendering/RenderSystem.h index 9b8e5a9f..37f99341 100644 --- a/include/Engine/Rendering/RenderSystem.h +++ b/include/Engine/Rendering/RenderSystem.h @@ -15,7 +15,7 @@ #include "Renderer.h" #include "PointLightJob.h" #include "../Core/Transform.h" -#include "DebugCameraInputController.h" +#include "../Core/EPlayerSpawned.h" class RenderSystem : public ImpureSystem { @@ -29,7 +29,9 @@ private: const IRenderer* m_Renderer; RenderFrame* m_RenderFrame; Camera* m_Camera; + World* m_World; EntityWrapper m_CurrentCamera = EntityWrapper::Invalid; + EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; EventRelay m_ESetCamera; bool OnSetCamera(Events::SetCamera &event); @@ -41,6 +43,9 @@ private: void fillModels(std::list>& jobs); void fillLight(std::list>& jobs); + + EventRelay m_EPlayerSpawned; + bool OnPlayerSpawned(Events::PlayerSpawned& e); }; #endif \ No newline at end of file diff --git a/include/Engine/Rendering/Skeleton.h b/include/Engine/Rendering/Skeleton.h index 73d407dd..3b89b89b 100644 --- a/include/Engine/Rendering/Skeleton.h +++ b/include/Engine/Rendering/Skeleton.h @@ -4,6 +4,7 @@ #include #include "Common.h" #include "../GLM.h" +#include //struct Bone //{ @@ -38,9 +39,9 @@ public: , OffsetMatrix(offsetMatrix) { } - int ID; std::string Name; glm::mat4 OffsetMatrix; + int ID; Bone* Parent; std::vector Children; diff --git a/include/Game/Systems/HealthSystem.h b/include/Game/Systems/HealthSystem.h index f9843f11..3b069349 100644 --- a/include/Game/Systems/HealthSystem.h +++ b/include/Game/Systems/HealthSystem.h @@ -24,7 +24,7 @@ public: private: //methods which will take care of specific events EventRelay m_EPlayerDamage; - bool HealthSystem::OnPlayerDamaged(const Events::PlayerDamage& e); + bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e); EventRelay m_EPlayerHealthPickup; bool HealthSystem::OnPlayerHealthPickup(const Events::PlayerHealthPickup& e); diff --git a/include/Game/Systems/InterpolationSystem.h b/include/Game/Systems/InterpolationSystem.h index 4b05571b..1e345c91 100644 --- a/include/Game/Systems/InterpolationSystem.h +++ b/include/Game/Systems/InterpolationSystem.h @@ -10,11 +10,12 @@ #include "Common.h" #include "Core/System.h" #include "Core/EventBroker.h" +#include "Core/ResourceManager.h" +#include "Core/ConfigFile.h" +#include "Core/EPlayerSpawned.h" #include "Network/EInterpolate.h" -#define SNAPSHOTINTERVAL 0.05f - class InterpolationSystem : public PureSystem { struct Transform @@ -25,29 +26,28 @@ class InterpolationSystem : public PureSystem double interpolationTime; }; public: - InterpolationSystem(World* world, EventBroker* eventBroker) - : System(world, eventBroker) - , PureSystem("Transform") - { - EVENT_SUBSCRIBE_MEMBER(m_EInterpolate, &InterpolationSystem::OnInterpolate); - } + InterpolationSystem(World* world, EventBroker* eventBroker); ~InterpolationSystem() { } virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& transform, double dt) override; private: std::unordered_map m_NextTransform; std::unordered_map m_LastReceivedTransform; + EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; //glm::vec3 vectorInterpolation(glm::vec3 prev, glm::vec3 next, double currentTime); template T vectorInterpolation(T prev, T next, double currentTime) { T difference = next - prev; - T vector = (difference / SNAPSHOTINTERVAL) * static_cast(currentTime); + T vector = (difference / m_SnapshotInterval) * static_cast(currentTime); return vector; } + float m_SnapshotInterval; EventRelay m_EInterpolate; bool InterpolationSystem::OnInterpolate(const Events::Interpolate& e); + EventRelay m_EPlayerSpawned; + bool OnPlayerSpawned(Events::PlayerSpawned& e); }; #endif diff --git a/include/Game/Systems/LifetimeSystem.h b/include/Game/Systems/LifetimeSystem.h new file mode 100644 index 00000000..da88cfa2 --- /dev/null +++ b/include/Game/Systems/LifetimeSystem.h @@ -0,0 +1,21 @@ +#ifndef LifetimeSystem_h__ +#define LifetimeSystem_h__ + +#include "Core/System.h" + +class LifetimeSystem : public ImpureSystem, PureSystem +{ +public: + LifetimeSystem(World* world, EventBroker* eventBroker) + : System(world, eventBroker) + , PureSystem("Lifetime") + { } + + virtual void Update(double dt) override; + virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& cLifetime, double dt) override; + +private: + std::vector m_Deletions; +}; + +#endif \ No newline at end of file diff --git a/include/Game/Systems/PlayerHUD.h b/include/Game/Systems/PlayerHUD.h new file mode 100644 index 00000000..180f5a2a --- /dev/null +++ b/include/Game/Systems/PlayerHUD.h @@ -0,0 +1,23 @@ +#ifndef PlayerHUD_h__ +#define PlayerHUD_h__ + +#include "../../Engine/Core/System.h" +#include "../../Engine/GLM.h" +#include "../../Engine/Rendering/ESetCamera.h" +#include + +class PlayerHUD : public ImpureSystem +{ +public: + PlayerHUD(World* world, EventBroker* eventBrokerer); + ~PlayerHUD(); + + virtual void Update(double dt) override; + +private: + World* m_World; + EventBroker* m_EventBroker; + +}; + +#endif \ No newline at end of file diff --git a/include/Game/Systems/PlayerMovementSystem.h b/include/Game/Systems/PlayerMovementSystem.h index 1be61bdd..f39740ec 100644 --- a/include/Game/Systems/PlayerMovementSystem.h +++ b/include/Game/Systems/PlayerMovementSystem.h @@ -1,14 +1,23 @@ #include "Common.h" #include "GLM.h" #include "Core/System.h" +#include "Core/EPlayerSpawned.h" +#include "Input/FirstPersonInputController.h" +#include -class PlayerMovementSystem : public PureSystem +class PlayerMovementSystem : public ImpureSystem, PureSystem { public: - PlayerMovementSystem(World* world, EventBroker* eventBroker) - : System(world, eventBroker) - , PureSystem("Player") - { } + PlayerMovementSystem(World* world, EventBroker* eventBroker); + ~PlayerMovementSystem(); + virtual void Update(double dt) override; virtual void UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt); + +private: + // State + std::unordered_map*> m_PlayerInputControllers; + + EventRelay m_EPlayerSpawned; + bool OnPlayerSpawned(Events::PlayerSpawned& e); }; \ No newline at end of file diff --git a/include/Game/Systems/PlayerSpawnSystem.h b/include/Game/Systems/PlayerSpawnSystem.h index 8ade03a6..b0ff1d79 100644 --- a/include/Game/Systems/PlayerSpawnSystem.h +++ b/include/Game/Systems/PlayerSpawnSystem.h @@ -2,6 +2,9 @@ #include "Input/EInputCommand.h" #include "Systems/SpawnerSystem.h" #include "Events/ESpawnerSpawn.h" +#include "Core/EPlayerSpawned.h" +#include "Rendering/ESetCamera.h" +#include "Core/ConfigFile.h" class PlayerSpawnSystem : public ImpureSystem { @@ -11,8 +14,18 @@ public: virtual void Update(double dt) override; private: + struct SpawnRequest + { + int PlayerID; + ComponentInfo::EnumType Team; + }; + + bool m_NetworkEnabled = false; + std::vector m_SpawnRequests; + std::map m_PlayerEntities; + EventRelay m_OnInputCommand; bool OnInputCommand(const Events::InputCommand& e); - - std::vector m_SpawnRequests; + EventRelay m_OnPlayerSpawnerd; + bool OnPlayerSpawned(Events::PlayerSpawned& e); }; \ No newline at end of file diff --git a/include/Game/Systems/WeaponSystem.h b/include/Game/Systems/WeaponSystem.h index 5acf72b3..0dd5cc54 100644 --- a/include/Game/Systems/WeaponSystem.h +++ b/include/Game/Systems/WeaponSystem.h @@ -9,7 +9,10 @@ #include "Core/System.h" #include "Core/EPlayerDamage.h" #include "Core/EShoot.h" +#include "Core/EPlayerSpawned.h" #include "Input/EInputCommand.h" +#include "Core/EntityFile.h" +#include "Core/EntityFileParser.h" #include #include @@ -23,16 +26,18 @@ public: virtual void Update(double dt) override; private: - //methods which will take care of specific events - EventRelay m_EShoot; - bool WeaponSystem::OnShoot(const Events::Shoot& e); - - EventRelay m_EInputCommand; - bool WeaponSystem::OnInputCommand(const Events::InputCommand& e); - IRenderer* m_Renderer; - std::vector> m_EShootVector; + // State + EntityWrapper m_LocalPlayer = EntityWrapper::Invalid; + + // Events + EventRelay m_EPlayerSpawned; + bool WeaponSystem::OnPlayerSpawned(const Events::PlayerSpawned& e); + EventRelay m_EShoot; + bool WeaponSystem::OnShoot(Events::Shoot& e); + EventRelay m_EInputCommand; + bool WeaponSystem::OnInputCommand(const Events::InputCommand& e); }; #endif \ No newline at end of file diff --git a/resources/DefaultConfig.ini b/resources/DefaultConfig.ini index fb490ec8..86dc735c 100644 --- a/resources/DefaultConfig.ini +++ b/resources/DefaultConfig.ini @@ -1,11 +1,13 @@ [Debug] LogLevel=1 LoadMap= -EditorEnabled=false ; if true -> Pool allocation is not used when calling Allocate/Free, just use regular dynamic allocation. ; if false -> Use pool allocation. DisableMemoryPool=false +[Editor] +CameraSpeed=3 + [Video] Fullscreen=false VSYNC=false @@ -19,6 +21,11 @@ IsServer=false Name=Bob Address=127.0.0.1 Port=13 +MaxConnections=8 +SnapshotInterval=0.05 +SendInputIntervalMs=33 +PingIntervalMs= 1000 +TimeoutMs=15000 [Multithreading] ResourceLoading=true diff --git a/resources/DefaultInput.ini b/resources/DefaultInput.ini index 5ed46241..d07ed6a3 100644 --- a/resources/DefaultInput.ini +++ b/resources/DefaultInput.ini @@ -21,4 +21,5 @@ F1=ToggleEditor X=EditorToggleTransformSpace C=ConnectToServer N=SwitchToServer -M=SwitchToClient \ No newline at end of file +M=SwitchToClient +P=SwitchToPlayer \ No newline at end of file diff --git a/resources/Schema/Components.xsd b/resources/Schema/Components.xsd index 12d94611..5ce7fa5f 100644 --- a/resources/Schema/Components.xsd +++ b/resources/Schema/Components.xsd @@ -23,4 +23,8 @@ + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Animation.xml b/resources/Schema/Components/Animation.xml new file mode 100644 index 00000000..01af2747 --- /dev/null +++ b/resources/Schema/Components/Animation.xml @@ -0,0 +1,6 @@ + + + + 0 + true + \ No newline at end of file diff --git a/resources/Schema/Components/Animation.xsd b/resources/Schema/Components/Animation.xsd new file mode 100644 index 00000000..0dd21f29 --- /dev/null +++ b/resources/Schema/Components/Animation.xsd @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Fill.xml b/resources/Schema/Components/Fill.xml new file mode 100644 index 00000000..397f8e49 --- /dev/null +++ b/resources/Schema/Components/Fill.xml @@ -0,0 +1,5 @@ + + + 0.0 + + \ No newline at end of file diff --git a/resources/Schema/Components/Fill.xsd b/resources/Schema/Components/Fill.xsd new file mode 100644 index 00000000..2bf6548f --- /dev/null +++ b/resources/Schema/Components/Fill.xsd @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/HealthHUD.xml b/resources/Schema/Components/HealthHUD.xml new file mode 100644 index 00000000..d7e20f92 --- /dev/null +++ b/resources/Schema/Components/HealthHUD.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/resources/Schema/Components/HealthHUD.xsd b/resources/Schema/Components/HealthHUD.xsd new file mode 100644 index 00000000..821a65a2 --- /dev/null +++ b/resources/Schema/Components/HealthHUD.xsd @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Lifetime.xml b/resources/Schema/Components/Lifetime.xml new file mode 100644 index 00000000..d7e230d7 --- /dev/null +++ b/resources/Schema/Components/Lifetime.xml @@ -0,0 +1,4 @@ + + + 0 + \ No newline at end of file diff --git a/resources/Schema/Components/Lifetime.xsd b/resources/Schema/Components/Lifetime.xsd new file mode 100644 index 00000000..137c1d4a --- /dev/null +++ b/resources/Schema/Components/Lifetime.xsd @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/resources/Schema/Components/Player.xml b/resources/Schema/Components/Player.xml index caefd6e6..b51326aa 100644 --- a/resources/Schema/Components/Player.xml +++ b/resources/Schema/Components/Player.xml @@ -1,8 +1,5 @@ - - false - false - false - false + 3 + 1.5 \ No newline at end of file diff --git a/resources/Schema/Components/Player.xsd b/resources/Schema/Components/Player.xsd index 1a315a35..13948dc2 100644 --- a/resources/Schema/Components/Player.xsd +++ b/resources/Schema/Components/Player.xsd @@ -9,11 +9,8 @@ - - - - - + + diff --git a/resources/Schema/Entities/AnimatedArmy.xml b/resources/Schema/Entities/AnimatedArmy.xml new file mode 100644 index 00000000..b711a0fe --- /dev/null +++ b/resources/Schema/Entities/AnimatedArmy.xml @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + models/dummyscene.mesh + + + + + + + + + + + models/animtest. + + + + + + + + + + + models/animTest.mesh + + + + + + + + + + + models/animTest.mesh + + + + + + + + + + + models/animTest.mesh + + + + + + + + + + + 0.13000047206878662 + + + + + + + + + + + + 0.30000001192092896 + + + + + + + + + + + + 5 + + + + + + + + + + + + 5 + 0.80000001192092896 + + + + + + + + + + + + 5 + + + + + + + + + + + + 5 + + + + + + + + + + + + + 0.50999981164932251 + + + + + + + + + + + + + 5 + 5 + + + + + + + + + + + + 5 + + + + + + + + + + + + 5 + + + + + + + + + + + + 5 + + + + + + + + + + + + 5 + + + + + + + + + + + + 5 + + + + + + + + + + + + 5 + + + + + + + + + + + 5 + + + + + + + + + + + + 5 + + + + + + + + + + + + diff --git a/resources/Schema/Entities/CaptureTest.xml b/resources/Schema/Entities/CaptureTest.xml index 70a8ae14..5f657e51 100644 --- a/resources/Schema/Entities/CaptureTest.xml +++ b/resources/Schema/Entities/CaptureTest.xml @@ -9,7 +9,7 @@ - ../assets/Models/DummyScene.obj + ../assets/Models/DummyScene.mesh @@ -41,7 +41,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -61,7 +61,7 @@ 1 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -81,7 +81,7 @@ 2 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -102,7 +102,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -120,7 +120,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -138,7 +138,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/CaptureTestState1.xml b/resources/Schema/Entities/CaptureTestState1.xml index 75b14858..ffabd5c2 100644 --- a/resources/Schema/Entities/CaptureTestState1.xml +++ b/resources/Schema/Entities/CaptureTestState1.xml @@ -9,7 +9,7 @@ - ../assets/Models/DummyScene.obj + ../assets/Models/DummyScene.mesh @@ -42,7 +42,7 @@ 6.9158446328696002 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -63,7 +63,7 @@ 1 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -83,7 +83,7 @@ 2 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -104,7 +104,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -122,7 +122,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -140,7 +140,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/CaptureTestState2.xml b/resources/Schema/Entities/CaptureTestState2.xml index 77673324..29abdfbd 100644 --- a/resources/Schema/Entities/CaptureTestState2.xml +++ b/resources/Schema/Entities/CaptureTestState2.xml @@ -9,7 +9,7 @@ - ../assets/Models/DummyScene.obj + ../assets/Models/DummyScene.mesh @@ -41,7 +41,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -61,7 +61,7 @@ 1 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -79,7 +79,7 @@ 2 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -98,7 +98,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -116,7 +116,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -134,7 +134,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/CaptureTestState3.xml b/resources/Schema/Entities/CaptureTestState3.xml index 99da90a6..ff875dc4 100644 --- a/resources/Schema/Entities/CaptureTestState3.xml +++ b/resources/Schema/Entities/CaptureTestState3.xml @@ -9,7 +9,7 @@ - ../assets/Models/DummyScene.obj + ../assets/Models/DummyScene.mesh @@ -41,7 +41,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -61,7 +61,7 @@ 1 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -81,7 +81,7 @@ 2 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -101,7 +101,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -122,7 +122,7 @@ 4 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -140,7 +140,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -158,7 +158,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -176,7 +176,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -194,7 +194,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/CaptureTestState4.xml b/resources/Schema/Entities/CaptureTestState4.xml index 695df52e..740d7e7b 100644 --- a/resources/Schema/Entities/CaptureTestState4.xml +++ b/resources/Schema/Entities/CaptureTestState4.xml @@ -9,7 +9,7 @@ - ../assets/Models/DummyScene.obj + ../assets/Models/DummyScene.mesh @@ -41,7 +41,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -61,7 +61,7 @@ 1 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -79,7 +79,7 @@ 2 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -96,7 +96,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -115,7 +115,7 @@ 4 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -133,7 +133,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -151,7 +151,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -169,7 +169,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -187,7 +187,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/CollidableCube.xml b/resources/Schema/Entities/CollidableCube.xml new file mode 100644 index 00000000..fd800a8b --- /dev/null +++ b/resources/Schema/Entities/CollidableCube.xml @@ -0,0 +1,15 @@ + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + diff --git a/resources/Schema/Entities/CollisionTest1.xml b/resources/Schema/Entities/CollisionTest1.xml index f0b310e1..e731a17a 100644 --- a/resources/Schema/Entities/CollisionTest1.xml +++ b/resources/Schema/Entities/CollisionTest1.xml @@ -24,7 +24,7 @@ false - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/CollisionTestLevel.xml b/resources/Schema/Entities/CollisionTestLevel.xml index 5c029cf3..66b1109f 100644 --- a/resources/Schema/Entities/CollisionTestLevel.xml +++ b/resources/Schema/Entities/CollisionTestLevel.xml @@ -11,7 +11,7 @@ - ../assets\Models\Test/ObstacleCourse.obj + ../assets/Models/Test/ObstacleCourse.mesh @@ -35,7 +35,7 @@ - ../assets\Models\Core\UnitCube.obj + ../assets/Models/Core/UnitCube.mesh false @@ -53,7 +53,7 @@ - ../assets\Models\Core\colorbox.obj + ../assets/Models/Core/colorbox.mesh diff --git a/resources/Schema/Entities/EditorTestWorld.xml b/resources/Schema/Entities/EditorTestWorld.xml index 2e95d626..5649a630 100755 --- a/resources/Schema/Entities/EditorTestWorld.xml +++ b/resources/Schema/Entities/EditorTestWorld.xml @@ -9,7 +9,7 @@ - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh @@ -58,7 +58,7 @@ - Models/DirectionalLightWidget.obj + Models/DirectionalLightWidget.mesh @@ -70,7 +70,7 @@ - Models/Assault.obj + Models/Assault.mesh @@ -94,7 +94,7 @@ - Models/Core/UnitSphere.obj + Models/Core/UnitSphere.mesh diff --git a/resources/Schema/Entities/EditorWidgetRotate.xml b/resources/Schema/Entities/EditorWidgetRotate.xml index b918794e..88452aac 100644 --- a/resources/Schema/Entities/EditorWidgetRotate.xml +++ b/resources/Schema/Entities/EditorWidgetRotate.xml @@ -18,7 +18,7 @@ - Models/RotationWidgetX.obj + Models/RotationWidgetX.mesh @@ -33,7 +33,7 @@ - Models/RotationWidgetY.obj + Models/RotationWidgetY.mesh @@ -48,7 +48,7 @@ - Models/RotationWidgetZ.obj + Models/RotationWidgetZ.mesh diff --git a/resources/Schema/Entities/EditorWidgetScale.xml b/resources/Schema/Entities/EditorWidgetScale.xml index 15bcb38b..786b079e 100644 --- a/resources/Schema/Entities/EditorWidgetScale.xml +++ b/resources/Schema/Entities/EditorWidgetScale.xml @@ -3,7 +3,7 @@ - Models/ScaleWidgetOrigin.obj + Models/ScaleWidgetOrigin.mesh @@ -20,7 +20,7 @@ - Models/ScaleWidgetX.obj + Models/ScaleWidgetX.mesh @@ -34,7 +34,7 @@ - Models/ScaleWidgetY.obj + Models/ScaleWidgetY.mesh @@ -48,7 +48,7 @@ - Models/ScaleWidgetZ.obj + Models/ScaleWidgetZ.mesh diff --git a/resources/Schema/Entities/EditorWidgetTranslate.xml b/resources/Schema/Entities/EditorWidgetTranslate.xml index a9b7eaff..d4ed5e76 100644 --- a/resources/Schema/Entities/EditorWidgetTranslate.xml +++ b/resources/Schema/Entities/EditorWidgetTranslate.xml @@ -3,7 +3,7 @@ - Models/TranslationWidgetOrigin.obj + Models/TranslationWidgetOrigin.mesh @@ -18,7 +18,7 @@ - Models/TranslationWidgetX.obj + Models/TranslationWidgetX.mesh @@ -30,7 +30,7 @@ - Models/TranslationWidgetY.obj + Models/TranslationWidgetY.mesh @@ -42,7 +42,7 @@ - Models/TranslationWidgetZ.obj + Models/TranslationWidgetZ.mesh @@ -54,7 +54,7 @@ - Models/WidgetPlaneX.obj + Models/WidgetPlaneX.mesh @@ -66,7 +66,7 @@ - Models/WidgetPlaneY.obj + Models/WidgetPlaneY.mesh @@ -78,7 +78,7 @@ - Models/WidgetPlaneZ.obj + Models/WidgetPlaneZ.mesh diff --git a/resources/Schema/Entities/FastWorld.xml b/resources/Schema/Entities/FastWorld.xml new file mode 100644 index 00000000..68d1dccb --- /dev/null +++ b/resources/Schema/Entities/FastWorld.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + Models/Core/UnitSphere.mesh + + + + + + + + + + + + + + Crouch Walk + + 32 + + + Models/AssaultAnimated.mesh + + + + + + + + + + + pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew pew + Fonts/DroidSans.ttf,60 + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/GameMap.xml b/resources/Schema/Entities/GameMap.xml new file mode 100644 index 00000000..97fd3f4d --- /dev/null +++ b/resources/Schema/Entities/GameMap.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + + Models\MapVersion1.mesh + + + + + + + + + 2 + + + Models/DirectionalLightWidget.mesh + false + + + + + + + + + + + + + 8 + 2.7999999523162842 + + + + + + + + + + + 8 + 2.7999999523162842 + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/Model.xml b/resources/Schema/Entities/Model.xml new file mode 100644 index 00000000..57856cbd --- /dev/null +++ b/resources/Schema/Entities/Model.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + models/animTest.mesh + + + + + + + + diff --git a/resources/Schema/Entities/MovementTest.xml b/resources/Schema/Entities/MovementTest.xml index 93b4d374..1378f623 100644 --- a/resources/Schema/Entities/MovementTest.xml +++ b/resources/Schema/Entities/MovementTest.xml @@ -6,65 +6,293 @@ - + - + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + Models/Assault.mesh + + + + + + + + + + + + + Models/Assault.mesh + + + + + + + + + + + + + + + Models/DirectionalLightWidget.mesh + + + + + + + + + + + + Models/Test/ObstacleCourse.mesh + - + - Models/Core/UnitCube.obj - + Models/Core/UnitCube.mesh + false - - + + + + + + + + Models/Core/UnitCube.mesh + false + + + + + + + + + + + + + + Models/Core/UnitCube.mesh + false + + + + + + + + + + + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + Models/Assault.mesh + + + + + + + + + + + + + Models/Assault.mesh + + + + + + + + + + - + - - - Models/Assault.obj - - - - - - - - - - - - - + - + false - - Models/Core/UnitCube.obj - - - + + 5 + + + + + + - - + + - + + + + + + + + + + + + + Fonts/DroidSans.ttf,100 + + false + + + + + + + + + + + + + Models/Camera.mesh + + + + + + + + + + + + + + + Models/Camera.mesh + false + + + + + + + + + + + + Models/AssaultHeadless.mesh + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + diff --git a/resources/Schema/Entities/OctreeTest.xml b/resources/Schema/Entities/OctreeTest.xml index 4d7fe709..a510a98c 100644 --- a/resources/Schema/Entities/OctreeTest.xml +++ b/resources/Schema/Entities/OctreeTest.xml @@ -11,7 +11,7 @@ - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh @@ -27,7 +27,7 @@ false - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh @@ -42,7 +42,7 @@ false - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/Player.xml b/resources/Schema/Entities/Player.xml index 6c3b39c3..7914f82b 100644 --- a/resources/Schema/Entities/Player.xml +++ b/resources/Schema/Entities/Player.xml @@ -2,17 +2,183 @@ - - + + + + + - - Models/Core/UnitSphere.obj - - - - + + + + + 5 + + + + + + + + + - + + + + + + + + + + + + + Fonts/DroidSans.ttf,100 + + + + + + + + + + + + + + Models/Camera.mesh + false + + + + + + + + + + + + + + + + + + + 1 + + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + + Models/CrosshairQuad.mesh + + + + + + + + + + + + + Models/AssaultWeapon.mesh + + + + + + + + + + + + + + + + + + + + + + + + + Models/Camera.mesh + false + + + + + + + + + + + + Hold Pos + + 1 + + + Models/AssaultAnimated.mesh + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + + + + + Models/Core/UnitCube.mesh + + false + + + + + + + + diff --git a/resources/Schema/Entities/PointLight.xml b/resources/Schema/Entities/PointLight.xml new file mode 100644 index 00000000..64c02753 --- /dev/null +++ b/resources/Schema/Entities/PointLight.xml @@ -0,0 +1,16 @@ + + + + + + 8 + 2.7999999523162842 + + + + + + + + + diff --git a/resources/Schema/Entities/RaptorCopter.xml b/resources/Schema/Entities/RaptorCopter.xml index 0ee62368..82aa0efa 100644 --- a/resources/Schema/Entities/RaptorCopter.xml +++ b/resources/Schema/Entities/RaptorCopter.xml @@ -7,7 +7,7 @@ - Models/Core/UnitRaptor.obj + Models/Core/UnitRaptor.mesh @@ -31,7 +31,7 @@ - Models/Core/UnitCylinder.obj + Models/Core/UnitCylinder.mesh @@ -44,7 +44,7 @@ - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh @@ -57,7 +57,7 @@ - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/RayBlue b/resources/Schema/Entities/RayBlue new file mode 100644 index 00000000..d34e185e --- /dev/null +++ b/resources/Schema/Entities/RayBlue @@ -0,0 +1,17 @@ + + + + + + Models/Core/UnitCylinder.mesh + + + + + + + + + + + diff --git a/resources/Schema/Entities/RayBlue.xml b/resources/Schema/Entities/RayBlue.xml new file mode 100644 index 00000000..3b985a7e --- /dev/null +++ b/resources/Schema/Entities/RayBlue.xml @@ -0,0 +1,19 @@ + + + + + + 0.25 + + + Models/CylinderBullet.mesh + + + + + + + + + + diff --git a/resources/Schema/Entities/RayRed b/resources/Schema/Entities/RayRed new file mode 100644 index 00000000..d34e185e --- /dev/null +++ b/resources/Schema/Entities/RayRed @@ -0,0 +1,17 @@ + + + + + + Models/Core/UnitCylinder.mesh + + + + + + + + + + + diff --git a/resources/Schema/Entities/RayRed.xml b/resources/Schema/Entities/RayRed.xml new file mode 100644 index 00000000..df563476 --- /dev/null +++ b/resources/Schema/Entities/RayRed.xml @@ -0,0 +1,19 @@ + + + + + + 0.25 + + + Models/CylinderBullet.mesh + + + + + + + + + + diff --git a/resources/Schema/Entities/RenderingWorld.xml b/resources/Schema/Entities/RenderingWorld.xml index 69b18982..20301d5f 100644 --- a/resources/Schema/Entities/RenderingWorld.xml +++ b/resources/Schema/Entities/RenderingWorld.xml @@ -6,43 +6,124 @@ - + - - ActionCamera - - - Models/Camera.obj - + + 65.990005493164063 + + - - + - + - - Camera #2 - Fonts/DroidSans.ttf,64 - 0 - + + Models/Assault.obj + + - - + + + + + 80 + + + Models/Camera.mesh + + + + + + + + + + + + + + + + + + + 0.65990006923675537 + + + + Models/Core/UnitHexagon.mesh + + + + + + + + + + + + + 24 + Fonts/DroidSans.ttf,60 + + + + + + + + + + + 262 + Fonts/DroidSans.ttf,60 + + + + + + + + + + + + + + + + + 65/100 + Fonts/DroidSans.ttf,60 + + + + + + + + + + + + - Models/Core/UnitPlane.obj + Models/Core/UnitPlane.mesh - + @@ -51,7 +132,8 @@ - Models/Assault.obj + Models/Assault.mesh + @@ -64,6 +146,7 @@ Welcome! Fonts/DroidSans.ttf,1280 + @@ -74,32 +157,40 @@ - 2 - + 3 + - - + + + Models/Core/UnitSphere.obj + + - 6 + 70 0.5 + + + Models/Core/UnitSphere.obj + + - 8 + 70 @@ -111,21 +202,30 @@ + + Models/Core/UnitSphere.obj + + - 6 + 70 0.5 + + + Models/Core/UnitSphere.obj + + - 8 + 70 @@ -137,21 +237,30 @@ + + Models/Core/UnitSphere.obj + + - 6 + 70 0.5 + + + Models/Core/UnitSphere.obj + + - - 8 + + 70 @@ -163,19 +272,28 @@ + + Models/Core/UnitSphere.obj + + - 6 + 70 0.5 + + + Models/Core/UnitSphere.obj + + - 8 + 70 @@ -190,7 +308,8 @@ - Models/Assault.obj + Models/Assault.mesh + @@ -198,46 +317,15 @@ - - - - MainCamera - - - - Models/Camera.obj - false - - - - - - - - - - - Taiwan #1 - Fonts/DroidSans.ttf,64 - 0 - - - - - - - - - - - + - 0.80000001192092896 + 0.10000000149011612 - Models/DirectionalLightWidget.obj + Models/DirectionalLightWidget.mesh + @@ -249,7 +337,8 @@ - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh + diff --git a/resources/Schema/Entities/ShootEventTest.xml b/resources/Schema/Entities/ShootEventTest.xml index eae333b6..9e9adf50 100644 --- a/resources/Schema/Entities/ShootEventTest.xml +++ b/resources/Schema/Entities/ShootEventTest.xml @@ -9,7 +9,7 @@ - ../assets/Models/DummyScene.obj + ../assets/Models/DummyScene.mesh @@ -41,7 +41,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -61,7 +61,7 @@ 1 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -79,7 +79,7 @@ 2 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -96,7 +96,7 @@ 3 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -115,7 +115,7 @@ 4 - ../assets/Models/Core/UnitSphere.obj + ../assets/Models/Core/UnitSphere.mesh @@ -133,7 +133,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -151,7 +151,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -171,7 +171,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh @@ -191,7 +191,7 @@ - ../assets/Models/Core/UnitCube.obj + ../assets/Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/SoundTestLevel.xml b/resources/Schema/Entities/SoundTestLevel.xml index 366ae81b..4e9c3093 100644 --- a/resources/Schema/Entities/SoundTestLevel.xml +++ b/resources/Schema/Entities/SoundTestLevel.xml @@ -12,7 +12,7 @@ - Models/Core/UnitCube.obj + Models/Core/UnitCube.mesh diff --git a/resources/Schema/Entities/SpawnTest.xml b/resources/Schema/Entities/SpawnTest.xml index a5574027..16608b47 100644 --- a/resources/Schema/Entities/SpawnTest.xml +++ b/resources/Schema/Entities/SpawnTest.xml @@ -24,7 +24,7 @@ - Models/Core/UnitSphere.obj + Models/Core/UnitSphere.mesh @@ -37,7 +37,7 @@ - Models/Core/UnitSphere.obj + Models/Core/UnitSphere.mesh diff --git a/resources/Schema/Entities/Spawnpoint b/resources/Schema/Entities/Spawnpoint new file mode 100644 index 00000000..a53aaa08 --- /dev/null +++ b/resources/Schema/Entities/Spawnpoint @@ -0,0 +1,32 @@ + + + + + + + Models/Core/UnitCube.mesh + + + Schema/Entities/Player.xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Schema/Entities/Test.xml b/resources/Schema/Entities/Test.xml index 42e2660e..1f8da1a3 100644 --- a/resources/Schema/Entities/Test.xml +++ b/resources/Schema/Entities/Test.xml @@ -6,7 +6,7 @@ - Models/DummyScene.obj + Models/DummyScene.mesh @@ -19,7 +19,7 @@ - Models/Camera.obj + Models/Camera.mesh diff --git a/resources/Schema/Types.xsd b/resources/Schema/Types.xsd index fb584c64..8d9bf289 100644 --- a/resources/Schema/Types.xsd +++ b/resources/Schema/Types.xsd @@ -16,6 +16,9 @@ + + + diff --git a/resources/Schema/Types/Entity.xsd b/resources/Schema/Types/Entity.xsd index 5583b8b8..5d135847 100644 --- a/resources/Schema/Types/Entity.xsd +++ b/resources/Schema/Types/Entity.xsd @@ -32,6 +32,7 @@ + diff --git a/resources/Shaders/BasicForward.vert.glsl b/resources/Shaders/BasicForward.vert.glsl index 96f081f4..cb231e45 100644 --- a/resources/Shaders/BasicForward.vert.glsl +++ b/resources/Shaders/BasicForward.vert.glsl @@ -9,18 +9,14 @@ layout(location = 1) in vec3 Normal; layout(location = 2) in vec3 Tangent; layout(location = 3) in vec3 BiTangent; layout(location = 4) in vec2 TextureCoords; -layout(location = 5) in vec4 DiffuseVertexColor; -layout(location = 6) in vec4 SpecularVertexColor; -layout(location = 7) in vec4 BoneIndices1; -layout(location = 8) in vec4 BoneIndices2; -layout(location = 9) in vec4 BoneWeights1; -layout(location = 10) in vec4 BoneWeights2; +layout(location = 5) in vec4 BoneIndices; +layout(location = 6) in vec4 BoneWeights; + out VertexData{ vec3 Position; vec3 Normal; vec2 TextureCoordinate; - vec4 DiffuseColor; }Output; void main() @@ -30,5 +26,4 @@ void main() Output.Position = Position; Output.TextureCoordinate = TextureCoords; Output.Normal = Normal; - Output.DiffuseColor = DiffuseVertexColor; } \ No newline at end of file diff --git a/resources/Shaders/ForwardPlus.frag.glsl b/resources/Shaders/ForwardPlus.frag.glsl index e158640e..f33d870b 100644 --- a/resources/Shaders/ForwardPlus.frag.glsl +++ b/resources/Shaders/ForwardPlus.frag.glsl @@ -4,10 +4,14 @@ uniform mat4 M; uniform mat4 V; uniform mat4 P; uniform vec4 Color; +uniform vec4 DiffuseColor; uniform vec2 ScreenDimensions; +uniform vec4 FillColor; +uniform float FillPercentage; layout (binding = 0) uniform sampler2D DiffuseTexture; layout (binding = 1) uniform sampler2D GlowMap; + #define TILE_SIZE 16 struct LightSource { @@ -46,7 +50,6 @@ in VertexData{ vec3 Position; vec3 Normal; vec2 TextureCoordinate; - vec4 DiffuseColor; }Input; out vec4 sceneColor; @@ -135,11 +138,22 @@ void main() } //sceneColor += Input.DiffuseColor; - vec4 color_result = Input.DiffuseColor * (totalLighting.Diffuse + totalLighting.Specular) * diffuseTexel * Color; + vec4 color_result = DiffuseColor * (totalLighting.Diffuse + totalLighting.Specular) * diffuseTexel * Color; + + + float pos = ((P * vec4(Input.Position, 1)).y + 1.0)/2.0; + + if(pos <= FillPercentage) { + color_result += FillColor; + } //bloomColor = vec4(0.3, 0.8, 0.6, 1.0); sceneColor = vec4(color_result.xyz, clamp(color_result.a, 0, 1)); //These if statements should be removed if they are slow. color_result += glowTexel; + + + + bloomColor = vec4(clamp(color_result.xyz - 1.0, 0, 100), 1.0); /* if(color_result.x > 1 || color_result.y > 1 || color_result.z > 1) { @@ -150,8 +164,9 @@ void main() //sceneColor += Input.DiffuseColor * (totalLighting.Diffuse) * diffuseTexel * Color; //sceneColor += Input.DiffuseColor + vec4(0.0, LightGrids.Data[currentTile].Amount/3, 0, 1); - //sceneColor = diffuseTexel * Input.DiffuseColor * Color; - //sceneColor += vec4(currentTile/3600.f, 0, 0, 1); + + + //Tiled Debug Code /* diff --git a/resources/Shaders/ForwardPlus.vert.glsl b/resources/Shaders/ForwardPlus.vert.glsl index d3b8ba16..c51216cb 100644 --- a/resources/Shaders/ForwardPlus.vert.glsl +++ b/resources/Shaders/ForwardPlus.vert.glsl @@ -3,32 +3,37 @@ uniform mat4 M; uniform mat4 V; uniform mat4 P; +uniform mat4 Bones[100]; layout(location = 0) in vec3 Position; layout(location = 1) in vec3 Normal; layout(location = 2) in vec3 Tangent; layout(location = 3) in vec3 BiTangent; layout(location = 4) in vec2 TextureCoords; -layout(location = 5) in vec4 DiffuseVertexColor; -layout(location = 6) in vec4 SpecularVertexColor; -layout(location = 7) in vec4 BoneIndices1; -layout(location = 8) in vec4 BoneIndices2; -layout(location = 9) in vec4 BoneWeights1; -layout(location = 10) in vec4 BoneWeights2; +layout(location = 5) in vec4 BoneIndices; +layout(location = 6) in vec4 BoneWeights; out VertexData{ vec3 Position; vec3 Normal; vec2 TextureCoordinate; - vec4 DiffuseColor; }Output; void main() { - gl_Position = P*V*M * vec4(Position, 1.0); - Output.Position = Position; + + mat4 boneTransform = mat4(1); + if(BoneWeights[0] > 0.0f){ + boneTransform = BoneWeights[0] * Bones[int(BoneIndices[0])] + + BoneWeights[1] * Bones[int(BoneIndices[1])] + + BoneWeights[2] * Bones[int(BoneIndices[2])] + + BoneWeights[3] * Bones[int(BoneIndices[3])]; + } + + gl_Position = P*V*M*boneTransform * vec4(Position, 1.0); + + Output.Position = (boneTransform * vec4(Position, 1.0)).xyz; Output.TextureCoordinate = TextureCoords; Output.Normal = vec3(M * vec4(Normal, 0.0)); - Output.DiffuseColor = DiffuseVertexColor; } \ No newline at end of file diff --git a/resources/Shaders/Picking.vert.glsl b/resources/Shaders/Picking.vert.glsl index b2857cea..205a8fdd 100644 --- a/resources/Shaders/Picking.vert.glsl +++ b/resources/Shaders/Picking.vert.glsl @@ -3,18 +3,15 @@ uniform mat4 M; uniform mat4 V; uniform mat4 P; +uniform mat4 Bones[100]; layout(location = 0) in vec3 Position; layout(location = 1) in vec3 Normal; layout(location = 2) in vec3 Tangent; layout(location = 3) in vec3 BiTangent; layout(location = 4) in vec2 TextureCoords; -layout(location = 5) in vec4 DiffuseVertexColor; -layout(location = 6) in vec4 SpecularVertexColor; -layout(location = 7) in vec4 BoneIndices1; -layout(location = 8) in vec4 BoneIndices2; -layout(location = 9) in vec4 BoneWeights1; -layout(location = 10) in vec4 BoneWeights2; +layout(location = 5) in vec4 BoneIndices; +layout(location = 6) in vec4 BoneWeights; out VertexData{ vec3 Position; @@ -22,7 +19,14 @@ out VertexData{ void main() { - gl_Position = P * V* M * vec4(Position, 1.0); + mat4 boneTransform = mat4(1); + if(BoneWeights[0] > 0.0f){ + boneTransform = BoneWeights[0] * Bones[int(BoneIndices[0])] + + BoneWeights[1] * Bones[int(BoneIndices[1])] + + BoneWeights[2] * Bones[int(BoneIndices[2])] + + BoneWeights[3] * Bones[int(BoneIndices[3])]; + } - Output.Position = Position; + gl_Position = P*V*M*boneTransform * vec4(Position, 1.0); + Output.Position = (boneTransform * vec4(Position, 1.0)).xyz; } \ No newline at end of file diff --git a/src/Engine/Collision/CollisionSystem.cpp b/src/Engine/Collision/CollisionSystem.cpp index 612d0707..8d43534c 100644 --- a/src/Engine/Collision/CollisionSystem.cpp +++ b/src/Engine/Collision/CollisionSystem.cpp @@ -27,7 +27,7 @@ void CollisionSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& c // } // if (Collision::AABBVsAABB(boxA, boxB, resolutionVector)) { // (glm::vec3&)cTransform["Position"] += resolutionVector; - // cPhysics["Velocity"] = glm::vec3(0, 0, 0); + // ((glm::vec3&)cPhysics["Velocity"]).y = 0.f; // } //} diff --git a/src/Engine/Core/EntityWrapper.cpp b/src/Engine/Core/EntityWrapper.cpp index 98b9c7d4..1f3fc1ab 100644 --- a/src/Engine/Core/EntityWrapper.cpp +++ b/src/Engine/Core/EntityWrapper.cpp @@ -3,8 +3,16 @@ const EntityWrapper EntityWrapper::Invalid = EntityWrapper(nullptr, EntityID_Invalid); +const std::string EntityWrapper::Name() +{ + return World->GetName(ID); +} + bool EntityWrapper::HasComponent(const std::string& componentName) { + if (!Valid()) { + return false; + } return World->HasComponent(ID, componentName); } @@ -17,6 +25,35 @@ EntityWrapper EntityWrapper::Parent() } } +EntityWrapper EntityWrapper::FirstChildByName(const std::string& name) +{ + return firstChildByNameRecursive(name, this->ID); +} + +EntityWrapper EntityWrapper::FirstParentWithComponent(const std::string& componentType) +{ + EntityWrapper entity = *this; + while (entity.Parent().Valid()) { + entity = entity.Parent(); + if (entity.HasComponent(componentType)) { + return entity; + } + } + return EntityWrapper::Invalid; +} + +bool EntityWrapper::IsChildOf(EntityWrapper potentialParent) +{ + EntityWrapper entity = *this; + while (entity.Parent().Valid()) { + entity = entity.Parent(); + if (entity == potentialParent) { + return true; + } + } + return false; +} + bool EntityWrapper::Valid() { if (this->World == nullptr) { @@ -65,3 +102,29 @@ EntityWrapper::operator bool() return this->Valid(); } +EntityWrapper EntityWrapper::firstChildByNameRecursive(const std::string& name, EntityID parent) +{ + if (!this->World->ValidEntity(parent)) { + return EntityWrapper::Invalid; + } + + auto itPair = this->World->GetChildren(parent); + if (itPair.first == itPair.second) { + return EntityWrapper::Invalid; + } + + for (auto it = itPair.first; it != itPair.second; ++it) { + std::string itName = this->World->GetName(it->second); + if (itName == name) { + return EntityWrapper(this->World, it->second); + } else if (it->second != EntityID_Invalid) { + EntityWrapper result = firstChildByNameRecursive(name, it->second); + if (result != EntityWrapper::Invalid) { + return result; + } + } + } + + return EntityWrapper::Invalid; +} + diff --git a/src/Engine/Core/Transform.cpp b/src/Engine/Core/Transform.cpp index 054b1984..333c3532 100644 --- a/src/Engine/Core/Transform.cpp +++ b/src/Engine/Core/Transform.cpp @@ -1,5 +1,22 @@ #include "Core/Transform.h" +glm::mat4 Transform::AbsoluteTransformation(EntityWrapper entity) +{ + glm::mat4 t = glm::mat4(1.f); + + while (entity.Valid()) { + t = glm::translate((glm::vec3&)entity["Transform"]["Position"]) * glm::toMat4(glm::quat((glm::vec3&)entity["Transform"]["Orientation"])) * glm::scale((glm::vec3&)entity["Transform"]["Scale"]) * t; + entity = entity.Parent(); + } + + return t; +} + +glm::vec3 Transform::AbsolutePosition(EntityWrapper entity) +{ + return AbsolutePosition(entity.World, entity.ID); +} + glm::vec3 Transform::AbsolutePosition(World* world, EntityID entity) { glm::vec3 position; @@ -14,6 +31,24 @@ glm::vec3 Transform::AbsolutePosition(World* world, EntityID entity) return position; } +glm::vec3 Transform::AbsoluteOrientationEuler(EntityWrapper entity) +{ + glm::vec3 orientation; + + while (entity.Valid()) { + ComponentWrapper transform = entity["Transform"]; + orientation += (glm::vec3)transform["Orientation"]; + entity = entity.Parent(); + } + + return orientation; +} + +glm::quat Transform::AbsoluteOrientation(EntityWrapper entity) +{ + return AbsoluteOrientation(entity.World, entity.ID); +} + glm::quat Transform::AbsoluteOrientation(World* world, EntityID entity) { glm::quat orientation; @@ -27,6 +62,11 @@ glm::quat Transform::AbsoluteOrientation(World* world, EntityID entity) return orientation; } +glm::vec3 Transform::AbsoluteScale(EntityWrapper entity) +{ + return AbsoluteScale(entity.World, entity.ID); +} + glm::vec3 Transform::AbsoluteScale(World* world, EntityID entity) { glm::vec3 scale(1.f); @@ -40,8 +80,15 @@ glm::vec3 Transform::AbsoluteScale(World* world, EntityID entity) return scale; } +glm::mat4 Transform::ModelMatrix(EntityWrapper entity) +{ + return ModelMatrix(entity.ID, entity.World); +} + glm::mat4 Transform::ModelMatrix(EntityID entity, World* world) { + return AbsoluteTransformation(EntityWrapper(world, entity)); + glm::vec3 position = Transform::AbsolutePosition(world, entity); glm::quat orientation = Transform::AbsoluteOrientation(world, entity); glm::vec3 scale = Transform::AbsoluteScale(world, entity); diff --git a/src/Engine/Core/World.cpp b/src/Engine/Core/World.cpp index 477e2ab2..0c4c9ce4 100644 --- a/src/Engine/Core/World.cpp +++ b/src/Engine/Core/World.cpp @@ -1,4 +1,6 @@ #include "Core/World.h" +#include "Core/EEntityDeleted.h" +#include "Core/EComponentDeleted.h" World::~World() { @@ -21,37 +23,7 @@ EntityID World::CreateEntity(EntityID parent /*= 0*/) void World::DeleteEntity(EntityID entity) { - // Delete components - for (auto& pair : m_ComponentPools) { - auto& pool = pair.second; - if (pool->KnowsEntity(entity)) { - auto& c = pool->GetByEntity(entity); - pool->Delete(c); - } - } - - // Loop through children - std::vector childrenToDelete; - auto children = m_EntityChildren.equal_range(entity); - for (auto it = children.first; it != children.second; ++it) { - childrenToDelete.push_back(it->second); - } - for (auto& child : childrenToDelete) { - DeleteEntity(child); - } - - EntityID parent = m_EntityParents.at(entity); - m_EntityParents.erase(entity); - auto parentChildren = m_EntityChildren.equal_range(parent); - for (auto it = parentChildren.first; it != parentChildren.second; ++it) { - if (it->second == entity) { - m_EntityChildren.erase(it); - break; - } - } - - // Erase potential name - m_EntityNames.erase(entity); + deleteEntityRecursive(entity, false); } bool World::ValidEntity(EntityID entity) const @@ -96,7 +68,15 @@ void World::DeleteComponent(EntityID entity, const std::string& componentType) { ComponentPool* pool = m_ComponentPools.at(componentType); ComponentWrapper c = pool->GetByEntity(entity); - return pool->Delete(c); + pool->Delete(c); + + if (m_EventBroker != nullptr) { + Events::ComponentDeleted e; + e.Entity = entity; + e.ComponentType = componentType; + e.Cascaded = false; + m_EventBroker->Publish(e); + } } const ComponentPool* World::GetComponents(const std::string& componentType) @@ -155,3 +135,57 @@ EntityID World::generateEntityID() return m_CurrentEntityID++; } +void World::deleteEntityRecursive(EntityID entity, bool cascaded /*= false*/) +{ + // Don't attempt to delete entities that don't exist anyway + if (!ValidEntity(entity)) { + return; + } + + if (m_EventBroker != nullptr) { + Events::EntityDeleted e; + e.DeletedEntity = entity; + e.Cascaded = cascaded; + m_EventBroker->Publish(e); + } + + // Delete components + for (auto& pair : m_ComponentPools) { + auto& pool = pair.second; + if (pool->KnowsEntity(entity)) { + auto& c = pool->GetByEntity(entity); + pool->Delete(c); + if (m_EventBroker != nullptr) { + Events::ComponentDeleted e; + e.Entity = entity; + e.ComponentType = pair.first; + e.Cascaded = true; + m_EventBroker->Publish(e); + } + } + } + + // Loop through children + std::vector childrenToDelete; + auto children = m_EntityChildren.equal_range(entity); + for (auto it = children.first; it != children.second; ++it) { + childrenToDelete.push_back(it->second); + } + for (auto& child : childrenToDelete) { + deleteEntityRecursive(child, true); + } + + EntityID parent = m_EntityParents.at(entity); + m_EntityParents.erase(entity); + auto parentChildren = m_EntityChildren.equal_range(parent); + for (auto it = parentChildren.first; it != parentChildren.second; ++it) { + if (it->second == entity) { + m_EntityChildren.erase(it); + break; + } + } + + // Erase potential name + m_EntityNames.erase(entity); +} + diff --git a/src/Engine/Editor/EditorGUI.cpp b/src/Engine/Editor/EditorGUI.cpp index 390e69a5..1200ce34 100644 --- a/src/Engine/Editor/EditorGUI.cpp +++ b/src/Engine/Editor/EditorGUI.cpp @@ -1,4 +1,5 @@ #include "Editor/EditorGUI.h" +#include "Rendering/ESetCamera.h" EditorGUI::EditorGUI(World* world, EventBroker* eventBroker) : m_World(world) @@ -305,6 +306,14 @@ bool EditorGUI::drawComponentNode(EntityWrapper entity, const ComponentInfo& ci) } } + if (ci.Name == "Camera") { + if (ImGui::Button("Activate")) { + Events::SetCamera e; + e.CameraEntity = entity; + m_EventBroker->Publish(e); + } + } + return true; } diff --git a/src/Engine/Editor/EditorRenderSystem.cpp b/src/Engine/Editor/EditorRenderSystem.cpp index 2e31e3f0..2aad1340 100644 --- a/src/Engine/Editor/EditorRenderSystem.cpp +++ b/src/Engine/Editor/EditorRenderSystem.cpp @@ -39,7 +39,7 @@ void EditorRenderSystem::Update(double dt) continue; } catch (const std::exception&) { try { - model = ResourceManager::Load<::Model>("Models/Core/Error.obj"); + model = ResourceManager::Load<::Model>("Models/Core/Error.mesh"); } catch (const std::exception&) { continue; } @@ -48,7 +48,7 @@ void EditorRenderSystem::Update(double dt) EntityWrapper entity(m_World, cModel.EntityID); glm::mat4 modelMatrix = Transform::ModelMatrix(entity.ID, entity.World); for (auto matGroup : model->MaterialGroups()) { - std::shared_ptr modelJob = std::make_shared(model, scene.Camera, modelMatrix, matGroup, cModel, entity.World); + std::shared_ptr modelJob = std::make_shared(model, scene.Camera, modelMatrix, matGroup, cModel, entity.World, glm::vec4(0), 0.f); scene.ForwardJobs.push_back(modelJob); } } diff --git a/src/Engine/Editor/EditorSystem.cpp b/src/Engine/Editor/EditorSystem.cpp index 35b8a361..6ffd0e24 100644 --- a/src/Engine/Editor/EditorSystem.cpp +++ b/src/Engine/Editor/EditorSystem.cpp @@ -14,10 +14,11 @@ EditorSystem::EditorSystem(World* world, EventBroker* eventBroker, IRenderer* re m_EditorWorldSystemPipeline->AddSystem(0, m_Renderer); m_EditorWorldSystemPipeline->AddSystem(1, m_Renderer, m_RenderFrame); - m_Camera = importEntity(EntityWrapper(m_EditorWorld, EntityID_Invalid), "Schema/Entities/Empty.xml"); - m_EditorWorld->AttachComponent(m_Camera.ID, "Transform"); - m_EditorWorld->AttachComponent(m_Camera.ID, "Camera"); - m_DebugCameraInputController = new DebugCameraInputController(m_EventBroker, -1); + m_EditorCamera = importEntity(EntityWrapper(m_EditorWorld, EntityID_Invalid), "Schema/Entities/Empty.xml"); + m_ActualCamera = m_EditorCamera; + m_EditorWorld->AttachComponent(m_EditorCamera.ID, "Transform"); + m_EditorWorld->AttachComponent(m_EditorCamera.ID, "Camera"); + m_EditorCameraInputController = new EditorCameraInputController(m_EventBroker, -1); m_EditorGUI = new EditorGUI(m_World, m_EventBroker); m_EditorGUI->SetEntitySelectedCallback(std::bind(&EditorSystem::OnEntitySelected, this, std::placeholders::_1)); @@ -33,38 +34,70 @@ EditorSystem::EditorSystem(World* world, EventBroker* eventBroker, IRenderer* re EVENT_SUBSCRIBE_MEMBER(m_EMousePress, &EditorSystem::OnMousePress); EVENT_SUBSCRIBE_MEMBER(m_EWidgetDelta, &EditorSystem::OnWidgetDelta); + EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &EditorSystem::OnInputCommand); + EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &EditorSystem::OnSetCamera); m_EditorStats = new EditorStats(); - Events::SetCamera e; - e.CameraEntity = m_Camera; - m_EventBroker->Publish(e); + if (m_Enabled) { + Enable(); + } } EditorSystem::~EditorSystem() { delete m_EditorStats; delete m_EditorGUI; - delete m_DebugCameraInputController; + delete m_EditorCameraInputController; delete m_EditorWorldSystemPipeline; delete m_EditorWorld; } void EditorSystem::Update(double dt) { - m_EventBroker->Process(); - m_EditorGUI->Draw(); - m_EditorStats->Draw(dt); + double now = glfwGetTime(); + double actualDelta = now - m_LastTime; + m_LastTime = now; - if (m_CurrentSelection.Valid() && m_Widget.Valid()) { - (glm::vec3&)m_Widget["Transform"]["Position"] = Transform::AbsolutePosition(m_CurrentSelection.World, m_CurrentSelection.ID); + if (m_Enabled) { + m_EventBroker->Process(); + m_EditorGUI->Draw(); + m_EditorStats->Draw(actualDelta); + + if (m_CurrentSelection.Valid() && m_Widget.Valid()) { + (glm::vec3&)m_Widget["Transform"]["Position"] = Transform::AbsolutePosition(m_CurrentSelection.World, m_CurrentSelection.ID); + } + + m_EditorWorldSystemPipeline->Update(actualDelta); + + ComponentWrapper& cameraTransform = m_EditorCamera["Transform"]; + glm::vec3& ori = cameraTransform["Orientation"]; + ori.x = m_EditorCameraInputController->Rotation().x; + ori.y = m_EditorCameraInputController->Rotation().y; + glm::vec3& pos = cameraTransform["Position"]; + pos += m_EditorCameraInputController->Movement() * glm::inverse(glm::quat(ori)) * (float)actualDelta; } +} - m_EditorWorldSystemPipeline->Update(dt); +void EditorSystem::Enable() +{ + m_EditorCameraInputController->Enable(); + m_EventBroker->Publish(Events::UnlockMouse()); + Events::SetCamera e; + e.CameraEntity = m_EditorCamera; + m_EventBroker->Publish(e); + (glm::vec3&)m_EditorCamera["Transform"]["Position"] = Transform::AbsolutePosition(m_ActualCamera); + m_Enabled = true; +} - m_DebugCameraInputController->Update(dt); - m_Camera["Transform"]["Position"] = m_DebugCameraInputController->Position(); - m_Camera["Transform"]["Orientation"] = glm::eulerAngles(m_DebugCameraInputController->Orientation()); +void EditorSystem::Disable() +{ + m_EditorCameraInputController->Disable(); + m_EventBroker->Publish(Events::LockMouse()); + Events::SetCamera e; + e.CameraEntity = m_ActualCamera; + m_EventBroker->Publish(e); + m_Enabled = false; } void EditorSystem::OnEntitySelected(EntityWrapper entity) @@ -148,6 +181,33 @@ bool EditorSystem::OnWidgetDelta(const Events::WidgetDelta& e) return true; } +bool EditorSystem::OnInputCommand(const Events::InputCommand& e) +{ + if (e.PlayerID != -1) { + return false; + } + + if (e.Command == "ToggleEditor" && e.Value > 0) { + if (m_Enabled) { + Disable(); + } else { + Enable(); + } + } + return true; +} + +bool EditorSystem::OnSetCamera(const Events::SetCamera& e) +{ + if (m_Enabled && e.CameraEntity != m_EditorCamera) { + m_ActualCamera = e.CameraEntity; + Events::SetCamera e2; + e2.CameraEntity = m_EditorCamera; + //m_EventBroker->Publish(e2); + } + return true; +} + EntityWrapper EditorSystem::importEntity(EntityWrapper parent, boost::filesystem::path filePath) { if (parent.World == nullptr) { diff --git a/src/Engine/Network/Client.cpp b/src/Engine/Network/Client.cpp index 10432bba..6c43cc8b 100644 --- a/src/Engine/Network/Client.cpp +++ b/src/Engine/Network/Client.cpp @@ -5,14 +5,20 @@ using namespace boost::asio::ip; Client::Client(ConfigFile* config) : m_Socket(m_IOService) { - // Asumes root node is EntityID 0 - insertIntoServerClientMaps(0, 0); + Network::initialize(); + + // Asumes root node is EntityID_Invalid + insertIntoServerClientMaps(EntityID_Invalid, EntityID_Invalid); + // Init timer + m_TimeSinceSentInputs = std::clock(); // Default is local host std::string address = config->Get("Networking.Address", "127.0.0.1"); - int port = config->Get("Networking.Port", 13); + int port = config->Get("Networking.Port", 27666); m_ReceiverEndpoint = udp::endpoint(boost::asio::ip::address::from_string(address), port); // Set up network stream m_PlayerName = config->Get("Networking.Name", "Raptorcopter"); + m_SendInputIntervalMs = config->Get("Networking.SendInputIntervalMs", 33); + } Client::~Client() @@ -25,7 +31,8 @@ void Client::Start(World* world, EventBroker* eventBroker) // Subscribe to events EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Client::OnInputCommand); - EVENT_SUBSCRIBE_MEMBER(m_EPlayeDamage, &Client::OnPlayerDamage); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerDamage, &Client::OnPlayerDamage); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Client::OnPlayerSpawned); m_Socket.connect(m_ReceiverEndpoint); LOG_INFO("I am client. BIP BOP"); @@ -37,19 +44,25 @@ void Client::Update() readFromServer(); if (m_IsConnected) { hasServerTimedOut(); + // Don't sent 1 input in 1 packet, bunch em up. + if (m_SendInputIntervalMs < (1000 * (std::clock() - m_TimeSinceSentInputs) / (double)CLOCKS_PER_SEC)) { + sendInputCommands(); + m_TimeSinceSentInputs = std::clock(); + } + sendLocalPlayerTransform(); } + Network::Update(); } void Client::readFromServer() { while (m_Socket.available()) { - bytesRead = receive(readBuf, INPUTSIZE); + bytesRead = receive(readBuf); if (bytesRead > 0) { Packet packet(readBuf, bytesRead); parseMessageType(packet); } } - sendInputCommands(); } void Client::parseMessageType(Packet& packet) @@ -66,12 +79,9 @@ void Client::parseMessageType(Packet& packet) case MessageType::Connect: parseConnect(packet); break; - case MessageType::ClientPing: + case MessageType::Ping: parsePing(); break; - case MessageType::ServerPing: - parseServerPing(); - break; case MessageType::Message: break; case MessageType::Snapshot: @@ -81,6 +91,19 @@ void Client::parseMessageType(Packet& packet) break; case MessageType::PlayerConnected: parsePlayerConnected(packet); + break; + case MessageType::Kick: + parseKick(); + break; + case MessageType::OnPlayerSpawned: + parsePlayersSpawned(packet); + break; + case MessageType::EntityDeleted: + parseEntityDeletion(packet); + break; + case MessageType::ComponentDeleted: + parseComponentDeletion(packet); + break; default: break; } @@ -99,11 +122,6 @@ void Client::parsePlayerConnected(Packet & packet) } void Client::parsePing() -{ - -} - -void Client::parseServerPing() { // Might miss connect message so set it here instead. m_IsConnected = true; @@ -112,11 +130,49 @@ void Client::parseServerPing() LOG_INFO("%i: response time with ctime(ms): %f", m_PacketID, m_DurationOfPingTime); m_StartPingTime = std::clock(); - Packet packet(MessageType::ServerPing, m_SendPacketID); + Packet packet(MessageType::Ping, m_SendPacketID); packet.WriteString("Ping recieved"); send(packet); } +void Client::parseKick() +{ + LOG_WARNING("You have been kicked from the server."); + m_IsConnected = false; +} + +void Client::parsePlayersSpawned(Packet& packet) +{ + Events::PlayerSpawned e; + e.Player = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); + e.Spawner = EntityWrapper(m_World, m_ServerIDToClientID[packet.ReadPrimitive()]); + e.PlayerID = -1; + e.PlayerName = packet.ReadString(); + m_EventBroker->Publish(e); +} + +void Client::parseEntityDeletion(Packet & packet) +{ + EntityID entityToDelete = packet.ReadPrimitive(); + // TODO: What if an entity that didn't previously exist comes as a delete request and later comes in a delayed snapshot? + if (m_ServerIDToClientID.find(entityToDelete) != m_ServerIDToClientID.end()) { + EntityID localEntity = m_ServerIDToClientID.at(entityToDelete); + if (m_World->ValidEntity(localEntity)) { + m_World->DeleteEntity(localEntity); + deleteFromServerClientMaps(entityToDelete, localEntity); + } + } +} + +void Client::parseComponentDeletion(Packet & packet) +{ + EntityID entity = packet.ReadPrimitive(); + std::string componentType = packet.ReadString(); + if (m_World->HasComponent(entity, componentType)) { + m_World->DeleteComponent(m_ServerIDToClientID.at(entity), componentType); + } +} + // Fields with strings will not work right now void Client::InterpolateFields(Packet& packet, const ComponentInfo& componentInfo, const EntityID& entityID, const std::string& componentType) { @@ -151,75 +207,71 @@ void Client::updateFields(Packet& packet, const ComponentInfo& componentInfo, co void Client::parseSnapshot(Packet& packet) { - std::string componentType = packet.ReadString(); while (packet.DataReadSize() < packet.Size()) { - // Components EntityID - EntityID receivedEntityID = packet.ReadPrimitive(); - // Parents EntityID - EntityID receivedParentEntityID = packet.ReadPrimitive(); - ComponentInfo componentInfo = m_World->GetComponents(componentType)->ComponentInfo(); - // Check if the received EntityID is mapped to one of our local EntityIDs - if (serverClientMapsHasEntity(receivedEntityID)) { - // Get the local EntityID - EntityID entityID = m_ServerIDToClientID.at(receivedEntityID); - // Check if the component exists - if (m_World->HasComponent(entityID, componentType)) { - // If the entity and the component exists update it - if (componentType == "Transform") { - InterpolateFields(packet, componentInfo, entityID, componentType); + EntityID serverEntityID = packet.ReadPrimitive(); + EntityID serverParentID = packet.ReadPrimitive(); + std::string serverEntityName = packet.ReadString(); + int ammountOfComponents = packet.ReadPrimitive(); + for (int i = 0; i < ammountOfComponents; i++) { + std::string componentType = packet.ReadString(); + ComponentInfo componentInfo = m_World->GetComponents(componentType)->ComponentInfo(); + if (serverClientMapsHasEntity(serverEntityID)) { + EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID); + // Update entity + if (m_World->HasComponent(localEntityID, componentType)) { + // Update component + if (componentType == "Transform") { + // Interpolate only transform components + InterpolateFields(packet, componentInfo, localEntityID, componentType); + } else if (componentType == "Physics" && m_World->HasComponent(localEntityID, "Player")) { + // HACK: Ignore velocity of physics + packet.ReadData(componentInfo.Stride); + } else { + // Set component values + updateFields(packet, componentInfo, localEntityID, componentType); + } } else { - updateFields(packet, componentInfo, entityID, componentType); + // Has entity but no component + m_World->AttachComponent(localEntityID, componentType); + updateFields(packet, componentInfo, localEntityID, componentType); } - // if entity exists but not the component } else { - // Create component - m_World->AttachComponent(entityID, componentType); - // Copy data to newly created component - updateFields(packet, componentInfo, entityID, componentType); + // Create Entity and component + EntityID newLocalEntityID; + if (serverParentID == EntityID_Invalid) { + newLocalEntityID = m_World->CreateEntity(EntityID_Invalid); + } else { + newLocalEntityID = m_World->CreateEntity(m_ServerIDToClientID.at(serverParentID)); + } + m_World->SetName(newLocalEntityID, serverEntityName); + insertIntoServerClientMaps(serverEntityID, newLocalEntityID); + m_World->AttachComponent(newLocalEntityID, componentType); + updateFields(packet, componentInfo, newLocalEntityID, componentType); } - // If the entity dosent exist nor the component - } else { - // Create Entity - // If entity dosen't exist - EntityID newEntityID = m_World->CreateEntity(); - insertIntoServerClientMaps(receivedEntityID, newEntityID); - // Check if EntityIDs are out of sync - if (newEntityID != receivedEntityID) { - LOG_INFO("Client::parseSnapshot(Packet& packet): Newly created EntityID is not the \ - same as the one sent by server (EntityIDs are out of sync)"); - } - // Create component - m_World->AttachComponent(newEntityID, componentType); - // Copy data to newly created component - updateFields(packet, componentInfo, newEntityID, componentType); } - - // Parent Logic - // Don't need to check if receivedEntityID is mapped. (It should have been set) - if (receivedParentEntityID != std::numeric_limits::max()) { - if (serverClientMapsHasEntity(receivedParentEntityID)) { - m_World->SetParent(m_ServerIDToClientID.at(receivedEntityID), m_ServerIDToClientID.at(receivedParentEntityID)); - // If Parent dosen't exist create one and map receivedParentEntityID to it. - } else { - // Create the new parent and add it to map - EntityID newParentEntityID = m_World->CreateEntity(); - insertIntoServerClientMaps(receivedParentEntityID, newParentEntityID); - // Set the newly created Entity as parent. - m_World->SetParent(m_ServerIDToClientID.at(receivedEntityID), newParentEntityID); - } + // Parent logic + // This should be enough beacause we know that the entities arives in pre-order (there will always be a parent) + if (serverParentID != EntityID_Invalid) { + EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID); + m_World->SetParent(localEntityID, m_ServerIDToClientID.at(serverParentID)); } } } -int Client::receive(char* data, size_t length) +int Client::receive(char* data) { boost::system::error_code error; int bytesReceived = m_Socket.receive_from(boost - ::asio::buffer((void*)data, length), + ::asio::buffer((void*)data, INPUTSIZE), m_ReceiverEndpoint, 0, error); - + // Network Debug data + if (isReadingData) { + m_NetworkData.TotalDataReceived += bytesReceived; + m_NetworkData.DataReceivedThisInterval += bytesReceived; + m_NetworkData.AmountOfMessagesReceived++; + } if (error) { //LOG_ERROR("receive: %s", error.message().c_str()); } @@ -232,6 +284,12 @@ void Client::send(Packet& packet) packet.Data(), packet.Size()), m_ReceiverEndpoint, 0); + // Network Debug data + if (isReadingData) { + m_NetworkData.TotalDataSent += packet.Size(); + m_NetworkData.DataSentThisInterval += packet.Size(); + m_NetworkData.AmountOfMessagesSent++; + } } void Client::connect() @@ -250,14 +308,6 @@ void Client::disconnect() send(packet); } -void Client::ping() -{ - //Packet packet(MessageType::Connect, m_SendPacketID); - //packet.WriteString("Ping"); - //m_StartPingTime = std::clock(); - //send(packet); -} - bool Client::OnInputCommand(const Events::InputCommand & e) { if (e.Command == "ConnectToServer") { // Connect for now @@ -275,6 +325,15 @@ bool Client::OnInputCommand(const Events::InputCommand & e) if (e.Value > 0) { becomePlayer(); } + } else if (e.Command == "LogNetworkBandwidth") { + if (e.Value > 0) { + // Save to file if we no longer want to read data. + if (isReadingData) { + saveToFile(); + } + isReadingData = !isReadingData; + m_SaveDataTimer = std::clock(); + } } else { m_InputCommandBuffer.push_back(e); //LOG_DEBUG("Client::OnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); @@ -285,13 +344,40 @@ bool Client::OnInputCommand(const Events::InputCommand & e) bool Client::OnPlayerDamage(const Events::PlayerDamage & e) { - Packet packet(MessageType::OnInputCommand, m_SendPacketID); - packet.WritePrimitive(e.DamageAmount); - packet.WritePrimitive(e.PlayerDamagedID); + Packet packet(MessageType::OnPlayerDamage, m_SendPacketID); + packet.WritePrimitive(e.Damage); + packet.WritePrimitive(m_ClientIDToServerID.at(e.Player.ID)); send(packet); return false; } +bool Client::OnPlayerSpawned(const Events::PlayerSpawned& e) +{ + if (e.PlayerID == -1) { + m_LocalPlayer = e.Player; + } + return true; +} + +void Client::sendLocalPlayerTransform() +{ + if (!m_LocalPlayer.Valid()) { + return; + } + + ComponentWrapper cTransform = m_LocalPlayer["Transform"]; + glm::vec3& position = cTransform["Position"]; + glm::vec3& orientation = cTransform["Orientation"]; + Packet packet(MessageType::PlayerTransform, m_SendPacketID); + packet.WritePrimitive(position.x); + packet.WritePrimitive(position.y); + packet.WritePrimitive(position.z); + packet.WritePrimitive(orientation.x); + packet.WritePrimitive(orientation.y); + packet.WritePrimitive(orientation.z); + send(packet); +} + void Client::identifyPacketLoss() { // if no packets lost, difference should be equal to 1 @@ -305,7 +391,7 @@ bool Client::hasServerTimedOut() { // Time in ms float timeSincePing = 1000 * (std::clock() - m_StartPingTime) / static_cast(CLOCKS_PER_SEC); - if (timeSincePing > TIMEOUTMS) { + if (timeSincePing > m_TimeoutMs) { // Clear everything and go to menu. LOG_INFO("Server has timed out, returning to menu, Beep Boop."); m_IsConnected = false; @@ -319,7 +405,7 @@ EntityID Client::createPlayer() EntityID entityID = m_World->CreateEntity(); ComponentWrapper transform = m_World->AttachComponent(entityID, "Transform"); ComponentWrapper model = m_World->AttachComponent(entityID, "Model"); - model["Resource"] = "Models/Core/UnitSphere.obj"; + model["Resource"] = "Models/Core/UnitSphere.mesh"; ComponentWrapper player = m_World->AttachComponent(entityID, "Player"); return entityID; } @@ -345,12 +431,26 @@ void Client::becomePlayer() bool Client::clientServerMapsHasEntity(EntityID clientEntityID) { - return m_ClientIDToServerID.find(clientEntityID) != m_ClientIDToServerID.end(); + if (m_ClientIDToServerID.find(clientEntityID) != m_ClientIDToServerID.end()) { + if (m_World->ValidEntity(clientEntityID)) { + return true; + } + EntityID serverEntityID = m_ClientIDToServerID.at(clientEntityID); + deleteFromServerClientMaps(serverEntityID, clientEntityID); + } + return false; } bool Client::serverClientMapsHasEntity(EntityID serverEntityID) { - return m_ServerIDToClientID.find(serverEntityID) != m_ServerIDToClientID.end(); + if (m_ServerIDToClientID.find(serverEntityID) != m_ServerIDToClientID.end()) { + EntityID localEntityID = m_ServerIDToClientID.at(serverEntityID); + if (m_World->ValidEntity(localEntityID)) { + return true; + } + deleteFromServerClientMaps(serverEntityID, localEntityID); + } + return false; } void Client::insertIntoServerClientMaps(EntityID serverEntityID, EntityID clientEntityID) @@ -359,3 +459,9 @@ void Client::insertIntoServerClientMaps(EntityID serverEntityID, EntityID client m_ClientIDToServerID.insert(std::make_pair(clientEntityID, serverEntityID)); } + +void Client::deleteFromServerClientMaps(EntityID serverEntityID, EntityID clientEntityID) +{ + m_ServerIDToClientID.erase(serverEntityID); + m_ClientIDToServerID.erase(clientEntityID); +} diff --git a/src/Engine/Network/Network.cpp b/src/Engine/Network/Network.cpp new file mode 100644 index 00000000..f4dcd1a2 --- /dev/null +++ b/src/Engine/Network/Network.cpp @@ -0,0 +1,68 @@ +#include "Network/Network.h" + +void Network::Update() +{ + updateNetworkData(); +} + +void Network::saveToFile() +{ + std::ofstream outfile; + time_t t = time(0); + // get time now + struct tm * now = localtime(&t); + // Get current time and date + std::string dateAndTime = "BandwidthData - " + std::to_string(now->tm_year + 1900) + '-' + + std::to_string(now->tm_mon + 1) + '-' + + std::to_string(now->tm_mday) + '_' + + std::to_string(now->tm_hour) + "h." + + std::to_string(now->tm_min) + "m." + + std::to_string(now->tm_sec) + 's'; + + outfile.open(dateAndTime + ".csv"); + outfile << "Total time," + std::to_string(m_NetworkData.TotalTime) + "\n"; + outfile << "Total data received," + std::to_string(m_NetworkData.TotalDataReceived) + "\n"; + outfile << "Total data sent," + std::to_string(m_NetworkData.TotalDataSent) + "\n"; + outfile << "Total messages received," + std::to_string(m_NetworkData.AmountOfMessagesReceived) + "\n"; + outfile << "Total messages sent," + std::to_string(m_NetworkData.AmountOfMessagesSent) + "\n"; + + float messagesReceivedPerSec = (float)m_NetworkData.AmountOfMessagesReceived / (m_NetworkData.TotalTime / 1000); + float messagesSentPerSec = (float)m_NetworkData.AmountOfMessagesSent / (m_NetworkData.TotalTime / 1000); + float dataReceivedPerSec = (float)m_NetworkData.TotalDataReceived / (m_NetworkData.TotalTime / 1000); + float dataSentPerSec = (float)m_NetworkData.TotalDataSent / (m_NetworkData.TotalTime / 1000); + outfile << "Avarage messages received / s: " + std::to_string(messagesReceivedPerSec) + "\n"; + outfile << "Avarage messages sents / s: " + std::to_string(messagesSentPerSec) + "\n"; + outfile << "Avarage data received B/s: " + std::to_string(dataReceivedPerSec) + "\n"; + outfile << "Avarage data sents B/s: " + std::to_string(dataSentPerSec) + "\n"; + + outfile << "time, avg receive B, avg send B\n"; + for (int i = 0; i < m_NetworkData.BandwidthBytes.size(); i++) { + outfile << std::to_string(i) + ","; + outfile << std::to_string(m_NetworkData.BandwidthBytes[i].first) + ","; + outfile << std::to_string(m_NetworkData.BandwidthBytes[i].second) + "\n"; + } + outfile.close(); + +} + +void Network::updateNetworkData() +{ + std::clock_t currentTime = std::clock(); + // Send snapshot + if (m_SaveDataIntervalMs < (1000 * (currentTime - m_SaveDataTimer) / (double)CLOCKS_PER_SEC)) { + // Set values + m_NetworkData.TotalTime += (1000 * (currentTime - m_SaveDataTimer) / (double)CLOCKS_PER_SEC); + m_NetworkData.BandwidthBytes.push_back(std::pair(m_NetworkData.DataReceivedThisInterval, m_NetworkData.DataSentThisInterval)); + // Reset interval stuff + m_SaveDataTimer = std::clock(); + m_NetworkData.DataSentThisInterval = 0; + m_NetworkData.DataReceivedThisInterval = 0; + } +} + +void Network::initialize() +{ + ConfigFile* config = ResourceManager::Load("Config.ini"); + m_MaxConnections = config->Get("Networking.MaxConnections", 8); + m_TimeoutMs = config->Get("Networking.TimeoutMs", 20000); +} diff --git a/src/Engine/Network/Packet.cpp b/src/Engine/Network/Packet.cpp index 6308a130..d40a1b32 100644 --- a/src/Engine/Network/Packet.cpp +++ b/src/Engine/Network/Packet.cpp @@ -39,6 +39,7 @@ void Packet::Init(MessageType type, unsigned int & packetID) Packet::WritePrimitive(messageType); Packet::WritePrimitive(packetID); packetID++; + m_HeaderSize = m_Offset; } void Packet::WriteString(const std::string& str) diff --git a/src/Engine/Network/Server.cpp b/src/Engine/Network/Server.cpp index f4373a83..adf810aa 100644 --- a/src/Engine/Network/Server.cpp +++ b/src/Engine/Network/Server.cpp @@ -1,7 +1,13 @@ #include "Network/Server.h" -Server::Server() : m_Socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 13)) -{ } +Server::Server() : m_Socket(m_IOService, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 27666)) +{ + Network::initialize(); + ConfigFile* config = ResourceManager::Load("Config.ini"); + snapshotInterval = 1000 * config->Get("Networking.SnapshotInterval", 0.05); + pingIntervalMs = config->Get("Networking.PingIntervalMs", 1000); + +} Server::~Server() { @@ -14,9 +20,9 @@ void Server::Start(World* world, EventBroker* eventBroker) m_EventBroker = eventBroker; // Subscribe to events EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &Server::OnInputCommand); - for (size_t i = 0; i < MAXCONNECTIONS; i++) { - m_PlayerDefinitions[i].StopTime = std::clock(); - } + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &Server::OnPlayerSpawned); + EVENT_SUBSCRIBE_MEMBER(m_EEntityDeleted, &Server::OnEntityDeleted); + EVENT_SUBSCRIBE_MEMBER(m_EComponentDeleted, &Server::OnComponentDeleted); LOG_INFO("I am Server. BIP BOP\n"); } @@ -24,14 +30,17 @@ void Server::Update() { readFromClients(); m_EventBroker->Process(); -} + if (isReadingData) { + Network::Update(); + } +} void Server::readFromClients() { while (m_Socket.available()) { try { - bytesRead = receive(readBuffer, INPUTSIZE); + bytesRead = receive(readBuffer); Packet packet(readBuffer, bytesRead); parseMessageType(packet); } catch (const std::exception& err) { @@ -46,7 +55,7 @@ void Server::readFromClients() } // Send pings each - if (intervalMs < (1000 * (currentTime - previousePingMessage) / (double)CLOCKS_PER_SEC)) { + if (pingIntervalMs < (1000 * (currentTime - previousePingMessage) / (double)CLOCKS_PER_SEC)) { sendPing(); previousePingMessage = currentTime; } @@ -70,11 +79,8 @@ void Server::parseMessageType(Packet& packet) case MessageType::Connect: parseConnect(packet); break; - case MessageType::ClientPing: - //parseClientPing(); - break; - case MessageType::ServerPing: - parseServerPing(); + case MessageType::Ping: + parsePing(); break; case MessageType::Message: break; @@ -89,29 +95,46 @@ void Server::parseMessageType(Packet& packet) case MessageType::OnPlayerDamage: parseOnPlayerDamage(packet); break; - case MessageType::BecomePlayer: - createPlayer(); + case MessageType::PlayerTransform: + parsePlayerTransform(packet); break; default: break; } } -int Server::receive(char * data, size_t length) +int Server::receive(char * data) { - length = m_Socket.receive_from( + unsigned int length = m_Socket.receive_from( boost::asio::buffer((void*)data - , length) + , INPUTSIZE) , m_ReceiverEndpoint, 0); + // Network Debug data + if (isReadingData) { + m_NetworkData.TotalDataReceived += length; + m_NetworkData.DataReceivedThisInterval += length; + m_NetworkData.AmountOfMessagesReceived++; + } return length; } -void Server::send(Packet& packet, int userID) +void Server::send(PlayerID player, Packet& packet) { - int bytesSent = m_Socket.send_to( - boost::asio::buffer(packet.Data(), packet.Size()), - m_ConnectedUsers[userID].Endpoint, - 0); + try { + int bytesSent = m_Socket.send_to( + boost::asio::buffer(packet.Data(), packet.Size()), + m_ConnectedPlayers[player].Endpoint, + 0); + // Network Debug data + if (isReadingData) { + m_NetworkData.TotalDataSent += packet.Size(); + m_NetworkData.DataSentThisInterval += packet.Size(); + m_NetworkData.AmountOfMessagesSent++; + } + } catch (const boost::system::system_error& e) { + // TODO: Clean up invalid endpoints out of m_ConnectedPlayers later + m_ConnectedPlayers[player].Endpoint = boost::asio::ip::udp::endpoint(); + } } void Server::send(Packet & packet) @@ -122,59 +145,83 @@ void Server::send(Packet & packet) packet.Size()), m_ReceiverEndpoint, 0); + if (isReadingData) { + // Network Debug data + m_NetworkData.TotalDataSent += packet.Size(); + m_NetworkData.DataSentThisInterval += packet.Size(); + } } void Server::broadcast(Packet& packet) { - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() != boost::asio::ip::address()) { - packet.ChangePacketID(m_ConnectedUsers[i].PacketID); - send(packet, i); - } + for (auto& kv : m_ConnectedPlayers) { + packet.ChangePacketID(kv.second.PacketID); + send(kv.first, packet); } } // Send snapshot fields void Server::sendSnapshot() { - // Should time this + Packet packet(MessageType::Snapshot); + addChildrenToPacket(packet, EntityID_Invalid); + broadcast(packet); +} + +void Server::addChildrenToPacket(Packet & packet, EntityID entityID) +{ + auto itPair = m_World->GetChildren(entityID); std::unordered_map worldComponentPools = m_World->GetComponentPools(); - for (auto& it : worldComponentPools) { - Packet packet(MessageType::Snapshot); - ComponentPool* componentPool = it.second; - ComponentInfo componentInfo = componentPool->ComponentInfo(); - // Component Type - packet.WriteString(componentInfo.Name); - for (auto& componentWrapper : *componentPool) { - // Components EntityID - packet.WritePrimitive(componentWrapper.EntityID); - // Parents EntityID - packet.WritePrimitive(m_World->GetParent(componentWrapper.EntityID)); - for (auto& componentField : componentWrapper.Info.FieldsInOrder) { - ComponentInfo::Field_t fieldInfo = componentInfo.Fields.at(componentField); - if (fieldInfo.Type == "string") { - std::string& value = componentWrapper[componentField]; - packet.WriteString(value); - } else { - packet.WriteData(componentWrapper.Data + fieldInfo.Offset, fieldInfo.Stride); + // Loop through every child + for (auto it = itPair.first; it != itPair.second; it++) { + EntityID childEntityID = it->second; + // Write EntityID and parentsID and Entity name + packet.WritePrimitive(childEntityID); + packet.WritePrimitive(entityID); + packet.WriteString(m_World->GetName(childEntityID)); + // Write components to child + int numberOfComponents = 0; + for (auto& i : worldComponentPools) { + if (i.second->KnowsEntity(childEntityID)) { + numberOfComponents++; + } + } + // Write how many components should be read + packet.WritePrimitive(numberOfComponents); + for (auto& i : worldComponentPools) { + // If the entity exist in the pool + if (i.second->KnowsEntity(childEntityID)) { + ComponentWrapper componentWrapper = i.second->GetByEntity(childEntityID); + // ComponentType + packet.WriteString(componentWrapper.Info.Name); + // Loop through fields + for (auto& componentField : componentWrapper.Info.FieldsInOrder) { + ComponentInfo::Field_t fieldInfo = componentWrapper.Info.Fields.at(componentField); + if (fieldInfo.Type == "string") { + std::string& value = componentWrapper[componentField]; + packet.WriteString(value); + } else { + packet.WriteData(componentWrapper.Data + fieldInfo.Offset, fieldInfo.Stride); + } } } } - broadcast(packet); + // Go to to your children + addChildrenToPacket(packet, childEntityID); } } void Server::sendPing() { // Prints connected players ping - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() != boost::asio::ip::address()) { - int ping = 1000 * (m_ConnectedUsers[i].StopTime - m_StartPingTime) / static_cast(CLOCKS_PER_SEC); - LOG_INFO("Last packetID received %i: User %i's ping: %i", m_ConnectedUsers[i].PacketID, i, std::abs(ping)); - } - } + //for (int i = 0; i < m_ConnectedPlayers.size(); i++) { + // if (m_ConnectedPlayers[i].Endpoint.address() != boost::asio::ip::address()) { + // int ping = 1000 * (m_ConnectedPlayers[i].StopTime - m_StartPingTime) / static_cast(CLOCKS_PER_SEC); + // LOG_INFO("Last packetID received %i: User %i's ping: %i", m_ConnectedPlayers[i].PacketID, i, std::abs(ping)); + // } + //} // Create ping message - Packet packet(MessageType::ServerPing); + Packet packet(MessageType::Ping); packet.WriteString("Ping from server"); // Time message m_StartPingTime = std::clock(); @@ -187,11 +234,11 @@ void Server::checkForTimeOuts() int startPing = 1000 * m_StartPingTime / static_cast(CLOCKS_PER_SEC); - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() != boost::asio::ip::address()) { - int stopPing = 1000 * m_ConnectedUsers[i].StopTime / + for (int i = 0; i < m_ConnectedPlayers.size(); i++) { + if (m_ConnectedPlayers[i].Endpoint.address() != boost::asio::ip::address()) { + int stopPing = 1000 * m_ConnectedPlayers[i].StopTime / static_cast(CLOCKS_PER_SEC); - if (startPing > stopPing + TIMEOUTMS) { + if (startPing > stopPing + m_TimeoutMs) { LOG_INFO("User %i timed out!", i); disconnect(i); } @@ -199,35 +246,30 @@ void Server::checkForTimeOuts() } } -void Server::disconnect(int i) +void Server::disconnect(PlayerID playerID) { //broadcast("A player disconnected"); - LOG_INFO("User %s disconnected/timed out", m_PlayerDefinitions[i].Name.c_str()); + LOG_INFO("User %s disconnected/timed out", m_ConnectedPlayers[playerID].Name.c_str()); // Remove enteties and stuff (When we can remove entity, remove it and tell clients to remove the copy they have) - m_PlayerDefinitions[i].Endpoint = boost::asio::ip::udp::endpoint(); - m_PlayerDefinitions[i].EntityID = -1; - m_PlayerDefinitions[i].Name = ""; - m_PlayerDefinitions[i].PacketID = 0; - m_ConnectedUsers.erase(m_ConnectedUsers.begin() + i); + Events::PlayerDisconnected e; + e.Entity = m_ConnectedPlayers[playerID].EntityID; + e.PlayerID = playerID; + m_EventBroker->Publish(e); + + m_ConnectedPlayers.erase(playerID); } void Server::parseOnInputCommand(Packet& packet) { - int playerID = -1; + PlayerID player = -1; // Check which player it was who sent the message - for (int i = 0; i < MAXCONNECTIONS; i++) { - // if the player is connected set playerID to the correct PlayerID - if (m_PlayerDefinitions[i].Endpoint.address() == m_ReceiverEndpoint.address() - && m_PlayerDefinitions[i].Endpoint.port() == m_ReceiverEndpoint.port()) { - playerID = i; - break; - } - } - if (playerID != -1) { + player = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); + if (player != -1) { while (packet.DataReadSize() < packet.Size()) { Events::InputCommand e; e.Command = packet.ReadString(); - e.PlayerID = playerID; // Set correct player id + e.PlayerID = player; // Set correct player id + e.Player = EntityWrapper(m_World, m_ConnectedPlayers.at(player).EntityID); e.Value = packet.ReadPrimitive(); m_EventBroker->Publish(e); //LOG_INFO("Server::parseOnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); @@ -238,8 +280,8 @@ void Server::parseOnInputCommand(Packet& packet) void Server::parseOnPlayerDamage(Packet & packet) { Events::PlayerDamage e; - e.DamageAmount = packet.ReadPrimitive(); - e.PlayerDamagedID = packet.ReadPrimitive(); + e.Damage = packet.ReadPrimitive(); + e.Player = EntityWrapper(m_World, packet.ReadPrimitive()); m_EventBroker->Publish(e); //LOG_DEBUG("Server::parseOnPlayerDamage: Command is %s. Value is %f. PlayerID is %i.", e.DamageAmount, e.PlayerDamagedID, e.TypeOfDamage.c_str()); } @@ -251,9 +293,9 @@ void Server::parseConnect(Packet& packet) if (GetPlayerIDFromEndpoint(m_ReceiverEndpoint) != -1) { return; } - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() == m_ReceiverEndpoint.address() && - m_ConnectedUsers[i].Endpoint.port() == m_ReceiverEndpoint.port()) { + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() && + kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) { // Already connected return; } @@ -265,11 +307,11 @@ void Server::parseConnect(Packet& packet) pd.Name = packet.ReadString(); pd.PacketID = 0; pd.StopTime = std::clock(); - m_ConnectedUsers.push_back(pd); + m_ConnectedPlayers[m_NextPlayerID++] = pd; LOG_INFO("Spectator \"%s\" connected on IP: %s", pd.Name.c_str(), pd.Endpoint.address().to_string().c_str()); // Send a message to the player that connected - Packet connnectPacket(MessageType::Connect, m_ConnectedUsers[m_ConnectedUsers.size() - 1].PacketID); + Packet connnectPacket(MessageType::Connect, pd.PacketID); send(connnectPacket); // Send notification that a player has connected @@ -281,9 +323,10 @@ void Server::parseDisconnect() { LOG_INFO("%i: Parsing disconnect", m_PacketID); - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() == m_ReceiverEndpoint.address()) { - disconnect(i); + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.Endpoint.address() == m_ReceiverEndpoint.address() && + kv.second.Endpoint.port() == m_ReceiverEndpoint.port()) { + disconnect(kv.first); break; } } @@ -292,21 +335,21 @@ void Server::parseDisconnect() void Server::parseClientPing() { LOG_INFO("%i: Parsing ping", m_PacketID); - int playerID = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); - if (playerID == -1) { + PlayerID player = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); + if (player == -1) { return; } // Return ping - Packet packet(MessageType::ClientPing, m_PlayerDefinitions[playerID].PacketID); + Packet packet(MessageType::Ping, m_ConnectedPlayers[player].PacketID); packet.WriteString("Ping received"); send(packet); } -void Server::parseServerPing() +void Server::parsePing() { - for (int i = 0; i < m_ConnectedUsers.size(); i++) { - if (m_ConnectedUsers[i].Endpoint.address() == m_ReceiverEndpoint.address()) { - m_ConnectedUsers[i].StopTime = std::clock(); + for (int i = 0; i < m_ConnectedPlayers.size(); i++) { + if (m_ConnectedPlayers[i].Endpoint.address() == m_ReceiverEndpoint.address()) { + m_ConnectedPlayers[i].StopTime = std::clock(); break; } } @@ -321,49 +364,19 @@ void Server::identifyPacketLoss() } } -void Server::createPlayer() +void Server::kick(PlayerID player) { - if (GetPlayerIDFromEndpoint(m_ReceiverEndpoint) != -1) { - // Already connected as player - LOG_WARNING("Already connected!"); - return; - } - int userIndex; - for (userIndex = 0; userIndex < m_ConnectedUsers.size(); userIndex++) { - if (m_ConnectedUsers[userIndex].Endpoint.address() == m_ReceiverEndpoint.address() && - m_ConnectedUsers[userIndex].Endpoint.port() == m_ReceiverEndpoint.port()) { - // Found user - break; - } - } - if (userIndex == m_ConnectedUsers.size()) { - LOG_WARNING("Not a recognized user!"); - return; - } - for (int playerIndex = 0; playerIndex < MAXCONNECTIONS; playerIndex++) { - if (m_PlayerDefinitions[playerIndex].Endpoint.address() == boost::asio::ip::address()) { - m_PlayerDefinitions[playerIndex] = m_ConnectedUsers[userIndex]; - EntityID entityID = m_World->CreateEntity(); - ComponentWrapper transform = m_World->AttachComponent(entityID, "Transform"); - transform["Position"] = glm::vec3(-1.5f, 0.f, 0.f); - ComponentWrapper model = m_World->AttachComponent(entityID, "Model"); - model["Resource"] = "Models/Core/UnitSphere.obj"; - model["Color"] = glm::vec4(rand()%255 / 255.f, rand()%255 / 255.f, rand() %255 / 255.f, 1.f); - ComponentWrapper player = m_World->AttachComponent(entityID, "Player"); - m_PlayerDefinitions[playerIndex].EntityID = entityID; - return; - } - } - LOG_WARNING("Server is full!"); - + disconnect(player); + Packet packet = Packet(MessageType::Kick); + send(packet); } -int Server::GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint) +PlayerID Server::GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint) { - for (int i = 0; i < MAXCONNECTIONS; i++) { - if (m_PlayerDefinitions[i].Endpoint.address() == endpoint.address() && - m_PlayerDefinitions[i].Endpoint.port() == endpoint.port()) { - return i; + for (auto& kv : m_ConnectedPlayers) { + if (kv.second.Endpoint.address() == endpoint.address() && + kv.second.Endpoint.port() == endpoint.port()) { + return kv.first; } } return -1; @@ -372,5 +385,70 @@ int Server::GetPlayerIDFromEndpoint(boost::asio::ip::udp::endpoint endpoint) bool Server::OnInputCommand(const Events::InputCommand & e) { //LOG_DEBUG("Server::OnInputCommand: Command is %s. Value is %f. PlayerID is %i.", e.Command.c_str(), e.Value, e.PlayerID); + if (e.Command == "LogNetworkBandwidth" && e.Value > 0) { + if (isReadingData) { + saveToFile(); + } + isReadingData = !isReadingData; + m_SaveDataTimer = std::clock(); + } + if (e.Command == "KickPlayer" && e.Value > 0) { + kick(0); + } + return true; } + +bool Server::OnPlayerSpawned(const Events::PlayerSpawned & e) +{ + m_ConnectedPlayers[e.PlayerID].EntityID = e.Player.ID; + + Packet packet = Packet(MessageType::OnPlayerSpawned); + packet.WritePrimitive(e.Player.ID); + packet.WritePrimitive(e.Spawner.ID); + // We don't send PlayerID here because it will always be set to -1 + packet.WriteString(m_ConnectedPlayers[e.PlayerID].Name); + send(e.PlayerID, packet); + return false; +} + +bool Server::OnEntityDeleted(const Events::EntityDeleted & e) +{ + if (!e.Cascaded) { + Packet packet = Packet(MessageType::EntityDeleted); + packet.WritePrimitive(e.DeletedEntity); + broadcast(packet); + } + return false; +} + +bool Server::OnComponentDeleted(const Events::ComponentDeleted & e) +{ + if (!e.Cascaded) { + Packet packet = Packet(MessageType::ComponentDeleted); + packet.WritePrimitive(e.Entity); + packet.WriteString(e.ComponentType); + broadcast(packet); + } + return false; +} + +void Server::parsePlayerTransform(Packet& packet) +{ + glm::vec3 position; + glm::vec3 orientation; + position.x = packet.ReadPrimitive(); + position.y = packet.ReadPrimitive(); + position.z = packet.ReadPrimitive(); + orientation.x = packet.ReadPrimitive(); + orientation.y = packet.ReadPrimitive(); + orientation.z = packet.ReadPrimitive(); + + PlayerID playerID = GetPlayerIDFromEndpoint(m_ReceiverEndpoint); + EntityWrapper player(m_World, m_ConnectedPlayers.at(playerID).EntityID); + + if (player.Valid()) { + player["Transform"]["Position"] = position; + player["Transform"]["Orientation"] = orientation; + } +} diff --git a/src/Engine/Rendering/AnimationSystem.cpp b/src/Engine/Rendering/AnimationSystem.cpp new file mode 100644 index 00000000..2126362f --- /dev/null +++ b/src/Engine/Rendering/AnimationSystem.cpp @@ -0,0 +1,47 @@ +#include "Rendering/AnimationSystem.h" + +void AnimationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& animationComponent, double dt) +{ + if(!entity.HasComponent("Model")) { + return; + } + + Model* model; + try { + model = ResourceManager::Load<::Model, true>(entity["Model"]["Resource"]); + } catch (const std::exception&) { + return; + } + + + Skeleton* skeleton = model->m_RawModel->m_Skeleton; + const Skeleton::Animation* animation = skeleton->GetAnimation(animationComponent["Name"]); + + if(animation != nullptr) { + double animationSpeed = (double)animationComponent["Speed"]; + + if (animationSpeed != 0.0) { + double nextTime = (double)animationComponent["Time"] + animationSpeed * dt; + + + if (!(bool)animationComponent["Loop"] && glm::abs(nextTime) > animation->Duration) { + (double&)animationComponent["Time"] = glm::sign(nextTime) * animation->Duration; + (double&)animationComponent["Speed"] = 0.0; + Events::AnimationComplete e; + e.Entity = entity; + e.Name = (std::string)animationComponent["Name"]; + m_EventBroker->Publish(e); + } else { + if (glm::abs(nextTime) > animation->Duration) { + (double&)animationComponent["Time"] = glm::abs(nextTime) - animation->Duration; + } else { + (double&)animationComponent["Time"] = nextTime; + } + } + } + } + + + +} + diff --git a/src/Engine/Rendering/DrawBloomPass.cpp b/src/Engine/Rendering/DrawBloomPass.cpp index 1ad97eb5..8fe1d807 100644 --- a/src/Engine/Rendering/DrawBloomPass.cpp +++ b/src/Engine/Rendering/DrawBloomPass.cpp @@ -4,7 +4,7 @@ DrawBloomPass::DrawBloomPass(IRenderer* renderer) { m_Renderer = renderer; - m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.obj"); + m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.mesh"); InitializeTextures(); InitializeBuffers(); diff --git a/src/Engine/Rendering/DrawColorCorrectionPass.cpp b/src/Engine/Rendering/DrawColorCorrectionPass.cpp index c9789602..ba9efe3e 100644 --- a/src/Engine/Rendering/DrawColorCorrectionPass.cpp +++ b/src/Engine/Rendering/DrawColorCorrectionPass.cpp @@ -4,8 +4,8 @@ DrawColorCorrectionPass::DrawColorCorrectionPass(IRenderer* renderer) { m_Renderer = renderer; - m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.obj"); - m_Exposure = 1; //TODO: Renderer: Fixa så att denna går att ändra på genom komponent eller setting. + m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.mesh"); + m_Exposure = 0.4; //TODO: Renderer: Fixa så att denna går att ändra på genom komponent eller setting. InitializeShaderPrograms(); } diff --git a/src/Engine/Rendering/DrawFinalPass.cpp b/src/Engine/Rendering/DrawFinalPass.cpp index 0a91a9db..0cd0e576 100644 --- a/src/Engine/Rendering/DrawFinalPass.cpp +++ b/src/Engine/Rendering/DrawFinalPass.cpp @@ -65,6 +65,7 @@ void DrawFinalPass::Draw(RenderScene& scene) glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); glUniform2f(glGetUniformLocation(shaderHandle, "ScreenDimensions"), m_Renderer->Resolution().Width, m_Renderer->Resolution().Height); + //TODO: Render: Add code for more jobs than modeljobs. for (auto &job : scene.ForwardJobs) { auto modelJob = std::dynamic_pointer_cast(job); @@ -72,6 +73,11 @@ void DrawFinalPass::Draw(RenderScene& scene) //TODO: Kolla upp "header/include/common" shader saken så man slipper skicka in asmycket uniforms glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); glUniform4fv(glGetUniformLocation(shaderHandle, "Color"), 1, glm::value_ptr(modelJob->Color)); + glUniform4fv(glGetUniformLocation(shaderHandle, "DiffuseColor"), 1, glm::value_ptr(modelJob->DiffuseColor)); + + glUniform4fv(glGetUniformLocation(shaderHandle, "FillColor"), 1, glm::value_ptr(modelJob->FillColor)); + glUniform1f(glGetUniformLocation(shaderHandle, "FillPercentage"), modelJob->FillPercentage); + glActiveTexture(GL_TEXTURE0); if(modelJob->DiffuseTexture != nullptr) { @@ -80,22 +86,32 @@ void DrawFinalPass::Draw(RenderScene& scene) glBindTexture(GL_TEXTURE_2D, m_WhiteTexture->m_Texture); } glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, m_BlackTexture->m_Texture); - - /*if(modelJob->GlowMap != nullptr) { - glBindTexture(GL_TEXTURE_2D, modelJob->GlowMap->m_Texture); + if (modelJob->IncandescenceTexture != nullptr) { + glBindTexture(GL_TEXTURE_2D, modelJob->IncandescenceTexture->m_Texture); } else { glBindTexture(GL_TEXTURE_2D, m_BlackTexture->m_Texture); + } + + /*if(modelJob->GlowMap != nullptr) { + glBindTexture(GL_TEXTURE_2D, modelJob->GlowMap->m_Texture); + } else { + glBindTexture(GL_TEXTURE_2D, m_BlackTexture->m_Texture); }*/ + //TODO: Fixa så att modelsJobs kan spela upp olika animationer och så att den kan få in en tid istället för 1.0f - Hälsningar Johan och Andreas :) + if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) { + + if (modelJob->Animation != nullptr) { + std::vector frameBones = modelJob->Skeleton->GetFrameBones( *modelJob->Animation, modelJob->AnimationTime); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); + } + } + glBindVertexArray(modelJob->Model->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); - glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, 0, modelJob->StartIndex); - - continue; + glDrawElements(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(modelJob->StartIndex * sizeof(unsigned int))); } } - m_FinalPassFrameBuffer.Unbind(); GLERROR("DrawFinalPass::Draw: END"); delete state; } @@ -133,4 +149,4 @@ void DrawFinalPass::GenerateMipMapTexture(GLuint* texture, GLenum wrapping, glm: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); GLERROR("MipMap Texture initialization failed"); -} +} \ No newline at end of file diff --git a/src/Engine/Rendering/DrawScreenQuadPass.cpp b/src/Engine/Rendering/DrawScreenQuadPass.cpp index 4b155fc9..b17f5e29 100644 --- a/src/Engine/Rendering/DrawScreenQuadPass.cpp +++ b/src/Engine/Rendering/DrawScreenQuadPass.cpp @@ -4,7 +4,7 @@ DrawScreenQuadPass::DrawScreenQuadPass(IRenderer* renderer) { m_Renderer = renderer; - m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.obj"); + m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.mesh"); InitializeShaderPrograms(); } diff --git a/src/Engine/Rendering/Model.cpp b/src/Engine/Rendering/Model.cpp index 33539a14..cf4923a3 100644 --- a/src/Engine/Rendering/Model.cpp +++ b/src/Engine/Rendering/Model.cpp @@ -15,6 +15,9 @@ Model::Model(std::string fileName) if (!group.SpecularMapPath.empty()) { group.SpecularMap = std::shared_ptr(ResourceManager::Load(group.SpecularMapPath)); } + if (!group.IncandescenceMapPath.empty()) { + group.IncandescenceMap = std::shared_ptr(ResourceManager::Load(group.IncandescenceMapPath)); + } } // Generate GL buffers @@ -32,7 +35,7 @@ Model::Model(std::string fileName) GLERROR("GLEW: BufferFail4"); glBindBuffer(GL_ARRAY_BUFFER, buffer); - std::vector structSizes = { 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4 }; + std::vector structSizes = { 3, 3, 3, 3, 2, 4, 4 }; int stride = 0; for (int size : structSizes) { stride += size; @@ -48,10 +51,6 @@ Model::Model(std::string fileName) glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; } GLERROR("GLEW: BufferFail5"); @@ -62,10 +61,6 @@ Model::Model(std::string fileName) glEnableVertexAttribArray(4); glEnableVertexAttribArray(5); glEnableVertexAttribArray(6); - glEnableVertexAttribArray(7); - glEnableVertexAttribArray(8); - glEnableVertexAttribArray(9); - glEnableVertexAttribArray(10); GLERROR("GLEW: BufferFail5"); //CreateBuffers(); diff --git a/src/Engine/Rendering/PickingPass.cpp b/src/Engine/Rendering/PickingPass.cpp index f063b344..c7e23479 100644 --- a/src/Engine/Rendering/PickingPass.cpp +++ b/src/Engine/Rendering/PickingPass.cpp @@ -48,7 +48,7 @@ void PickingPass::Draw(RenderScene& scene) PickingPassState* state = new PickingPassState(m_PickingBuffer.GetHandle()); //TODO: Render: Add code for more jobs than modeljobs. - GLuint ShaderHandle = m_PickingProgram->GetHandle(); + GLuint shaderHandle = m_PickingProgram->GetHandle(); m_PickingProgram->Bind(); if (scene.ClearDepth) { @@ -75,22 +75,30 @@ void PickingPass::Draw(RenderScene& scene) m_EntityColors[std::make_tuple(pickInfo.Entity, pickInfo.World, pickInfo.Camera)] = glm::ivec2(pickColor[0], pickColor[1]); if (m_ColorCounter[0] > 255) { m_ColorCounter[0] = 0; - m_ColorCounter[1]++; + m_ColorCounter[1] += 5; } else { - m_ColorCounter[0]++; + m_ColorCounter[0] += 50; } } m_PickingColorsToEntity[glm::ivec2(pickColor[0], pickColor[1])] = pickInfo; - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); - glUniformMatrix4fv(glGetUniformLocation(ShaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); - glUniform2fv(glGetUniformLocation(ShaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "M"), 1, GL_FALSE, glm::value_ptr(modelJob->Matrix)); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "V"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ViewMatrix())); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "P"), 1, GL_FALSE, glm::value_ptr(scene.Camera->ProjectionMatrix())); + glUniform2fv(glGetUniformLocation(shaderHandle, "PickingColor"), 1, glm::value_ptr(glm::vec2(pickColor[0], pickColor[1]))); + + if (modelJob->Model->m_RawModel->m_Skeleton != nullptr) { + + if (modelJob->Animation != nullptr) { + std::vector frameBones = modelJob->Skeleton->GetFrameBones(*modelJob->Animation, modelJob->AnimationTime); + glUniformMatrix4fv(glGetUniformLocation(shaderHandle, "Bones"), frameBones.size(), GL_FALSE, glm::value_ptr(frameBones[0])); + } + } glBindVertexArray(modelJob->Model->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, modelJob->Model->ElementBuffer); - glDrawElementsBaseVertex(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, nullptr, modelJob->StartIndex); + glDrawElements(GL_TRIANGLES, modelJob->EndIndex - modelJob->StartIndex + 1, GL_UNSIGNED_INT, (void*)(modelJob->StartIndex * sizeof(unsigned int))); } } diff --git a/src/Engine/Rendering/RawModel.cpp b/src/Engine/Rendering/RawModelAssimp.cpp similarity index 89% rename from src/Engine/Rendering/RawModel.cpp rename to src/Engine/Rendering/RawModelAssimp.cpp index 256346f1..7bf72a22 100644 --- a/src/Engine/Rendering/RawModel.cpp +++ b/src/Engine/Rendering/RawModelAssimp.cpp @@ -1,6 +1,8 @@ -#include "Rendering/RawModel.h" +#include "Rendering/RawModelAssimp.h" -RawModel::RawModel(std::string fileName) +#ifdef USING_ASSIMP_AS_IMPORTER + +RawModelAssimp::RawModelAssimp(std::string fileName) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(fileName, aiProcess_CalcTangentSpace | aiProcess_Triangulate); @@ -38,7 +40,7 @@ RawModel::RawModel(std::string fileName) //LOG_DEBUG("Index count %i", numIndices); //LOG_DEBUG("Model has %i embedded textures", scene->mNumTextures); - + std::vector> boneInfo; std::map boneNameMapping; @@ -74,21 +76,7 @@ RawModel::RawModel(std::string fileName) auto uv = mesh->mTextureCoords[0][vertexIndex]; desc.TextureCoords = glm::vec2(uv.x, uv.y); } - - // Material diffuse color - aiColor3D diffuse; - material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse); - float opacity; - material->Get(AI_MATKEY_OPACITY, opacity); - desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, opacity); - - desc.DiffuseVertexColor = glm::vec4(diffuse.r, diffuse.g, diffuse.b, opacity); - // Material specular color - aiColor3D specular; - material->Get(AI_MATKEY_COLOR_SPECULAR, specular); - desc.SpecularVertexColor = glm::vec4(specular.r, specular.g, specular.b, 1.f); - m_Vertices.push_back(desc); } @@ -128,7 +116,7 @@ RawModel::RawModel(std::string fileName) } for (auto& vertex : m_Vertices) { vertex.Tangent = glm::normalize(vertex.Tangent); - vertex.BiTangent = glm::normalize(glm::cross(vertex.Tangent, glm::normalize(vertex.Normal))); + vertex.BiNormal = glm::normalize(glm::cross(vertex.Tangent, glm::normalize(vertex.Normal))); } // Material info @@ -201,10 +189,7 @@ RawModel::RawModel(std::string fileName) LOG_WARNING("Vertex weights (%i) greater than max weights per vertex (%i)", weights.size(), maxWeights); } for (int weightIndex = 0; weightIndex < weights.size() && weightIndex < maxWeights && weightIndex < 4; ++weightIndex) { - std::tie(desc.BoneIndices1[weightIndex], desc.BoneWeights1[weightIndex]) = weights[weightIndex]; - } - for (int weightIndex = 4; weightIndex < weights.size() && weightIndex < maxWeights && weightIndex < 8; ++weightIndex) { - std::tie(desc.BoneIndices2[weightIndex - 4], desc.BoneWeights2[weightIndex - 4]) = weights[weightIndex]; + std::tie(desc.BoneIndices[weightIndex], desc.BoneWeights[weightIndex]) = weights[weightIndex]; } } @@ -290,14 +275,14 @@ RawModel::RawModel(std::string fileName) } } -RawModel::~RawModel() +RawModelAssimp::~RawModelAssimp() { if (m_Skeleton) { delete m_Skeleton; } } -void RawModel::CreateSkeleton(std::vector> &boneInfo, std::map &boneNameMapping, aiNode* node, int parentID) +void RawModelAssimp::CreateSkeleton(std::vector> &boneInfo, std::map &boneNameMapping, aiNode* node, int parentID) { std::string nodeName = node->mName.C_Str(); @@ -317,3 +302,5 @@ void RawModel::CreateSkeleton(std::vector> &b CreateSkeleton(boneInfo, boneNameMapping, child, parentID); } } + +#endif \ No newline at end of file diff --git a/src/Engine/Rendering/RawModelCustom.cpp b/src/Engine/Rendering/RawModelCustom.cpp new file mode 100644 index 00000000..83f6e703 --- /dev/null +++ b/src/Engine/Rendering/RawModelCustom.cpp @@ -0,0 +1,381 @@ +#include "Rendering/RawModelCustom.h" + +#ifndef USING_ASSIMP_AS_IMPORTER + +RawModelCustom::RawModelCustom(std::string fileName) +{ + if(fileName.substr(fileName.find_last_of(".")).compare(".mesh") != 0) { + throw Resource::FailedLoadingException("Unknown model file format. Please use \".mesh\" files."); + } + fileName = fileName.erase(fileName.find_last_of("."), fileName.find_last_of(".") - fileName.size()); + ReadMeshFile(fileName); + ReadMaterialFile(fileName); + ReadAnimationFile(fileName); +} + +void RawModelCustom::ReadMeshFile(std::string filePath) +{ + char* fileData; + filePath += ".mesh"; + std::ifstream in(filePath.c_str(), std::ios_base::binary | std::ios_base::ate); + + if (!in.is_open()) { + throw Resource::FailedLoadingException("Open mesh file failed"); + } + unsigned int fileByteSize = in.tellg(); + in.seekg(0, std::ios_base::beg); + + fileData = new char[fileByteSize]; + in.read(fileData, fileByteSize); + in.close(); + + unsigned int offset = 0; + if (fileByteSize > 0) { + ReadMeshFileHeader(offset, fileData, fileByteSize); + ReadMesh(offset, fileData, fileByteSize); + } + delete fileData; +} + +void RawModelCustom::ReadMeshFileHeader(unsigned int& offset, char* fileData, unsigned int& fileByteSize) +{ +#ifdef BOOST_LITTLE_ENDIAN + m_Vertices.resize(*(unsigned int*)(fileData + offset)); + offset += sizeof(unsigned int); + m_Indices.resize(*(unsigned int*)(fileData + offset)); + offset += sizeof(unsigned int); +#else +#endif +} + +void RawModelCustom::ReadMesh(unsigned int& offset, char* fileData, unsigned int& fileByteSize) +{ + ReadVertices(offset, fileData, fileByteSize); + ReadIndices(offset, fileData, fileByteSize); +} + +void RawModelCustom::ReadVertices(unsigned int& offset, char* fileData, unsigned int& fileByteSize) +{ +#ifdef BOOST_LITTLE_ENDIAN + if (offset + m_Vertices.size() * sizeof(Vertex) > fileByteSize) { + throw Resource::FailedLoadingException("Reading vertices failed"); + } + + memcpy(&m_Vertices[0], fileData + offset, m_Vertices.size() * sizeof(Vertex)); + offset += m_Vertices.size() * sizeof(Vertex); +#else +#endif +} + +void RawModelCustom::ReadIndices(unsigned int& offset, char* fileData, unsigned int& fileByteSize) +{ +#ifdef BOOST_LITTLE_ENDIAN + if (offset + m_Indices.size() * sizeof(unsigned int) > fileByteSize) { + throw Resource::FailedLoadingException("Reading indices failed"); + } + + memcpy(&m_Indices[0], fileData + offset, m_Indices.size() * sizeof(unsigned int)); + offset += m_Indices.size() * sizeof(unsigned int); + +#else +#endif +} + +void RawModelCustom::ReadMaterialFile(std::string filePath) +{ + char* fileData; + filePath += ".mtrl"; + std::ifstream in(filePath.c_str(), std::ios_base::binary | std::ios_base::ate); + + if (!in.is_open()) { + throw Resource::FailedLoadingException("Open material file failed"); + } + unsigned int fileByteSize = in.tellg(); + in.seekg(0, std::ios_base::beg); + + fileData = new char[fileByteSize]; + in.read(fileData, fileByteSize); + in.close(); + + unsigned int offset = 0; + if (fileByteSize > 0) { + ReadMaterials(offset, fileData, fileByteSize); + } + delete fileData; +} + +void RawModelCustom::ReadMaterials(unsigned int& offset, char* fileData, unsigned int& fileByteSize) +{ +#ifdef BOOST_LITTLE_ENDIAN + unsigned int* numMaterials = (unsigned int*)(fileData); + MaterialGroups.reserve(*numMaterials); + offset += sizeof(unsigned int); + + for (int i = 0; i < *numMaterials; i++) { + ReadMaterialSingle(offset, fileData, fileByteSize); + } +#else +#endif +} + +void RawModelCustom::ReadMaterialSingle(unsigned int &offset, char* fileData, unsigned int& fileByteSize) +{ + MaterialGroup newMaterial; + +#ifdef BOOST_LITTLE_ENDIAN + + if (offset + sizeof(unsigned int) * 4 > fileByteSize) { + throw Resource::FailedLoadingException("Reading Material texture names length failed"); + } + + unsigned int* nameLengths = (unsigned int*)(fileData + offset); + offset += sizeof(unsigned int) * 4; + + if (offset + sizeof(float) * 11 + sizeof(unsigned int) * 2 > fileByteSize) { + throw Resource::FailedLoadingException("Reading Material specular, reflection, color and start and end index values failed"); + } + + newMaterial.SpecularExponent = *(float*)(fileData + offset); + offset += sizeof(float); + newMaterial.ReflectionFactor = *(float*)(fileData + offset); + offset += sizeof(float); + + memcpy(&newMaterial.DiffuseColor[0], fileData + offset, sizeof(float) * 3); + offset += sizeof(float) * 3; + memcpy(&newMaterial.SpecularColor[0], fileData + offset, sizeof(float) * 3); + offset += sizeof(float) * 3; + memcpy(&newMaterial.IncandescenceColor[0], fileData + offset, sizeof(float) * 3); + offset += sizeof(float) * 3; + + newMaterial.StartIndex = *(unsigned int*)(fileData + offset); + offset += sizeof(unsigned int); + newMaterial.EndIndex = *(unsigned int*)(fileData + offset); + offset += sizeof(unsigned int); + + if (nameLengths[0] > 0) { + if (offset + nameLengths[0] > fileByteSize) { + throw Resource::FailedLoadingException("Reading Material texture path failed"); + } + + newMaterial.TexturePath = "Textures/"; + newMaterial.TexturePath += (fileData + offset); + newMaterial.TexturePath += ".png"; + offset += nameLengths[0]; + } + + if (nameLengths[1] > 0) { + if (offset + nameLengths[1] > fileByteSize) { + throw Resource::FailedLoadingException("Reading Material NormalMap path failed"); + } + newMaterial.NormalMapPath = "Textures/"; + newMaterial.NormalMapPath += (fileData + offset); + newMaterial.NormalMapPath += ".png"; + offset += nameLengths[1]; + } + + if (nameLengths[2] > 0) { + if (offset + nameLengths[2] > fileByteSize) { + throw Resource::FailedLoadingException("Reading Material SpecularMap path failed"); + } + newMaterial.SpecularMapPath = "Textures/"; + newMaterial.SpecularMapPath += (fileData + offset); + newMaterial.SpecularMapPath += ".png"; + offset += nameLengths[2]; + } + + if (nameLengths[3] > 0) { + if (offset + nameLengths[3] > fileByteSize) { + throw Resource::FailedLoadingException("Reading Material IncandescenceMap path failed"); + } + newMaterial.IncandescenceMapPath = "Textures/"; + newMaterial.IncandescenceMapPath += (fileData + offset); + newMaterial.IncandescenceMapPath += ".png"; + offset += nameLengths[3]; + } + +#else +#endif + + MaterialGroups.push_back(newMaterial); +} + +void RawModelCustom::ReadAnimationFile(std::string filePath) +{ + char* fileData; + filePath += ".anim"; + std::ifstream in(filePath.c_str(), std::ios_base::binary | std::ios_base::ate); + + if (!in.is_open()) { + //throw Resource::FailedLoadingException("Open animation file failed"); + return; + } + + unsigned int fileByteSize = in.tellg(); + in.seekg(0, std::ios_base::beg); + + fileData = new char[fileByteSize]; + in.read(fileData, fileByteSize); + in.close(); + + unsigned int offset = 0; + if (fileByteSize > 0) { + m_Skeleton = new Skeleton(); + +#ifdef BOOST_LITTLE_ENDIAN + unsigned int numBindPoses = *(unsigned int*)(fileData); + offset += sizeof(unsigned int); + unsigned int numAnimations = *(unsigned int*)(fileData + offset); + offset += sizeof(unsigned int); +#else +#endif + + ReadAnimationBindPoses(offset, fileData, fileByteSize); + ReadAnimationClips(offset, fileData, fileByteSize, numAnimations); + } + delete fileData; +} + +void RawModelCustom::ReadAnimationBindPoses(unsigned int &offset, char* fileData, unsigned int& fileByteSize) +{ +#ifdef BOOST_LITTLE_ENDIAN + unsigned int* numBones = (unsigned int*)(fileData + offset); + offset += sizeof(unsigned int); + + for (unsigned int i = 0; i < *numBones; i++) { + ReadAnimationJoint(offset, fileData, fileByteSize); + } +#else +#endif +} + +void RawModelCustom::ReadAnimationJoint(unsigned int &offset, char* fileData, unsigned int& fileByteSize) +{ +#ifdef BOOST_LITTLE_ENDIAN + if (offset + sizeof(unsigned int) > fileByteSize) { + throw Resource::FailedLoadingException("Reading Joint name length failed"); + } + unsigned int jointNameLength = *(unsigned int*)(fileData + offset); + offset += sizeof(unsigned int); + + if (offset + jointNameLength > fileByteSize) { + throw Resource::FailedLoadingException("Reading Joint name failed"); + } + std::string jointName = (fileData + offset); + offset += jointNameLength; + + if (offset + sizeof(float) * 4 * 4 > fileByteSize) { + throw Resource::FailedLoadingException("Reading Joint offset matrix failed"); + } + glm::mat4 offsetMatrix; + memcpy(&offsetMatrix, fileData + offset, sizeof(float) * 4 * 4); + offset += sizeof(float) * 4 * 4; + + if (offset + sizeof(int) > fileByteSize) { + throw Resource::FailedLoadingException("Reading Joint ID failed"); + } + int jointID = *(int*)(fileData + offset); + offset += sizeof(int); + + if (offset + sizeof(int) > fileByteSize) { + throw Resource::FailedLoadingException("Reading Joint Parent ID failed"); + } + int jointParentID = *(int*)(fileData + offset); + offset += sizeof(int); + + // Adding joint to the Skeleton + m_Skeleton->CreateBone(jointID, jointParentID, jointName, offsetMatrix); + + +#else +#endif +} + +void RawModelCustom::ReadAnimationClips(unsigned int &offset, char* fileData, unsigned int& fileByteSize, unsigned int numberOfClips) +{ + for (unsigned int i = 0; i < numberOfClips; i++) { + ReadAnimationClipSingle(offset, fileData, fileByteSize, i); + } +} + +void RawModelCustom::ReadAnimationClipSingle(unsigned int &offset, char* fileData, unsigned int& fileByteSize, unsigned int clipIndex) +{ +#ifdef BOOST_LITTLE_ENDIAN + Skeleton::Animation newAnimation; + + if (offset + sizeof(unsigned int) > fileByteSize) { + throw Resource::FailedLoadingException("Reading AnimationClip name length failed"); + } + unsigned int clipNameLength = *(unsigned int*)(fileData + offset); + offset += sizeof(unsigned int); + + if (offset + clipNameLength > fileByteSize) { + throw Resource::FailedLoadingException("Reading AnimationClip name failed"); + } + newAnimation.Name = (fileData + offset); + offset += clipNameLength; + + if (offset + sizeof(float) > fileByteSize) { + throw Resource::FailedLoadingException("Reading AnimationClip duration failed"); + } + + newAnimation.Duration = *(float*)(fileData + offset); + offset += sizeof(float); + + if (offset + sizeof(unsigned int) > fileByteSize) { + throw Resource::FailedLoadingException("Reading AnimationClip NrOfKeyframes failed"); + } + unsigned int nrOfKeyframes = *(unsigned int*)(fileData + offset); + offset += sizeof(unsigned int); + + if (offset + sizeof(unsigned int) > fileByteSize) { + throw Resource::FailedLoadingException("Reading AnimationClip NrOfJoints failed"); + } + unsigned int nrOfJoints = *(unsigned int*)(fileData + offset); + offset += sizeof(unsigned int); + + newAnimation.Keyframes.reserve(nrOfKeyframes); + for (unsigned int i = 0; i < nrOfKeyframes; i++) { + ReadAnimationKeyFrame(offset, fileData, fileByteSize, nrOfJoints, newAnimation); + } + m_Skeleton->Animations[newAnimation.Name] = newAnimation; +#else +#endif +} + +void RawModelCustom::ReadAnimationKeyFrame(unsigned int &offset, char* fileData, unsigned int& fileByteSize, unsigned int nrOfJoints, Skeleton::Animation& animation) +{ + Skeleton::Animation::Keyframe newKeyFrame; + + if (offset + sizeof(int) > fileByteSize) { + throw Resource::FailedLoadingException("Reading AnimationKeyFrame index failed"); + } + newKeyFrame.Index = *(int*)(fileData + offset); + offset += sizeof(int); + + if (offset + sizeof(float) > fileByteSize) { + throw Resource::FailedLoadingException("Reading AnimationKeyFrame time failed"); + } + newKeyFrame.Time = *(float*)(fileData + offset); + offset += sizeof(float); + + if (offset + sizeof(Skeleton::Animation::Keyframe::BoneProperty) * nrOfJoints> fileByteSize) { + throw Resource::FailedLoadingException("Reading AnimationKeyFrame joints failed"); + } + + Skeleton::Animation::Keyframe::BoneProperty newBone; + for (unsigned int i = 0; i < nrOfJoints; i++) { + memcpy(&newBone, (fileData + offset), sizeof(Skeleton::Animation::Keyframe::BoneProperty)); + offset += sizeof(Skeleton::Animation::Keyframe::BoneProperty); + newKeyFrame.BoneProperties[newBone.ID] = newBone; + } + animation.Keyframes.push_back(newKeyFrame); +} + +RawModelCustom::~RawModelCustom() +{ + if (m_Skeleton != nullptr) { + delete m_Skeleton; + } +} + +#endif \ No newline at end of file diff --git a/src/Engine/Rendering/RenderSystem.cpp b/src/Engine/Rendering/RenderSystem.cpp index cec07198..a67f2c75 100644 --- a/src/Engine/Rendering/RenderSystem.cpp +++ b/src/Engine/Rendering/RenderSystem.cpp @@ -4,9 +4,11 @@ RenderSystem::RenderSystem(World* world, EventBroker* eventBroker, const IRender : System(world, eventBroker) , m_Renderer(renderer) , m_RenderFrame(renderFrame) + , m_World(world) { EVENT_SUBSCRIBE_MEMBER(m_ESetCamera, &RenderSystem::OnSetCamera); EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &RenderSystem::OnInputCommand); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &RenderSystem::OnPlayerSpawned); m_Camera = new Camera((float)m_Renderer->Resolution().Width / m_Renderer->Resolution().Height, glm::radians(45.f), 0.01f, 5000.f); } @@ -46,28 +48,61 @@ void RenderSystem::fillModels(std::list>& jobs) continue; } + EntityWrapper entity(m_World, modelComponent.EntityID); + + // Don't render the local player + if (entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer)) { + if (!entity.HasComponent("HealthHUD") && entity.Name() != "Crosshair" && entity.Name() != "Weapon") { //Should work but needs to be fixed. Should only render the things "childed" to the local player camera + continue; + } + } + + if ((entity.Name() == "Weapon" || entity.HasComponent("HealthHUD")) && !entity.IsChildOf(m_LocalPlayer)) { + continue; + } + Model* model; try { model = ResourceManager::Load<::Model, true>(resource); } catch (const Resource::StillLoadingException&) { //continue; - model = ResourceManager::Load<::Model>("Models/Core/UnitRaptor.obj"); + model = ResourceManager::Load<::Model>("Models/Core/UnitRaptor.mesh"); } catch (const std::exception&) { try { - model = ResourceManager::Load<::Model>("Models/Core/Error.obj"); + model = ResourceManager::Load<::Model>("Models/Core/Error.mesh"); } catch (const std::exception&) { continue; } } + + float fillPercentage = 0.f; + glm::vec4 fillColor = glm::vec4(0); + + if(m_World->HasComponent(modelComponent.EntityID, "Fill")) { + auto fillComponent = m_World->GetComponent(modelComponent.EntityID, "Fill"); + fillPercentage = (float)(double)fillComponent["Percentage"]; + fillColor = (glm::vec4)fillComponent["Color"]; + } + + + glm::mat4 modelMatrix = Transform::ModelMatrix(modelComponent.EntityID, m_World); for (auto matGroup : model->MaterialGroups()) { - std::shared_ptr modelJob = std::shared_ptr(new ModelJob(model, m_Camera, modelMatrix, matGroup, modelComponent, m_World)); + std::shared_ptr modelJob = std::shared_ptr(new ModelJob(model, m_Camera, modelMatrix, matGroup, modelComponent, m_World, fillColor, fillPercentage)); jobs.push_back(modelJob); } } } +bool RenderSystem::OnPlayerSpawned(Events::PlayerSpawned& e) +{ + if (e.PlayerID == -1) { + m_LocalPlayer = e.Player; + } + return true; +} + void RenderSystem::fillPointLights(std::list>& jobs, World* world) { auto pointLights = m_World->GetComponents("PointLight"); @@ -140,8 +175,8 @@ void RenderSystem::fillText(std::list>& jobs, World* } glm::mat4 modelMatrix = Transform::ModelMatrix(textComponent.EntityID, world); - std::shared_ptr modelJob = std::shared_ptr(new TextJob(modelMatrix, font, textComponent)); - jobs.push_back(modelJob); + std::shared_ptr textJob = std::shared_ptr(new TextJob(modelMatrix, font, textComponent)); + jobs.push_back(textJob); } } @@ -155,12 +190,12 @@ void RenderSystem::Update(double dt) { m_EventBroker->Process(); - if (m_CurrentCamera) { - ComponentWrapper cameraTransform = m_CurrentCamera["Transform"]; - m_Camera->SetPosition(cameraTransform["Position"]); - m_Camera->SetOrientation(glm::quat((const glm::vec3&)cameraTransform["Orientation"])); + // Update the current camera used for rendering + if (m_CurrentCamera.Valid()) { + m_Camera->SetPosition(Transform::AbsolutePosition(m_CurrentCamera)); + m_Camera->SetOrientation(Transform::AbsoluteOrientation(m_CurrentCamera)); } - //Only supports opaque geometry atm + RenderScene scene; scene.Camera = m_Camera; diff --git a/src/Engine/Rendering/Renderer.cpp b/src/Engine/Rendering/Renderer.cpp index 770baabf..4f687a99 100644 --- a/src/Engine/Rendering/Renderer.cpp +++ b/src/Engine/Rendering/Renderer.cpp @@ -13,9 +13,9 @@ void Renderer::Initialize() m_TextPass->Initialize(); - m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.obj"); + /* m_ScreenQuad = ResourceManager::Load("Models/Core/ScreenQuad.obj"); m_UnitQuad = ResourceManager::Load("Models/Core/UnitQuad.obj"); - m_UnitSphere = ResourceManager::Load("Models/Core/UnitSphere.obj"); + m_UnitSphere = ResourceManager::Load("Models/Core/UnitSphere.obj");*/ m_ImGuiRenderPass = new ImGuiRenderPass(this, m_EventBroker); } diff --git a/src/Engine/Rendering/Skeleton.cpp b/src/Engine/Rendering/Skeleton.cpp index 44276308..cebabb67 100644 --- a/src/Engine/Rendering/Skeleton.cpp +++ b/src/Engine/Rendering/Skeleton.cpp @@ -68,7 +68,7 @@ std::vector Skeleton::GetFrameBones(const Animation& animation, doubl void Skeleton::AccumulateBoneTransforms(bool noRootMotion, const Animation::Keyframe ¤tFrame, const Animation::Keyframe &nextFrame, float progress, std::map &boneMatrices, const Bone* bone, glm::mat4 parentMatrix) { - glm::mat4 boneMatrix; + glm::mat4 boneMatrix; if (currentFrame.BoneProperties.find(bone->ID) != currentFrame.BoneProperties.end() || nextFrame.BoneProperties.find(bone->ID) != nextFrame.BoneProperties.end()) { Animation::Keyframe::BoneProperty currentBoneProperty = currentFrame.BoneProperties.at(bone->ID); @@ -84,10 +84,13 @@ void Skeleton::AccumulateBoneTransforms(bool noRootMotion, const Animation::Keyf positionInterp.z = 0; } + boneMatrix = parentMatrix * (glm::translate(positionInterp) * glm::toMat4(rotationInterp) * glm::scale(scaleInterp)); boneMatrices[bone->ID] = boneMatrix * bone->OffsetMatrix; } else { - boneMatrix = parentMatrix * bone->Parent->OffsetMatrix; // * glm::inverse(bone->OffsetMatrix); + if (bone->Parent) { + boneMatrix = parentMatrix; // * glm::inverse(bone->OffsetMatrix); + } boneMatrices[bone->ID] = boneMatrix; // * bone->OffsetMatrix; } @@ -95,6 +98,7 @@ void Skeleton::AccumulateBoneTransforms(bool noRootMotion, const Animation::Keyf std::string name = child->Name; AccumulateBoneTransforms(noRootMotion, currentFrame, nextFrame, progress, boneMatrices, child, boneMatrix); } + } int Skeleton::GetBoneID(std::string name) diff --git a/src/Engine/Sound/SoundSystem.cpp b/src/Engine/Sound/SoundSystem.cpp index 1292e0b1..a55c1fee 100644 --- a/src/Engine/Sound/SoundSystem.cpp +++ b/src/Engine/Sound/SoundSystem.cpp @@ -198,7 +198,7 @@ bool SoundSystem::OnPlaySoundOnPosition(const Events::PlaySoundOnPosition & e) (float&)(double)emitter["RollOffFactor"] = e.RollOffFactor; (float&)(double)emitter["ReferenceDistance"] = e.ReferenceDistance; auto model = m_World->AttachComponent(emitterID, "Model"); - (std::string&)model["Resource"] = "Models/Core/UnitCube.obj"; + (std::string&)model["Resource"] = "Models/Core/UnitCube.mesh"; // 360NoScope UnitCube source->Type = SoundType::SFX; m_Sources[emitterID] = source; playSound(source); diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index 384c4a5b..903b270f 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -10,6 +10,9 @@ #include "Core/EntityFileWriter.h" #include "Game/Systems/CapturePointSystem.h" #include "Game/Systems/WeaponSystem.h" +#include "Game/Systems/PlayerHUD.h" +#include "Game/Systems/LifetimeSystem.h" +#include "../Engine/Rendering/AnimationSystem.h" Game::Game(int argc, char* argv[]) { @@ -57,7 +60,7 @@ Game::Game(int argc, char* argv[]) m_FrameStack->Height = m_Renderer->Resolution().Height; // Create a world - m_World = new World(); + m_World = new World(m_EventBroker); std::string mapToLoad = m_Config->Get("Debug.LoadMap", ""); if (!mapToLoad.empty()) { auto file = ResourceManager::Load(mapToLoad); @@ -83,9 +86,13 @@ Game::Game(int argc, char* argv[]) m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel); m_SystemPipeline->AddSystem(updateOrderLevel, m_Renderer); + m_SystemPipeline->AddSystem(updateOrderLevel); // Populate Octree with collidables ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision); + m_SystemPipeline->AddSystem(updateOrderLevel); + m_SystemPipeline->AddSystem(updateOrderLevel); + // Collision and TriggerSystem should update after player. ++updateOrderLevel; m_SystemPipeline->AddSystem(updateOrderLevel, m_OctreeCollision); diff --git a/src/Game/Systems/HealthSystem.cpp b/src/Game/Systems/HealthSystem.cpp index 5e5be33e..9e118070 100644 --- a/src/Game/Systems/HealthSystem.cpp +++ b/src/Game/Systems/HealthSystem.cpp @@ -12,7 +12,6 @@ HealthSystem::HealthSystem(World* m_World, EventBroker* eventBroker) void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) { //if entityID of health is 9 then the players ID is also 9 (player,health are connected to the same entity) - ComponentWrapper player = m_World->GetComponent(component.EntityID, "Player"); double maxHealth = (double)component["MaxHealth"]; //process the DeltaHealthVector and change the entitys health accordingly @@ -46,10 +45,16 @@ void HealthSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& comp } } -bool HealthSystem::OnPlayerDamaged(const Events::PlayerDamage& e) +bool HealthSystem::OnPlayerDamaged(Events::PlayerDamage& e) { - //save the changed HP to a vector. it will be taken care of in UpdateComponent - m_DeltaHealthVector.push_back(std::make_tuple(e.PlayerDamagedID, -e.DamageAmount)); + ComponentWrapper cHealth = e.Player["Health"]; + double& health = cHealth["Health"]; + health -= e.Damage; + + if (health <= 0.0) { + m_World->DeleteEntity(e.Player.ID); + } + return true; } diff --git a/src/Game/Systems/InterpolationSystem.cpp b/src/Game/Systems/InterpolationSystem.cpp index 75f68678..bfb6952a 100644 --- a/src/Game/Systems/InterpolationSystem.cpp +++ b/src/Game/Systems/InterpolationSystem.cpp @@ -1,35 +1,30 @@ #include "Systems/InterpolationSystem.h" -//void InterpolationSystem::UpdateComponent(World * world, ComponentWrapper & transform, double dt) -//{ -// if (m_InterpolationPoints[transform.EntityID].size() > 0) { -// Transform& sTransform = m_InterpolationPoints[transform.EntityID].front(); -// sTransform.interpolationTime += dt; -// if (sTransform.interpolationTime > 0.05) { -// double time = std::fmod(sTransform.interpolationTime, 0.05f); -// m_InterpolationPoints[transform.EntityID].pop(); -// if (m_InterpolationPoints[transform.EntityID].size() <= 0) { -// return; -// } -// sTransform = m_InterpolationPoints[transform.EntityID].front(); -// sTransform.interpolationTime = time; -// } -// glm::vec3 nextPosition = sTransform.Position; -// glm::vec3 currentPosition = static_cast(transform["Position"]); -// transform["Position"] = vectorInterpolation(currentPosition, nextPosition, sTransform.interpolationTime); -// } -//} +InterpolationSystem::InterpolationSystem(World* world, EventBroker* eventBroker) + : System(world, eventBroker) + , PureSystem("Transform") +{ + ConfigFile* config = ResourceManager::Load("Config.ini"); + m_SnapshotInterval = config->Get("Networking.SnapshotInterval", 0.05); + EVENT_SUBSCRIBE_MEMBER(m_EInterpolate, &InterpolationSystem::OnInterpolate); + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &InterpolationSystem::OnPlayerSpawned); +} void InterpolationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& transform, double dt) { + // Don't interpolate entities that might already have been removed + if (!entity.Valid()) { + return; + } + if (m_NextTransform.find(transform.EntityID) != m_NextTransform.end()) { // Exists in map m_NextTransform[transform.EntityID].interpolationTime += dt; Transform sTransform = m_NextTransform[transform.EntityID]; double time = sTransform.interpolationTime; - if (time > SNAPSHOTINTERVAL) { + if (time > m_SnapshotInterval) { if (m_LastReceivedTransform.find(transform.EntityID) != m_LastReceivedTransform.end()) { m_NextTransform[transform.EntityID] = m_LastReceivedTransform[transform.EntityID]; - m_NextTransform[transform.EntityID].interpolationTime = time - SNAPSHOTINTERVAL; + m_NextTransform[transform.EntityID].interpolationTime = time - m_SnapshotInterval; sTransform = m_NextTransform[transform.EntityID]; m_LastReceivedTransform.erase(transform.EntityID); } else { @@ -37,14 +32,21 @@ void InterpolationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrappe } } if (transform.Info.Name == "Transform") { + bool isLocalPlayer = entity == m_LocalPlayer || entity.IsChildOf(m_LocalPlayer); // Position glm::vec3 nextPosition = sTransform.Position; glm::vec3 currentPosition = static_cast(transform["Position"]); - (glm::vec3&)transform["Position"] += vectorInterpolation(currentPosition, nextPosition, sTransform.interpolationTime); + // HACK: Don't force position for players + if (!isLocalPlayer) { + (glm::vec3&)transform["Position"] += vectorInterpolation(currentPosition, nextPosition, sTransform.interpolationTime); + } // Orientation - glm::quat nextOrientation = sTransform.Orientation; - glm::quat currentOrientation = glm::quat(static_cast(transform["Orientation"])); - (glm::vec3&)transform["Orientation"] = glm::eulerAngles(glm::slerp(currentOrientation, nextOrientation, sTransform.interpolationTime / SNAPSHOTINTERVAL)); + // Don't force orientation for players + if (!isLocalPlayer) { + glm::quat nextOrientation = sTransform.Orientation; + glm::quat currentOrientation = glm::quat(static_cast(transform["Orientation"])); + (glm::vec3&)transform["Orientation"] = glm::eulerAngles(glm::slerp(currentOrientation, nextOrientation, sTransform.interpolationTime / m_SnapshotInterval)); + } // Scale glm::vec3 nextScale = sTransform.Scale; glm::vec3 currentScale = static_cast(transform["Scale"]); @@ -53,6 +55,12 @@ void InterpolationSystem::UpdateComponent(EntityWrapper& entity, ComponentWrappe } } +bool InterpolationSystem::OnPlayerSpawned(Events::PlayerSpawned& e) +{ + m_LocalPlayer = e.Player; + return true; +} + bool InterpolationSystem::OnInterpolate(const Events::Interpolate & e) { Transform transform; @@ -72,15 +80,5 @@ bool InterpolationSystem::OnInterpolate(const Events::Interpolate & e) } else { // Did not m_NextTransform[e.Entity] = transform; } - // Check if queue already exists - //if (m_InterpolationPoints.find(e.Entity) != m_InterpolationPoints.end()) { // Did exist, push to queue - // m_InterpolationPoints[e.Entity].push(transform); - //} - - //else { // Did not exist, create queue - // std::queue transformQueue; - // transformQueue.push(transform); - // m_InterpolationPoints[e.Entity] = transformQueue; - //} return false; } diff --git a/src/Game/Systems/LifetimeSystem.cpp b/src/Game/Systems/LifetimeSystem.cpp new file mode 100644 index 00000000..db19e88a --- /dev/null +++ b/src/Game/Systems/LifetimeSystem.cpp @@ -0,0 +1,27 @@ +#include "Systems/LifetimeSystem.h" + +void LifetimeSystem::Update(double dt) +{ + for (auto& e : m_Deletions) { + m_World->DeleteEntity(e.ID); + } + m_Deletions.clear(); +} + +void LifetimeSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& cLifetime, double dt) +{ + if (dt == 0.0) { + return; + } + + if (!entity.Valid()) { + return; + } + + double& lifetime = cLifetime["Lifetime"]; + lifetime -= dt; + + if (lifetime <= 0.0) { + m_Deletions.push_back(entity); + } +} \ No newline at end of file diff --git a/src/Game/Systems/PlayerHUD.cpp b/src/Game/Systems/PlayerHUD.cpp new file mode 100644 index 00000000..55d0c3f1 --- /dev/null +++ b/src/Game/Systems/PlayerHUD.cpp @@ -0,0 +1,57 @@ +#include "Game/Systems/PlayerHUD.h" + +PlayerHUD::PlayerHUD(World* world, EventBroker* eventBrokerer) + :System(world, eventBrokerer) + , m_World(world) + , m_EventBroker(eventBrokerer) +{ + + +} + +PlayerHUD::~PlayerHUD() +{ + + +} + +void PlayerHUD::Update(double dt) +{ + auto healthHUDs = m_World->GetComponents("HealthHUD"); + if (healthHUDs == nullptr) { + return; + } + + for (auto& healthHUDComponent : *healthHUDs) { + EntityWrapper entity = EntityWrapper(m_World, healthHUDComponent.EntityID); + + EntityWrapper entityIDParent = entity; + while (entityIDParent.Parent().Valid()) { + entityIDParent = entityIDParent.Parent(); + + if (entityIDParent.HasComponent("Health")) { + break; + } + } + + if (entityIDParent.HasComponent("Health")) { + + if (entity.HasComponent("Text")) { + std::string s = ""; + s = s + std::to_string((int)(double)entityIDParent["Health"]["Health"]); + s = s + "/"; + s = s + std::to_string((int)(double)entityIDParent["Health"]["MaxHealth"]); + float healthPercentage = (double)entityIDParent["Health"]["Health"]/(double)entityIDParent["Health"]["MaxHealth"]; + (glm::vec4&)entity["Text"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, 1.f); + entity["Text"]["Content"] = s; + } + + if(entity.HasComponent("Fill")) { + float healthPercentage = (double)entityIDParent["Health"]["Health"]/(double)entityIDParent["Health"]["MaxHealth"]; + (glm::vec4&)entity["Fill"]["Color"] = glm::vec4(1.0 - healthPercentage, 0.f, healthPercentage, 0.f); + (double&)entity["Fill"]["Percentage"] = healthPercentage; + + } + } + } +} diff --git a/src/Game/Systems/PlayerMovementSystem.cpp b/src/Game/Systems/PlayerMovementSystem.cpp index e14cd146..0f6588be 100644 --- a/src/Game/Systems/PlayerMovementSystem.cpp +++ b/src/Game/Systems/PlayerMovementSystem.cpp @@ -1,18 +1,160 @@ #include "Systems/PlayerMovementSystem.h" +PlayerMovementSystem::PlayerMovementSystem(World* world, EventBroker* eventBroker) + : System(world, eventBroker) + , PureSystem("Player") +{ + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &PlayerMovementSystem::OnPlayerSpawned); +} + +PlayerMovementSystem::~PlayerMovementSystem() +{ + for (auto& kv : m_PlayerInputControllers) { + delete kv.second; + } +} + +void PlayerMovementSystem::Update(double dt) +{ + for (auto& kv : m_PlayerInputControllers) { + EntityWrapper player = kv.first; + auto& controller = kv.second; + + if (!player.Valid()) { + continue; + } + + EntityWrapper cameraEntity = player.FirstChildByName("Camera"); + if (cameraEntity.Valid()) { + glm::vec3& cameraOrientation = cameraEntity["Transform"]["Orientation"]; + cameraOrientation.x += controller->Rotation().x; + // Limit camera pitch so we don't break our necks + cameraOrientation.x = glm::clamp(cameraOrientation.x, -glm::half_pi(), glm::half_pi()); + } + + ComponentWrapper& cTransform = player["Transform"]; + glm::vec3& ori = cTransform["Orientation"]; + ori.y += controller->Rotation().y; + + float playerMovementSpeed = player["Player"]["MovementSpeed"]; + float playerCrouchSpeed = player["Player"]["CrouchSpeed"]; + + if (player.HasComponent("Physics")) { + ComponentWrapper cPhysics = player["Physics"]; + + glm::vec3 wishDirection = controller->Movement() * glm::inverse(glm::quat(ori)); + float wishSpeed; + if (controller->Crouching()) { + wishSpeed = playerCrouchSpeed; + } else { + wishSpeed = playerMovementSpeed; + } + glm::vec3& velocity = cPhysics["Velocity"]; + ImGui::Text("velocity: (%f, %f, %f)", velocity.x, velocity.y, velocity.z); + glm::vec3 groundVelocity(0.f, 0.f, 0.f); + groundVelocity.x = glm::dot(velocity, glm::vec3(1.f, 0.f, 0.f)); + groundVelocity.z = glm::dot(velocity, glm::vec3(0.f, 0.f, 1.f)); + ImGui::Text("groundVelocity: (%f, %f, %f) |%f|", groundVelocity.x, groundVelocity.y, groundVelocity.z, glm::length(wishDirection)); + ImGui::Text("wishDirection: (%f, %f, %f) |%f|", wishDirection.x, wishDirection.y, wishDirection.z, glm::length(wishDirection)); + float currentSpeedProj = glm::dot(groundVelocity, wishDirection); + float addSpeed = wishSpeed - currentSpeedProj; + ImGui::Text("currentSpeedProj: %f", currentSpeedProj); + ImGui::Text("wishSpeed: %f", wishSpeed); + ImGui::Text("addSpeed: %f", addSpeed); + + if (addSpeed > 0) { + static float accel = 15.f; + ImGui::InputFloat("accel", &accel); + static float airAccel = 0.5f; + ImGui::InputFloat("airAccel", &airAccel); + float actualAccel = (velocity.y != 0) ? airAccel : accel; + static float surfaceFriction = 5.f; + ImGui::InputFloat("surfaceFriction", &surfaceFriction); + float accelerationSpeed = actualAccel * (float)dt * wishSpeed * surfaceFriction; + accelerationSpeed = glm::min(accelerationSpeed, addSpeed); + velocity += accelerationSpeed * wishDirection; + ImGui::Text("velocity: (%f, %f, %f) |%f|", velocity.x, velocity.y, velocity.z, glm::length(velocity)); + } + + if (controller->Jumping() && !controller->Crouching() && velocity.y == 0.f) { + velocity.y += 4.f; + } + + //if (player.HasComponent("AABB")) { + // glm::vec3& size = player["AABB"]["Size"]; + // if (controller->Crouching()) { + // size = glm::vec3(1.f, 1.f, 1.f); + // } else { + // size = glm::vec3(1.f, 1.6f, 1.f); + // } + //} + + // Animations + EntityWrapper playerModel = player.FirstChildByName("PlayerModel"); + if (playerModel.Valid()) { + ComponentWrapper cAnimation = playerModel["Animation"]; + + float movementLength = glm::length(groundVelocity); + if (glm::length(controller->Movement()) > 0.f) { + if (controller->Crouching()) { + cAnimation["Name"] = "Crouch Walk"; + (double&)cAnimation["Speed"] = 1.f * -glm::sign(controller->Movement().z); + } else { + cAnimation["Name"] = "Run"; + (double&)cAnimation["Speed"] = 2.f * -glm::sign(controller->Movement().z); + } + } else { + if (controller->Crouching()) { + cAnimation["Name"] = "Crouch"; + (double&)cAnimation["Speed"] = 1.f; + } else { + cAnimation["Name"] = "Hold Pos"; + (double&)cAnimation["Speed"] = 1.f; + } + } + } + } + + controller->Reset(); + } +} + void PlayerMovementSystem::UpdateComponent(EntityWrapper& entity, ComponentWrapper& component, double dt) { ComponentWrapper& cTransform = entity["Transform"]; if (!entity.HasComponent("Physics")) { return; } - ComponentWrapper& cPhysics = entity["Physics"]; + ComponentWrapper& cPhysics = entity["Physics"]; glm::vec3& velocity = cPhysics["Velocity"]; + + // Ground friction + float speed = glm::length(velocity); + static float groundFriction = 7.f; + ImGui::InputFloat("groundFriction", &groundFriction); + static float airFriction = 0.f; + ImGui::InputFloat("airFriction", &airFriction); + float friction = (velocity.y != 0) ? airFriction : groundFriction; + if (speed > 0) { + float drop = speed * friction * (float)dt; + float multiplier = glm::max(speed - drop, 0.f) / speed; + velocity.x *= multiplier; + velocity.z *= multiplier; + } + if (cPhysics["Gravity"]) { - velocity.y -= 9.82 * dt; + velocity.y -= 9.82f * (float)dt; } glm::vec3& position = cTransform["Position"]; position += velocity * (float)dt; -} \ No newline at end of file +} + +bool PlayerMovementSystem::OnPlayerSpawned(Events::PlayerSpawned& e) +{ + // When a player spawns, create an input controller for them + m_PlayerInputControllers[e.Player] = new FirstPersonInputController(m_EventBroker, e.PlayerID); + + return true; +} diff --git a/src/Game/Systems/PlayerSpawnSystem.cpp b/src/Game/Systems/PlayerSpawnSystem.cpp index 4224d46c..7db6e8ff 100644 --- a/src/Game/Systems/PlayerSpawnSystem.cpp +++ b/src/Game/Systems/PlayerSpawnSystem.cpp @@ -4,6 +4,8 @@ PlayerSpawnSystem::PlayerSpawnSystem(World* m_World, EventBroker* eventBroker) : System(m_World, eventBroker) { EVENT_SUBSCRIBE_MEMBER(m_OnInputCommand, &PlayerSpawnSystem::OnInputCommand); + EVENT_SUBSCRIBE_MEMBER(m_OnPlayerSpawnerd, &PlayerSpawnSystem::OnPlayerSpawned); + m_NetworkEnabled = ResourceManager::Load("Config.ini")->Get("Networking.StartNetwork", false); } void PlayerSpawnSystem::Update(double dt) @@ -13,7 +15,7 @@ void PlayerSpawnSystem::Update(double dt) return; } - for (auto& team : m_SpawnRequests) { + for (auto& req : m_SpawnRequests) { for (auto& cPlayerSpawn : *playerSpawns) { EntityWrapper spawner(m_World, cPlayerSpawn.EntityID); if (!spawner.HasComponent("Spawner")) { @@ -22,7 +24,7 @@ void PlayerSpawnSystem::Update(double dt) // If the spawner has a team affiliation, check it if (spawner.HasComponent("Team")) { - if ((int)spawner["Team"]["Team"] != team) { + if ((int)spawner["Team"]["Team"] != req.Team) { continue; } } @@ -30,7 +32,15 @@ void PlayerSpawnSystem::Update(double dt) // Spawn the player! EntityWrapper player = SpawnerSystem::Spawn(spawner); // Set the player team affiliation - player["Team"]["Team"] = team; + player["Team"]["Team"] = req.Team; + + // Publish a PlayerSpawned event + Events::PlayerSpawned e; + e.PlayerID = req.PlayerID; + e.Player = player; + e.Spawner = spawner; + m_EventBroker->Publish(e); + } } m_SpawnRequests.clear(); @@ -42,10 +52,60 @@ bool PlayerSpawnSystem::OnInputCommand(const Events::InputCommand& e) return false; } + // Team picks should be processed ONLY server-side! + // Don't make a spawn request if PlayerID is -1, i.e. we're the client. + if (e.PlayerID == -1 && m_NetworkEnabled) { + return false; + } + if (e.Value != 0) { - m_SpawnRequests.push_back((int)e.Value); + SpawnRequest req; + req.PlayerID = e.PlayerID; + req.Team = (ComponentInfo::EnumType)e.Value; + m_SpawnRequests.push_back(req); } return true; } +bool PlayerSpawnSystem::OnPlayerSpawned(Events::PlayerSpawned& e) +{ + // When a player is actually spawned (since the actual spawning is handled on the server) + + // Check if a player already exists + if (m_PlayerEntities.count(e.PlayerID) != 0) { + // TODO: Disallow infinite respawning here + m_World->DeleteEntity(m_PlayerEntities[e.PlayerID].ID); + } + + // Store the player for future reference + m_PlayerEntities[e.PlayerID] = e.Player; + + // Set the camera to the correct entity + EntityWrapper cameraEntity = e.Player.FirstChildByName("Camera"); + if (cameraEntity.Valid()) { + Events::SetCamera e; + e.CameraEntity = cameraEntity; + m_EventBroker->Publish(e); + } + + // HACK: Set the player model color to team color + EntityWrapper playerModel = e.Player.FirstChildByName("PlayerModel"); + if (playerModel.Valid() && e.Player.HasComponent("Team")) { + ComponentWrapper cTeam = e.Player["Team"]; + ComponentWrapper cModel = playerModel["Model"]; + if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Red")) { + cModel["Color"] = glm::vec3(1.f, 0.f, 0.f); + } else if ((ComponentInfo::EnumType)cTeam["Team"] == cTeam["Team"].Enum("Blue")) { + cModel["Color"] = glm::vec3(0.f, 0.25f, 1.f); + } + } + + // TODO: Set the player name to whatever + EntityWrapper playerName = e.Player.FirstChildByName("PlayerName"); + if (playerName.Valid()) { + playerName["Text"]["Content"] = e.PlayerName; + } + + return true; +} \ No newline at end of file diff --git a/src/Game/Systems/SpawnerSystem.cpp b/src/Game/Systems/SpawnerSystem.cpp index 8cb5b99d..f9357113 100644 --- a/src/Game/Systems/SpawnerSystem.cpp +++ b/src/Game/Systems/SpawnerSystem.cpp @@ -50,7 +50,8 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent / // Set its position and orientation to that of the SpawnPoint spawnedEntity["Transform"]["Position"] = Transform::AbsolutePosition(spawnPoint.World, spawnPoint.ID); - spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint.World, spawnPoint.ID)); + // TODO: Quaternions, bitch + //spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint.World, spawnPoint.ID)); return spawnedEntity; } diff --git a/src/Game/Systems/WeaponSystem.cpp b/src/Game/Systems/WeaponSystem.cpp index 11f76490..f78e092f 100644 --- a/src/Game/Systems/WeaponSystem.cpp +++ b/src/Game/Systems/WeaponSystem.cpp @@ -5,49 +5,127 @@ WeaponSystem::WeaponSystem(World* world, EventBroker* eventBroker, IRenderer* re , ImpureSystem() , m_Renderer(renderer) { + EVENT_SUBSCRIBE_MEMBER(m_EPlayerSpawned, &WeaponSystem::OnPlayerSpawned); EVENT_SUBSCRIBE_MEMBER(m_EInputCommand, &WeaponSystem::OnInputCommand); EVENT_SUBSCRIBE_MEMBER(m_EShoot, &WeaponSystem::OnShoot); } void WeaponSystem::Update(double dt) { - for (int i = m_EShootVector.size(); i > 0; i--) - { - //TODO: check if player has enough ammo and if weapon has a cooldown or not - //pick the object - PickData pickDataFromShot = m_Renderer->Pick(std::get<1>(m_EShootVector[i - 1])); - if (pickDataFromShot.Entity == EntityID_Invalid) { - m_EShootVector.erase(m_EShootVector.begin() + i - 1); - continue; - } - //if its a player, do PlayerDamage event - const bool hasPlayerComponent = m_World->HasComponent(pickDataFromShot.Entity, "Player"); - if (hasPlayerComponent) { - Events::PlayerDamage ePlayerDamage; - //TODO: damage based on weapontype/class? - //TODO: multiple shots at the same time? (shotgunner) - ePlayerDamage.DamageAmount = 25; - ePlayerDamage.PlayerDamagedID = pickDataFromShot.Entity; - m_EventBroker->Publish(ePlayerDamage); - } - m_EShootVector.erase(m_EShootVector.begin() + i - 1); +} + +bool WeaponSystem::OnPlayerSpawned(const Events::PlayerSpawned& e) +{ + if (e.PlayerID == -1) { + m_LocalPlayer = e.Player; } + return true; } bool WeaponSystem::OnInputCommand(const Events::InputCommand& e) { if (e.Command == "PrimaryFire" && e.Value > 0) { Events::Shoot eShoot; - eShoot.shooter = e.PlayerID; + if (e.PlayerID == -1) { + eShoot.Player = m_LocalPlayer; + } else { + eShoot.Player = e.Player; + } m_EventBroker->Publish(eShoot); } + return true; } -bool WeaponSystem::OnShoot(const Events::Shoot& e) { - //screen center, based on current resolution! + +bool WeaponSystem::OnShoot(Events::Shoot& eShoot) +{ + if (!eShoot.Player.Valid()) { + return false; + } + + // TODO: Weapon firing effects here + + auto rayRed = ResourceManager::Load("Schema/Entities/RayRed.xml"); + auto rayBlue = ResourceManager::Load("Schema/Entities/RayBlue.xml"); + + EntityWrapper weapon = eShoot.Player.FirstChildByName("WeaponMuzzle"); + if (weapon.Valid()) { + EntityWrapper ray; + if ((ComponentInfo::EnumType)eShoot.Player["Team"]["Team"] == eShoot.Player["Team"]["Team"].Enum("Red")) { + EntityFileParser parser(rayRed); + EntityID rayID = parser.MergeEntities(m_World); + ray = EntityWrapper(m_World, rayID); + } else { + EntityFileParser parser(rayBlue); + EntityID rayID = parser.MergeEntities(m_World); + ray = EntityWrapper(m_World, rayID); + } + + glm::mat4 transformation = Transform::AbsoluteTransformation(weapon); + glm::vec3 scale; + glm::vec3 translation; + glm::quat orientation; + glm::vec3 skew; + glm::vec4 perspective; + glm::decompose(transformation, scale, orientation, translation, skew, perspective); + + // Matrix to euler angles + glm::vec3 euler; + euler.y = glm::asin(-transformation[0][2]); + if (cos(euler.y) != 0) { + euler.x = atan2(transformation[1][2], transformation[2][2]); + euler.z = atan2(transformation[0][1], transformation[0][0]); + } else { + euler.x = atan2(-transformation[2][0], transformation[1][1]); + euler.z = 0; + } + + //LOG_DEBUG("rotation: %f %f %f", euler.x, euler.y, euler.z); + (glm::vec3&)ray["Transform"]["Position"] = translation; + (glm::vec3&)ray["Transform"]["Orientation"] = euler; + //(glm::vec3&)ray["Transform"]["Orientation"] = Transform::AbsoluteOrientationEuler(weapon); + } + + // Only run further picking code client-side! + if (eShoot.Player != m_LocalPlayer) { + return false; + } + + // Screen center, based on current resolution! + // TODO: check if player has enough ammo and if weapon has a cooldown or not Rectangle screenResolution = m_Renderer->Resolution(); glm::vec2 centerScreen = glm::vec2(screenResolution.Width / 2, screenResolution.Height / 2); - m_EShootVector.push_back(std::make_pair(e.shooter, centerScreen)); + + // TODO: check if player has enough ammo and if weapon has a cooldown or not + + // Pick middle of screen + PickData pickData = m_Renderer->Pick(centerScreen); + if (pickData.Entity == EntityID_Invalid) { + return false; + } + + EntityWrapper player(m_World, pickData.Entity); + + // Only care about players being hit + if (!player.HasComponent("Player")) { + player = player.FirstParentWithComponent("Player"); + } + if (!player.Valid()) { + return false; + } + + // Check for friendly fire + EntityWrapper shooter = eShoot.Player; + if ((ComponentInfo::EnumType)player["Team"]["Team"] == (ComponentInfo::EnumType)shooter["Team"]["Team"]) { + return false; + } + + // TODO: Weapon damage calculations etc + Events::PlayerDamage ePlayerDamage; + ePlayerDamage.Player = player; + ePlayerDamage.Damage = 100; + m_EventBroker->Publish(ePlayerDamage); + return true; -} \ No newline at end of file +} diff --git a/src/Tests/CollisionTest.cpp b/src/Tests/CollisionTest.cpp index 9f522c06..67b6ce19 100644 --- a/src/Tests/CollisionTest.cpp +++ b/src/Tests/CollisionTest.cpp @@ -27,7 +27,9 @@ using boost::unit_test_framework::test_case; void RayTest(std::string fileName) { //simple box test Ray ray(glm::vec3(-50, 0, 0), glm::vec3(1, 0, 0)); - //using a rawmodel here, else we have to init the renderingsystem + //using a + + here, else we have to init the renderingsystem ResourceManager::RegisterType("RawModel"); auto unitBox = ResourceManager::Load(fileName); BOOST_REQUIRE(unitBox != nullptr); @@ -106,7 +108,7 @@ BOOST_AUTO_TEST_CASE(collisionTest2) BOOST_AUTO_TEST_CASE(rayVsModelTest) { //simple box test - RayTest("Models/Core/UnitCube.obj"); + RayTest("Models/Core/UnitCube.mesh"); // 360NoScope Unitcube } BOOST_AUTO_TEST_CASE(rayVsModelTest2) @@ -128,7 +130,7 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) someAABB = AABB(minPos, maxPos); //using a rawmodel here, else we have to init the renderingsystem ResourceManager::RegisterType("RawModel"); - auto unitBox = ResourceManager::Load("Models/Core/UnitCube.obj"); + auto unitBox = ResourceManager::Load("Models/Core/UnitCube.mesh"); // 360NoScope unitcube BOOST_CHECK(unitBox != nullptr); for (size_t i = 0; i < 1000000; i++) @@ -187,19 +189,19 @@ BOOST_AUTO_TEST_CASE(rayVsModelTest2) BOOST_AUTO_TEST_CASE(rayVsModelTest3) { //simple test - RayTest("Models/Core/UnitSphere.obj"); + RayTest("Models/Core/UnitSphere.mesh"); // 360NoScope unitSphere } BOOST_AUTO_TEST_CASE(rayVsModelTest4) { //simple test - RayTest("Models/Core/UnitCylinder.obj"); + RayTest("Models/Core/UnitCylinder.mesh"); // 360NoScope unitCylinder } BOOST_AUTO_TEST_CASE(rayVsModelTest5) { //simple test - RayTest("Models/Core/UnitRaptor.obj"); + RayTest("Models/Core/UnitRaptor.mesh"); // 360NoScope unitRaptor } BOOST_AUTO_TEST_CASE(octTest) { diff --git a/src/Tests/HealthSystemTest.cpp b/src/Tests/HealthSystemTest.cpp index b28a14ba..bdd9ba4b 100644 --- a/src/Tests/HealthSystemTest.cpp +++ b/src/Tests/HealthSystemTest.cpp @@ -56,7 +56,7 @@ GameHealthSystemTest::GameHealthSystemTest() EntityID playerID = m_World->CreateEntity(); ComponentWrapper transform = m_World->AttachComponent(playerID, "Transform"); ComponentWrapper model = m_World->AttachComponent(playerID, "Model"); - model["Resource"] = "Models/Core/UnitSphere.obj"; + model["Resource"] = "Models/Core/UnitSphere.mesh"; // 360NoScope UnitSphere ComponentWrapper player = m_World->AttachComponent(playerID, "Player"); ComponentWrapper health = m_World->AttachComponent(playerID, "Health"); healthsID = playerID; @@ -81,7 +81,7 @@ GameHealthSystemTest::GameHealthSystemTest() EntityID playerID2 = m_World->CreateEntity(); ComponentWrapper transform2 = m_World->AttachComponent(playerID2, "Transform"); ComponentWrapper model2 = m_World->AttachComponent(playerID2, "Model"); - model2["Resource"] = "Models/Core/UnitSphere.obj"; + model2["Resource"] = "Models/Core/UnitSphere.mesh"; // 360NoScope UnitSphere ComponentWrapper player2 = m_World->AttachComponent(playerID2, "Player"); ComponentWrapper health2 = m_World->AttachComponent(playerID2, "Health"); //END TEST diff --git a/src/Tests/OldOctTree.cpp b/src/Tests/OldOctTree.cpp index 16ecec65..8970988c 100644 --- a/src/Tests/OldOctTree.cpp +++ b/src/Tests/OldOctTree.cpp @@ -112,7 +112,7 @@ void OctTree::Update(float dt, World* world, Camera* cam) ComponentWrapper transform = world->AttachComponent(m_BoxID, "Transform"); transform["Scale"] = boxSize; ComponentWrapper model = world->AttachComponent(m_BoxID, "Model"); - model["Resource"] = "Models/Core/UnitBox.obj"; + model["Resource"] = "Models/Core/UnitBox.mesh"; // 360NoScope UnitBox m_UpdatedOnce = true; } diff --git a/src/Tests/ResourceManagerTest.cpp b/src/Tests/ResourceManagerTest.cpp index df1fd76d..1f9f4991 100644 --- a/src/Tests/ResourceManagerTest.cpp +++ b/src/Tests/ResourceManagerTest.cpp @@ -26,7 +26,7 @@ BOOST_AUTO_TEST_CASE(resourceManagerTest) BOOST_CHECK(!ResourceManager::IsResourceLoaded("ConfigFile", "Config.ini")); //configfile without register - BOOST_CHECK_THROW(ResourceManager::Load("Models/Core/ScreenQuad.obj"),Resource::FailedLoadingException); + BOOST_CHECK_THROW(ResourceManager::Load("Models/Core/ScreenQuad.mesh"),Resource::FailedLoadingException); //there is no error feedback to check if you try to release the wrong resources - hence that cant be tested either } diff --git a/tools/MayaExporter/MayaExporter/Export.cpp b/tools/MayaExporter/MayaExporter/Export.cpp new file mode 100644 index 00000000..d8f1abd4 --- /dev/null +++ b/tools/MayaExporter/MayaExporter/Export.cpp @@ -0,0 +1,224 @@ +#include "Export.h" + +Export::Export() +{ + +} + +bool Export::Meshes(std::string pathName, bool selectedOnly) +{ + MStatus status; + if (pathName.empty()) { + MGlobal::displayError(MString() + "Export::Meshes() got no pathName. Do not know where to write file"); + return false; + } + + MSelectionList selectedOnStart; + MGlobal::getActiveSelectionList(selectedOnStart); + if (selectedOnStart.length() > 0) { + for (int i = 0; i < selectedOnStart.length(); i++) { + MObject item; + selectedOnStart.getDependNode(i, item); + MGlobal::unselect(item); + } + } + MGlobal::displayInfo(MString() + "Disabel IKSolvers"); + status = MGlobal::executeCommand("doEnableNodeItems false all;"); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "EnableNodeItems false all failed: " + status.errorString()); + } + MGlobal::displayInfo(MString() + "Has disabel IKSolvers"); + MObjectArray Objects; + if (selectedOnly) { + // Retrieving the objects we currently have selected + + // Loop through or list of selection(s) + for (unsigned int i = 0; i < selectedOnStart.length(); i++) { + MObject object; + selectedOnStart.getDependNode(i, object); + + if (object.hasFn(MFn::kMesh)) { + MFnMesh shape(object); + + for (unsigned int k = 0; k < shape.parentCount(); k++) { + MFnDependencyNode thisNode(object); + MPlugArray connections; + thisNode.findPlug("inMesh").connectedTo(connections, true, true); + + for (unsigned int i = 0; i < connections.length(); i++) { + if (connections[i].node().apiType() == MFn::kSkinClusterFilter) { + MGlobal::select(shape.parent(i), MGlobal::kReplaceList); + MGlobal::displayInfo(MString() + "Moving " + thisNode.name() + " to bindPose."); + status = MGlobal::executeCommand("GoToBindPose;"); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "GoToBindPose: " + status.errorString()); + } + MGlobal::displayInfo(MString() + "Has moved " + thisNode.name() + " to bindPose."); + } + } + } + Objects.append(object); + } + } + } else { + // Loop through all nodes in the scene + MItDependencyNodes it(MFn::kMesh); + for (; !it.isDone(); it.next()) { + MObject node = it.thisNode(); + MFnDependencyNode thisNode(node); + MPlugArray connections; + thisNode.findPlug("inMesh").connectedTo(connections, true, true); + + for (unsigned int i = 0; i < connections.length(); i++) { + if (connections[i].node().apiType() == MFn::kSkinClusterFilter) { + MGlobal::select(node); + MGlobal::displayInfo(MString() + "Moving " + thisNode.name() + " to bindPose."); + MGlobal::executeCommand("GoToBindPose"); + MGlobal::displayInfo(MString() + "Has moved " + thisNode.name() + " to bindPose."); + } + } + + Objects.append(node); + } + } + GetMeshData(Objects); + WriteMeshData(pathName); + MGlobal::displayInfo(MString() + "Enabling IKSolvers"); + status = MGlobal::executeCommand("doEnableNodeItems true all;"); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "doEnableNodeItems true all: " + status.errorString()); + } + MGlobal::displayInfo(MString() + "Has enabling IKSolvers"); + if (selectedOnStart.length() > 0) { + for (int i = 0; i < selectedOnStart.length(); i++) { + MObject item; + selectedOnStart.getDependNode(i, item); + MGlobal::select(item); + } + } + return true; +} + +bool Export::Materials(std::string pathName) +{ + if (!GetMaterialData()) + return false; + WriteMaterialData(pathName); + return true; +} + +bool Export::Animations(std::string pathName, std::vector animInfo) +{ + if (MAnimControl::currentTime().unit() != MTime::kNTSCField) { + + MGlobal::displayError(MString() + "Please change to 60 FPS under Preferences/Settings!"); + return false; + } + + if (pathName.empty()) { + MGlobal::displayError(MString() + "Export::Animations() got no pathName. Do not know where to write file"); + return false; + } + allBindPoses = m_SkeletonHandler.GetBindPoses(); + + for (auto clip : animInfo) { + if (!GetAnimationData(clip)) { + MGlobal::displayError(MString() + "Export::Animations() failed to export " + clip.Name.c_str()); + return false; + } + } + WriteAnimData(pathName); + return true; +} + +bool Export::GetMeshData(MObjectArray object) +{ + meshes = m_MeshHandler.GetMeshData(object); + return true; +} + +bool Export::GetMaterialData() +{ + // Traverse scene and return vector with all materials + AllMaterials = m_MaterialHandler.DoIt(meshes); + + return true; +} + +bool Export::GetAnimationData(AnimationInfo animInfo) +{ + if (animInfo.Name.empty()) { + MGlobal::displayError(MString() + "A clip does not have a name"); + return false; + } + if (animInfo.End - animInfo.Start <= 0) { + MGlobal::displayError(MString() + "A clip ends before it starts or contains 0 frames"); + return false; + } + + allAnimations.push_back(m_SkeletonHandler.GetAnimData(animInfo.Name, animInfo.Start, animInfo.End)); + return true; +} + +void Export::WriteMeshData(std::string pathName) +{ + m_MeshFile.ASCIIFilePath(pathName +"_mesh.txt"); + m_MeshFile.binaryFilePath(pathName + ".mesh"); + + m_MeshFile.OpenFiles(); + + m_MeshFile.writeToFiles((OutputData*)&meshes); + + m_MeshFile.CloseFiles(); +} + +void Export::WriteAnimData(std::string pathName) +{ + if (allBindPoses.size() > 0) { + m_AnimFile.ASCIIFilePath(pathName + "_anim.txt"); + m_AnimFile.binaryFilePath(pathName + ".anim"); + + m_AnimFile.OpenFiles(); + int size = allBindPoses.size(); + m_AnimFile.writeToFiles(&size); + + + size = allAnimations.size(); + m_AnimFile.writeToFiles(&size); + + //print out all bind poses + for (auto aBindPose : allBindPoses) { + m_AnimFile.writeToFiles((OutputData*)&aBindPose); + } + for (auto aAnimation : allAnimations) { + m_AnimFile.writeToFiles((OutputData*)&aAnimation); + } + + m_AnimFile.CloseFiles(); + } else + MGlobal::displayInfo("Export::WriteAnimData() got called when allBindPoses contained no data, did not write nor created them"); +} + +void Export::WriteMaterialData(std::string pathName) +{ + m_MtrlFile.ASCIIFilePath(pathName +"_mtrl.txt"); + m_MtrlFile.binaryFilePath(pathName + ".mtrl"); + + m_MtrlFile.OpenFiles(); + + int size = (*AllMaterials).size(); + m_MtrlFile.writeToFiles(&size); + + for (auto aMaterial : *AllMaterials) { + m_MtrlFile.writeToFiles((OutputData*)&aMaterial); + } + m_MtrlFile.CloseFiles(); +} + +Export::~Export() +{ + /* delete m_MaterialHandler; + delete m_SkeletonHandler; + delete m_MeshHandler;*/ + +} \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/Export.h b/tools/MayaExporter/MayaExporter/Export.h new file mode 100644 index 00000000..42b40445 --- /dev/null +++ b/tools/MayaExporter/MayaExporter/Export.h @@ -0,0 +1,58 @@ +#ifndef Export_Export_h__ +#define Export_Export_h__ + +#include +#include + +#include "MayaIncludes.h" +#include "Material.h" +#include "Mesh.h" +#include "Skeleton.h" +#include "WriteToFile.h" + +class Export { +public: + Export(); + + ~Export(); + + struct AnimationInfo { + std::string Name; + int Start; + int End; + }; + + bool Meshes(std::string pathName, bool selectedOnly = false); + bool Materials(std::string pathName); + bool Animations(std::string pathName, std::vector animInfo); + +private: + bool GetMeshData(MObjectArray object); + bool GetMaterialData(); + bool GetAnimationData(AnimationInfo info); + + void WriteMeshData(std::string pathName); + void WriteAnimData(std::string pathName); + void WriteMaterialData(std::string pathName); + + Material m_MaterialHandler; + Skeleton m_SkeletonHandler; + MeshClass m_MeshHandler; + + + //File export + WriteToFile m_MeshFile; + WriteToFile m_AnimFile; + WriteToFile m_MtrlFile; + + //Mesh Data + Mesh meshes; + + //Animation Data + std::vector allBindPoses; + std::vector allAnimations; + + //Material Data + std::vector* AllMaterials; +}; +#endif \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/GeneratedFiles/Debug/moc_Menu.cpp b/tools/MayaExporter/MayaExporter/GeneratedFiles/Debug/moc_Menu.cpp index aef8d533..c7879431 100644 --- a/tools/MayaExporter/MayaExporter/GeneratedFiles/Debug/moc_Menu.cpp +++ b/tools/MayaExporter/MayaExporter/GeneratedFiles/Debug/moc_Menu.cpp @@ -22,7 +22,7 @@ static const uint qt_meta_data_Menu[] = { 6, // revision 0, // classname 0, 0, // classinfo - 7, 14, // methods + 5, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors @@ -30,22 +30,19 @@ static const uint qt_meta_data_Menu[] = { 0, // signalCount // slots: signature, parameters, type, tag, flags - 14, 6, 5, 5, 0x08, - 35, 5, 5, 5, 0x08, - 59, 5, 5, 5, 0x08, + 6, 5, 5, 5, 0x08, + 30, 5, 5, 5, 0x08, + 51, 5, 5, 5, 0x08, 75, 5, 5, 5, 0x08, - 95, 5, 5, 5, 0x08, - 116, 5, 5, 5, 0x08, - 137, 5, 5, 5, 0x08, + 91, 5, 5, 5, 0x08, 0 // eod }; static const char qt_meta_stringdata_Menu[] = { - "Menu\0\0checked\0ExportSelected(bool)\0" - "ExportPathClicked(bool)\0ExportAll(bool)\0" - "CancelClicked(bool)\0Button1Clicked(bool)\0" - "Button2Clicked(bool)\0Button3Clicked(bool)\0" + "Menu\0\0ExportPathClicked(bool)\0" + "AddClipClicked(bool)\0RemoveClipClicked(bool)\0" + "ExportAll(bool)\0CancelClicked(bool)\0" }; void Menu::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) @@ -54,13 +51,11 @@ void Menu::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void * Q_ASSERT(staticMetaObject.cast(_o)); Menu *_t = static_cast(_o); switch (_id) { - case 0: _t->ExportSelected((*reinterpret_cast< bool(*)>(_a[1]))); break; - case 1: _t->ExportPathClicked((*reinterpret_cast< bool(*)>(_a[1]))); break; - case 2: _t->ExportAll((*reinterpret_cast< bool(*)>(_a[1]))); break; - case 3: _t->CancelClicked((*reinterpret_cast< bool(*)>(_a[1]))); break; - case 4: _t->Button1Clicked((*reinterpret_cast< bool(*)>(_a[1]))); break; - case 5: _t->Button2Clicked((*reinterpret_cast< bool(*)>(_a[1]))); break; - case 6: _t->Button3Clicked((*reinterpret_cast< bool(*)>(_a[1]))); break; + case 0: _t->ExportPathClicked((*reinterpret_cast< bool(*)>(_a[1]))); break; + case 1: _t->AddClipClicked((*reinterpret_cast< bool(*)>(_a[1]))); break; + case 2: _t->RemoveClipClicked((*reinterpret_cast< bool(*)>(_a[1]))); break; + case 3: _t->ExportAll((*reinterpret_cast< bool(*)>(_a[1]))); break; + case 4: _t->CancelClicked((*reinterpret_cast< bool(*)>(_a[1]))); break; default: ; } } @@ -98,9 +93,9 @@ int Menu::qt_metacall(QMetaObject::Call _c, int _id, void **_a) if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { - if (_id < 7) + if (_id < 5) qt_static_metacall(this, _c, _id, _a); - _id -= 7; + _id -= 5; } return _id; } diff --git a/tools/MayaExporter/MayaExporter/Material.cpp b/tools/MayaExporter/MayaExporter/Material.cpp new file mode 100644 index 00000000..03521231 --- /dev/null +++ b/tools/MayaExporter/MayaExporter/Material.cpp @@ -0,0 +1,237 @@ +#include "Material.h" + +void Material::grabLambertProperties(MaterialNode& material_node, MFnDependencyNode& node) +{ + material_node.Name = node.name().asChar(); + + if (!findColorTexture(material_node, node)) { + node.findPlug("colorR").getValue(material_node.DiffuseColor[0]); + node.findPlug("colorG").getValue(material_node.DiffuseColor[1]); + node.findPlug("colorB").getValue(material_node.DiffuseColor[2]); + + } + + + if (!findIncandescenceTexture(material_node, node)) { + node.findPlug("incandescenceR").getValue(material_node.IncandescenceColor[0]); + node.findPlug("incandescenceG").getValue(material_node.IncandescenceColor[1]); + node.findPlug("incandescenceB").getValue(material_node.IncandescenceColor[2]); + } + + if (findNormalTexture(material_node, node)) { + MGlobal::displayWarning(MString() + "Material " + node.name() + " has no normal texture."); + } +} + +void Material::grabBlinnProperties(MaterialNode& material_node, MFnDependencyNode& node) +{ + if (!findSpecularTexture(material_node, node)) { + node.findPlug("specularColorR").getValue(material_node.SpecularColor[0]); + node.findPlug("specularColorG").getValue(material_node.SpecularColor[1]); + node.findPlug("specularColorB").getValue(material_node.SpecularColor[2]); + } + + m_Plug = node.findPlug("reflectivity"); + m_Plug.getValue(material_node.ReflectionFactor); + + m_Plug = node.findPlug("eccentricity"); + float TempEccent; + m_Plug.getValue(TempEccent); + + // Blinn works differently from Phong which is used in-game. + // This is some magic numbers and math to make a conversion estimate between the two. + // There is no exact conversion between the two, so there are errors. + + // Phong min/max is around Blinn 0.7/0.1 + TempEccent = std::max(std::min(TempEccent, 0.7f), 0.1f); + + material_node.SpecularExponent = std::max(std::min(((2.66f) + (427.0f) * exp((-14.8f) * TempEccent)), 100.0f), 2.0f); +} + +void Material::grabPhongProperties(MaterialNode& material_node, MFnDependencyNode& node) +{ + if (!findSpecularTexture(material_node, node)) { + node.findPlug("specularColorR").getValue(material_node.SpecularColor[0]); + node.findPlug("specularColorG").getValue(material_node.SpecularColor[1]); + node.findPlug("specularColorB").getValue(material_node.SpecularColor[2]); + } + + m_Plug = node.findPlug("reflectivity"); + m_Plug.getValue(material_node.ReflectionFactor); + + m_Plug = node.findPlug("cosinePower "); + m_Plug.getValue(material_node.SpecularExponent); +} + +bool Material::findColorTexture(MaterialNode& material_node, MFnDependencyNode& node) +{ + MPlugArray AllConnections; + + m_Plug = node.findPlug("color", true); + m_Plug.connectedTo(AllConnections, true, false); + + for (int i = 0; i < AllConnections.length(); i++) { + if (AllConnections[i].node().hasFn(MFn::kFileTexture)) { + MFnDependencyNode TextureNode(AllConnections[i].node()); + + std::string FullPath = TextureNode.findPlug("fileTextureName").asString().asChar(); + m_TexturePaths.push_back(FullPath); + + MString workspace; + MStatus status = MGlobal::executeCommand(MString("workspace -q -rd;"), + workspace); + FullPath = FullPath.substr(workspace.length()); + FullPath = FullPath.substr(FullPath.find_first_of("/") + 1); + material_node.ColorMapFile = FullPath.erase(FullPath.find_last_of("."), FullPath.find_last_of(".") - FullPath.size()); + + material_node.ColorMapFileLength = material_node.ColorMapFile.length() + 1; + // Test + MGlobal::displayInfo(MString() + "getAbsolutePathToResources: " + workspace); + MGlobal::displayInfo(MString() + "Texture file: " + FullPath.c_str()); + return true; + } + } + + return false; +} + +bool Material::findNormalTexture(MaterialNode& material_node, MFnDependencyNode& node) +{ + MPlugArray AllConnections; + MPlugArray AllBumpConnections; + + m_Plug = node.findPlug("normalCamera", true); + m_Plug.connectedTo(AllConnections, true, false); + + for (int i = 0; i < AllConnections.length(); i++) { + if (AllConnections[i].node().apiType() == MFn::kBump) { + MFnDependencyNode BumpNode(AllConnections[i].node()); + + BumpNode.findPlug("bumpValue").connectedTo(AllBumpConnections, true, false); + for (int j = 0; j < AllBumpConnections.length(); j++) { + if (AllBumpConnections[j].node().hasFn(MFn::kFileTexture)) { + MFnDependencyNode TextureNode(AllBumpConnections[j].node()); + + std::string FullPath = TextureNode.findPlug("ftn").asString().asChar(); + m_TexturePaths.push_back(FullPath); + + MString workspace; + MStatus status = MGlobal::executeCommand(MString("workspace -q -rd;"), + workspace); + FullPath = FullPath.substr(workspace.length()); + FullPath = FullPath.substr(FullPath.find_first_of("/") + 1); + material_node.NormalMapFile = FullPath.erase(FullPath.find_last_of("."), FullPath.find_last_of(".") - FullPath.size()); + material_node.NormalMapFileLength = material_node.NormalMapFile.length() + 1; + return true; + } + } + } + } + + return false; +} + +bool Material::findSpecularTexture(MaterialNode& material_node, MFnDependencyNode& node) +{ + MPlugArray AllConnections; + + m_Plug = node.findPlug("specularColor", true); + m_Plug.connectedTo(AllConnections, true, false); + + for (int i = 0; i < AllConnections.length(); i++) { + if (AllConnections[i].node().hasFn(MFn::kFileTexture)) { + MFnDependencyNode TextureNode(AllConnections[i].node()); + + std::string FullPath = TextureNode.findPlug("ftn").asString().asChar(); + m_TexturePaths.push_back(FullPath); + + MString workspace; + MStatus status = MGlobal::executeCommand(MString("workspace -q -rd;"), + workspace); + FullPath = FullPath.substr(workspace.length()); + FullPath = FullPath.substr(FullPath.find_first_of("/") + 1); + material_node.SpecularMapFile = FullPath.erase(FullPath.find_last_of("."), FullPath.find_last_of(".") - FullPath.size()); + material_node.SpecularMapFileLength = material_node.SpecularMapFile.length() + 1; + return true; + } + } + return false; +} + +bool Material::findIncandescenceTexture(MaterialNode& material_node, MFnDependencyNode& node) +{ + MPlugArray AllConnections; + + m_Plug = node.findPlug("incandescence", true); + m_Plug.connectedTo(AllConnections, true, false); + + for (int i = 0; i < AllConnections.length(); i++) { + if (AllConnections[i].node().hasFn(MFn::kFileTexture)) { + MFnDependencyNode TextureNode(AllConnections[i].node()); + + std::string FullPath = TextureNode.findPlug("ftn").asString().asChar(); + m_TexturePaths.push_back(FullPath); + + MString workspace; + MStatus status = MGlobal::executeCommand(MString("workspace -q -rd;"), + workspace); + FullPath = FullPath.substr(workspace.length()); + FullPath = FullPath.substr(FullPath.find_first_of("/") + 1); + material_node.IncandescenceMapFile = FullPath.erase(FullPath.find_last_of("."), FullPath.find_last_of(".") - FullPath.size()); + material_node.IncandescenceMapFileLength = material_node.IncandescenceMapFile.length() + 1; + return true; + } + } + return false; +} + +// Returns the absolute path for all textures. Use for copying texture files. +std::vector* Material::TexturePaths() +{ + return &m_TexturePaths; +} + +// Traverse the DAG and grab all the materials +std::vector* Material::DoIt(Mesh mesh) +{ + // All materials we care about inherit from Lambert + MItDependencyNodes matIt(MFn::kLambert); + m_AllMaterials.clear(); + while (!matIt.isDone()) { + MFnDependencyNode MaterialFnDN(matIt.thisNode()); + MaterialNode MaterialStorage; + bool meshHasMaterial = false; + //Mesh Indices is a map with : indices> + int totalIndices = 0; + for (auto aMeshMaterial : mesh.Indices) { + MGlobal::displayInfo(MString() + "Material: " + aMeshMaterial.first.c_str() + " " + MaterialFnDN.name().asChar()); + if (aMeshMaterial.first.compare(MaterialFnDN.name().asChar()) == 0) { + meshHasMaterial = true; + MaterialStorage.IndexStart = totalIndices; + MaterialStorage.IndexEnd = totalIndices + aMeshMaterial.second.size() - 1; + break; + } + totalIndices += aMeshMaterial.second.size(); + } + if (meshHasMaterial) { + grabLambertProperties(MaterialStorage, MaterialFnDN); + + if (matIt.thisNode().hasFn(MFn::kPhong)) { + grabPhongProperties(MaterialStorage, MaterialFnDN); + + } else if (matIt.thisNode().hasFn(MFn::kBlinn)) { + grabBlinnProperties(MaterialStorage, MaterialFnDN); + + } else if (matIt.thisNode().hasFn(MFn::kLambert)) { + MaterialStorage.ReflectionFactor = 0.0f; + MaterialStorage.SpecularExponent = 0.0f; + } + + m_AllMaterials.push_back(MaterialStorage); + } + matIt.next(); + } + + return &m_AllMaterials; +} + diff --git a/tools/MayaExporter/MayaExporter/Material.h b/tools/MayaExporter/MayaExporter/Material.h new file mode 100644 index 00000000..e405dd07 --- /dev/null +++ b/tools/MayaExporter/MayaExporter/Material.h @@ -0,0 +1,115 @@ +#ifndef Material_Material_h__ +#define Material_Material_h__ + +#include +#include +#include +#include +#include + +#include "MayaIncludes.h" +#include "OutputData.h" +#include "Mesh.h" + +class MaterialNode : public OutputData +{ +public: + std::string Name; + + float ReflectionFactor; + float SpecularExponent; + + float DiffuseColor[3]{ 1.0f, 1.0f, 1.0f }; + unsigned int ColorMapFileLength = 0; + std::string ColorMapFile; + + float SpecularColor[3]{ 1.0f, 1.0f, 1.0f }; + unsigned int SpecularMapFileLength = 0; + std::string SpecularMapFile; + + unsigned int NormalMapFileLength = 0; + std::string NormalMapFile; + + float IncandescenceColor[3]{ 1.0f, 1.0f, 1.0f }; + unsigned int IncandescenceMapFileLength = 0; + std::string IncandescenceMapFile; + + unsigned int IndexStart; + unsigned int IndexEnd; + + virtual void WriteBinary(std::ostream& out) + { + out.write((char*)&ColorMapFileLength, sizeof(unsigned int)); + out.write((char*)&NormalMapFileLength, sizeof(unsigned int)); + out.write((char*)&SpecularMapFileLength, sizeof(unsigned int)); + out.write((char*)&IncandescenceMapFileLength, sizeof(unsigned int)); + + out.write((char*)&SpecularExponent, sizeof(float)); + out.write((char*)&ReflectionFactor, sizeof(float)); + out.write((char*)&DiffuseColor, sizeof(float) * 3); + out.write((char*)&SpecularColor, sizeof(float) * 3); + out.write((char*)&IncandescenceColor, sizeof(float) * 3); + out.write((char*)&IndexStart, sizeof(unsigned int)); + out.write((char*)&IndexEnd, sizeof(unsigned int)); + + out.write(ColorMapFile.c_str(), ColorMapFileLength); + out.write(NormalMapFile.c_str(), NormalMapFileLength); + out.write(SpecularMapFile.c_str(), SpecularMapFileLength); + out.write(IncandescenceMapFile.c_str(), IncandescenceMapFileLength); + } + + virtual void WriteASCII(std::ostream& out) const + { + out << "New Material _ not in binary" << endl; + out << "number of indices: " << Name << " _ not in binary" << endl; + + out << "ColorMapFile length: " << ColorMapFileLength << endl; + out << "NormalMapFile length: " << NormalMapFileLength << endl; + out << "SpecularMapFile length: " << SpecularMapFileLength << endl; + out << "IncandescenceMapFile length: " << IncandescenceMapFileLength << endl; + + out << "SpecularExponent: " << SpecularExponent << endl; + out << "ReflectionFactor: " << ReflectionFactor << endl; + out << "DiffuseColor: " << DiffuseColor[0] << " " << DiffuseColor[1] << " " << DiffuseColor[2] << endl; + out << "SpecularColor: " << SpecularColor[0] << " " << SpecularColor[1] << " " << SpecularColor[2] << endl; + out << "IncandescenceColor: " << IncandescenceColor[0] << " " << IncandescenceColor[1] << " " << IncandescenceColor[2] << endl; + out << "IndexStart: " << IndexStart << endl; + out << "IndexEnd: " << IndexEnd << endl; + + if (ColorMapFileLength > 0) + out << "ColorMapFile: " << ColorMapFile << endl; + + if (NormalMapFileLength > 0) + out << "NormalMapFile: " << NormalMapFile << endl; + + if (SpecularMapFileLength > 0) + out << "SpecularMapFile: " << SpecularMapFile << endl; + + if (IncandescenceMapFileLength > 0) + out << "IncandescenceMapFile: " << IncandescenceMapFile << endl; + } +}; + +class Material +{ +public: + Material() {}; + ~Material() {}; + std::vector* DoIt(Mesh mesh); + std::vector* TexturePaths(); +private: + MPlug m_Plug; + + std::vector m_AllMaterials; + std::vector m_TexturePaths; + + bool findColorTexture(MaterialNode& material_node, MFnDependencyNode& node); + bool findNormalTexture(MaterialNode& material_node, MFnDependencyNode& node); + bool findSpecularTexture(MaterialNode& material_node, MFnDependencyNode& node); + bool findIncandescenceTexture(MaterialNode& material_node, MFnDependencyNode& node); + void grabLambertProperties(MaterialNode& material_node, MFnDependencyNode& node); + void grabBlinnProperties(MaterialNode& material_node, MFnDependencyNode& node); + void grabPhongProperties(MaterialNode& material_node, MFnDependencyNode& node); +}; + +#endif // Material_Material_h__ \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/MayaExporter.vcxproj b/tools/MayaExporter/MayaExporter/MayaExporter.vcxproj index 42b1b45a..4a7ac07e 100644 --- a/tools/MayaExporter/MayaExporter/MayaExporter.vcxproj +++ b/tools/MayaExporter/MayaExporter/MayaExporter.vcxproj @@ -140,6 +140,7 @@ MultiThreadedDLL true + Disabled Windows @@ -152,6 +153,8 @@ + + true @@ -172,6 +175,9 @@ true + + + @@ -194,6 +200,11 @@ + + + + + Moc%27ing Menu.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp @@ -213,6 +224,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath) + diff --git a/tools/MayaExporter/MayaExporter/MayaExporter.vcxproj.filters b/tools/MayaExporter/MayaExporter/MayaExporter.vcxproj.filters index d5bcfd00..ae3d2478 100644 --- a/tools/MayaExporter/MayaExporter/MayaExporter.vcxproj.filters +++ b/tools/MayaExporter/MayaExporter/MayaExporter.vcxproj.filters @@ -50,6 +50,21 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -69,5 +84,23 @@ Generated Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/MayaIncludes.h b/tools/MayaExporter/MayaExporter/MayaIncludes.h index 3573d30d..7322bdc5 100644 --- a/tools/MayaExporter/MayaExporter/MayaIncludes.h +++ b/tools/MayaExporter/MayaExporter/MayaIncludes.h @@ -30,6 +30,21 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Wrappers @@ -58,5 +73,6 @@ #pragma comment(lib,"Foundation.lib") #pragma comment(lib,"OpenMaya.lib") #pragma comment(lib,"OpenMayaUI.lib") +#pragma comment (lib, "OpenMayaAnim.lib") #endif \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/Menu.cpp b/tools/MayaExporter/MayaExporter/Menu.cpp index 6af300aa..9cc6de91 100644 --- a/tools/MayaExporter/MayaExporter/Menu.cpp +++ b/tools/MayaExporter/MayaExporter/Menu.cpp @@ -10,223 +10,200 @@ Menu::Menu() Menu::Menu(QDialog* dialog) { // Save the dialog pointer. Needed when the application gets destroyed - dialogPointer = dialog; + m_DialogPointer = dialog; // Create QpushButtons & give them names - exportSelectedButton = new QPushButton("&Export Selected", this); - browseButton = new QPushButton("&...", this); - exportAllButton = new QPushButton("&Export All", this); - cancelButton = new QPushButton("&Cancel", this); + m_BrowseButton = new QPushButton("&...", this); + m_ExportAllButton = new QPushButton("&Export", this); + m_CancelButton = new QPushButton("&Cancel", this); + m_AddClipsButton = new QPushButton("&Add Clips", this); + m_RemoveClipsButton = new QPushButton("&Remove Latest Clip", this); // Option box and checkboxes QGroupBox *optionsBox = new QGroupBox(tr("Options")); - exportAnimationsButton = new QCheckBox(tr("&Export Animations")); - copyTexturesButton = new QCheckBox(tr("&Copy Textures")); - button3 = new QCheckBox(tr("option3")); + m_ExportSelectedButton = new QCheckBox(tr("&Export Selected")); + m_ExportAnimationsButton = new QCheckBox(tr("&Export Animations")); + m_ExportMaterialButton = new QCheckBox(tr("&Export Material"));; - exportAnimationsButton->setChecked(true); - copyTexturesButton->setChecked(true); + m_ExportAnimationsButton->setChecked(true); + m_ExportMaterialButton->setChecked(true); QVBoxLayout *vbox = new QVBoxLayout; - vbox->addWidget(exportAnimationsButton); - vbox->addWidget(copyTexturesButton); - vbox->addWidget(button3); + vbox->addWidget(m_ExportSelectedButton); + vbox->addWidget(m_ExportAnimationsButton); + vbox->addWidget(m_ExportMaterialButton); + vbox->addStretch(1); optionsBox->setLayout(vbox); // Connect the buttons with signals & functions - connect(exportSelectedButton, SIGNAL(clicked(bool)), this, SLOT(ExportSelected(bool))); - connect(browseButton, SIGNAL(clicked(bool)), this, SLOT(ExportPathClicked(bool))); - connect(exportAllButton, SIGNAL(clicked(bool)), this, SLOT(ExportAll(bool))); - connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(CancelClicked(bool))); + connect(m_BrowseButton, SIGNAL(clicked(bool)), this, SLOT(ExportPathClicked(bool))); + connect(m_ExportAllButton, SIGNAL(clicked(bool)), this, SLOT(ExportAll(bool))); + connect(m_CancelButton, SIGNAL(clicked(bool)), this, SLOT(CancelClicked(bool))); + connect(m_AddClipsButton, SIGNAL(clicked(bool)), this, SLOT(AddClipClicked(bool))); + connect(m_RemoveClipsButton, SIGNAL(clicked(bool)), this, SLOT(RemoveClipClicked(bool))); - connect(exportAnimationsButton, SIGNAL(clicked(bool)), this, SLOT(Button1Clicked(bool))); - connect(copyTexturesButton, SIGNAL(clicked(bool)), this, SLOT(Button2Clicked(bool))); - connect(button3, SIGNAL(clicked(bool)), this, SLOT(Button3Clicked(bool))); + connect(m_ExportSelectedButton, SIGNAL(clicked(bool)), this, SLOT(NULL)); + connect(m_ExportAnimationsButton, SIGNAL(clicked(bool)), this, SLOT(NULL)); + connect(m_ExportMaterialButton, SIGNAL(clicked(bool)), this, SLOT(NULL)); // Creating several layouts, adding widgets & adding them to one layout in the end QHBoxLayout* topLayout = new QHBoxLayout; QVBoxLayout* midLayout = new QVBoxLayout; QHBoxLayout* botLayout = new QHBoxLayout; QVBoxLayout* baseLayout = new QVBoxLayout; + QHBoxLayout* clipButtonLayout = new QHBoxLayout; + QHBoxLayout* startEndLabelLayout = new QHBoxLayout; + m_ClipLayout = new QVBoxLayout; - exportPath = new QLineEdit; - fileDialog = new QFileDialog; + + m_ExportPath = new QLineEdit; + m_FileDialog = new QFileDialog; + QString tmpPath("C:/Users/Nickelodion/Desktop/workspace/tacticalZ/assets/models/"); + m_ExportPath->setText(tmpPath); QLabel* exportLabel = new QLabel; exportLabel->setText("Export Path:"); - + QLabel* nameLabel = new QLabel; + nameLabel->setText("Name:"); + QLabel* startLabel = new QLabel; + startLabel->setText("Start:"); + QLabel* endLabel = new QLabel; + endLabel->setText("End:"); + + //exportLabel->setText("Export Path:"); + midLayout->addWidget(optionsBox); topLayout->addWidget(exportLabel); - topLayout->addWidget(exportPath); - topLayout->addWidget(browseButton); + topLayout->addWidget(m_ExportPath); + topLayout->addWidget(m_BrowseButton); - botLayout->addWidget(exportSelectedButton); - botLayout->addWidget(exportAllButton); - botLayout->addWidget(cancelButton); + botLayout->addWidget(m_ExportAllButton); + botLayout->addWidget(m_CancelButton); + + startEndLabelLayout->addWidget(nameLabel); + startEndLabelLayout->addWidget(startLabel); + startEndLabelLayout->addWidget(endLabel); + + clipButtonLayout->addWidget(m_AddClipsButton); + clipButtonLayout->addWidget(m_RemoveClipsButton); baseLayout->addLayout(topLayout); baseLayout->addLayout(midLayout); baseLayout->addSpacing(10); - baseLayout->addLayout(botLayout); + + baseLayout->addSpacing(10); + baseLayout->addLayout(clipButtonLayout); + baseLayout->addLayout(startEndLabelLayout); + baseLayout->addLayout(m_ClipLayout); baseLayout->addStretch(); // Set the layout for our window dialog->setLayout(baseLayout); -} + this->AddClipClicked(true); + //for (unsigned int i = 0; i < 3; i++) { + // this->AddClipClicked(true); + //} -void Menu::ExportSelected(bool checked) -{ - // Retrieving the objects we currently have selected - MSelectionList selected; - MGlobal::getActiveSelectionList(selected); - - // Loop through or list of selection(s) - for (unsigned int i = 0; i < selected.length();i++) - { - MObject object; - selected.getDependNode(i, object); - MFnDependencyNode thisNode(object); - - cout << thisNode.name().asChar() << endl; - GetMeshData(object); - } - if (exportPath->text().isEmpty()) - cout << "Please select a folder." << endl; - else - cout << exportPath->text().toLocal8Bit().constData() << endl; } void Menu::ExportPathClicked(bool) { // Opens up a file dialog. Save/Changes the name in the exportPath - fileDialog->setFileMode(QFileDialog::Directory); - fileDialog->setOption(QFileDialog::ShowDirsOnly); - QString fileName = fileDialog->getExistingDirectory(this, "Select", "/home", QFileDialog::ShowDirsOnly); - exportPath->setText(fileName); + m_FileDialog->setFileMode(QFileDialog::Directory); + m_FileDialog->setOption(QFileDialog::ShowDirsOnly); + QString fileName = m_FileDialog->getExistingDirectory(this, "Select", "/home", QFileDialog::ShowDirsOnly); + m_ExportPath->setText(fileName); +} + +void Menu::AddClipClicked(bool) +{ + QHBoxLayout* tempLayout = new QHBoxLayout; + + QLineEdit* nameLineEdit = new QLineEdit; + QLineEdit* startLineEdit = new QLineEdit; + QLineEdit* endLineEdit = new QLineEdit; + + m_AnimationClipName.push_back(nameLineEdit); + m_StartFrameLines.push_back(startLineEdit); + m_EndFrameLines.push_back(endLineEdit); + + tempLayout->addWidget(nameLineEdit); + tempLayout->addWidget(startLineEdit); + tempLayout->addWidget(endLineEdit); + + m_ClipLayout->addLayout(tempLayout); + //m_ClipLayout->update(); + layouts.push_back(tempLayout); +} + +void Menu::RemoveClipClicked(bool) +{ + if (m_StartFrameLines.size() > 0) { + QLayoutItem* tempWidget;// = m_ClipLayout->itemAt(0); + + for (unsigned int i = 0; i < layouts.size(); i++) { + while ((tempWidget = layouts[layouts.size() - 1]->takeAt(0)) != 0) { + delete tempWidget->widget(); + delete tempWidget; + } + } + + m_ClipLayout->removeItem(tempWidget); + m_ClipLayout->update(); + + layouts.pop_back(); + m_StartFrameLines.pop_back(); + m_EndFrameLines.pop_back(); + m_AnimationClipName.pop_back(); + } } void Menu::ExportAll(bool) { - MDagPath path; - - // Loop through all nodes in the scene - MItDependencyNodes it(MFn::kInvalid); - for (;!it.isDone();it.next()) - { - MObject node = it.thisNode(); - if (node.hasFn(MFn::kMesh)) - { - MFnDependencyNode thisNode(node); + if (m_ExportPath->text().isEmpty()) { + MGlobal::displayError(MString() + "Please select a folder."); + return; + } - cout << thisNode.name().asChar() << endl; - GetMeshData(node); - } - } - if (exportPath->text().isEmpty()) - cout << "Please select a folder." << endl; - else - cout << exportPath->text().toLocal8Bit().constData() << endl; + //Export meshes + if (!m_Export.Meshes(m_ExportPath->text().toLocal8Bit().constData(), m_ExportSelectedButton->isChecked())) { + MGlobal::displayError(MString() + "Could not export mesh"); + return; + } + + if (m_ExportMaterialButton->isChecked()) { + if (!m_Export.Materials(m_ExportPath->text().toLocal8Bit().constData())) { + MGlobal::displayError(MString() + "Could not export materials"); + return; + } + } + + std::vector animations; + for (unsigned int i = 0; i < m_AnimationClipName.size(); i++) { + Export::AnimationInfo thisClip; + thisClip.Name = std::string(m_AnimationClipName[i]->text().toLocal8Bit().constData()); + thisClip.Start = m_StartFrameLines[i]->text().toInt(); + thisClip.End = m_EndFrameLines[i]->text().toInt(); + + animations.push_back(thisClip); + } + if (m_ExportAnimationsButton->isChecked()) { + //Export Animations + if (!m_Export.Animations(m_ExportPath->text().toLocal8Bit().constData(), animations)) { + MGlobal::displayError(MString() + "Could not export animations"); + return; + } + } } void Menu::CancelClicked(bool) { - dialogPointer->close(); -} - -void Menu::Button1Clicked(bool) -{ - if(exportAnimationsButton->isChecked()) - cout << "1 checked!" << endl; - else - cout << "1 unchecked!" << endl; -} - -void Menu::Button2Clicked(bool) -{ - if (copyTexturesButton->isChecked()) - cout << "2 checked!" << endl; - else - cout << "2 unchecked!" << endl; -} - -void Menu::Button3Clicked(bool) -{ - if (button3->isChecked()) - cout << "3 checked!" << endl; - else - cout << "3 unchecked!" << endl; -} - -void Menu::GetMeshData(MObject object) -{ - // In here, we retrieve triangulated polygons from the mesh - MFnMesh mesh(object); - - map> vertexToIndex; - - vector verticesData; - vectorindexArray; - - MIntArray intdexOffsetVertexCount, vertices, triangleList; - MPointArray dummy; - - UINT vertexIndex; - MVector normal; - MPoint pos; - float2 UV; - VertexLayout thisVertex; - - for (MItMeshPolygon meshPolyIter(object); !meshPolyIter.isDone(); meshPolyIter.next()) - { - vector localVertexToGlobalIndex; - meshPolyIter.getVertices(vertices); - - meshPolyIter.getTriangles(dummy, triangleList); - UINT indexOffset = verticesData.size(); - - for (UINT i = 0; i < vertices.length(); i++) - { - vertexIndex = meshPolyIter.vertexIndex(i); - pos = meshPolyIter.point(i); - pos.get(thisVertex.pos); - - meshPolyIter.getNormal(i, normal); - thisVertex.normal[0] = normal[0]; - thisVertex.normal[1] = normal[1]; - thisVertex.normal[2] = normal[2]; - - meshPolyIter.getUV(i, UV); - thisVertex.uv[0] = UV[0]; - thisVertex.uv[1] = UV[1]; - - verticesData.push_back(thisVertex); - localVertexToGlobalIndex.push_back(vertexIndex); - - cout << "Pos: " << thisVertex.pos[0] << "/" << thisVertex.pos[1] << "/" << thisVertex.pos[2] << endl; - cout << "Normals: " << thisVertex.normal[0] << "/" << thisVertex.normal[1] << "/" << thisVertex.normal[2] << endl; - cout << "UV: " << thisVertex.uv[0] << "/" << thisVertex.uv[1] << endl; - } - for (UINT i = 0; i < triangleList.length(); i++) - { - UINT k = 0; - while (localVertexToGlobalIndex[k] != triangleList[i]) - k++; - indexArray.push_back(indexOffset + k); - } - } - -} - -void Menu::exportMaterial(MObject object) -{ - MItDependencyNodes matIt(MFn::kLambert); - - + m_DialogPointer->close(); } Menu::~Menu() @@ -235,5 +212,8 @@ Menu::~Menu() //delete browseButton; //delete exportPath; //delete fileDialog; - fileDialog->~QFileDialog(); + m_FileDialog->~QFileDialog(); + + //delete m_Export; + //delete MaterialHandler; } \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/Menu.h b/tools/MayaExporter/MayaExporter/Menu.h index 44eca702..fb95a953 100644 --- a/tools/MayaExporter/MayaExporter/Menu.h +++ b/tools/MayaExporter/MayaExporter/Menu.h @@ -1,10 +1,9 @@ -#ifndef BUTTONS_H -#define BUTTONS_H +#ifndef Menu_Menu_h__ +#define Menu_Menu_h__ #include #include -#include "MayaIncludes.h" // Qt #pragma comment(lib, "QtCore4") #pragma comment(lib, "QtGui4") @@ -30,13 +29,11 @@ #include #include #include +#include +#include -struct VertexLayout -{ - float pos[3]; - float normal[3]; - float uv[2]; -}; +#include "MayaIncludes.h" +#include "Export.h" class Menu : public QWidget { @@ -45,35 +42,36 @@ public: Menu(QDialog* dialog); ~Menu(); - void GetMeshData(MObject object); - void exportMaterial(MObject object); - private slots: - void ExportSelected(bool checked); void ExportPathClicked(bool); + void AddClipClicked(bool); + void RemoveClipClicked(bool); void ExportAll(bool); void CancelClicked(bool); - void Button1Clicked(bool); - void Button2Clicked(bool); - void Button3Clicked(bool); - - private: Menu(); - QPushButton* exportSelectedButton; - QPushButton* browseButton; - QPushButton* exportAllButton; - QPushButton* cancelButton; + std::vector m_AnimationClipName; + std:: vector m_StartFrameLines; + std::vector m_EndFrameLines; + std::vector layouts; + QVBoxLayout* m_ClipLayout; - QCheckBox* exportAnimationsButton; - QCheckBox* copyTexturesButton; - QCheckBox* button3; + QPushButton* m_BrowseButton = nullptr; + QPushButton* m_ExportAllButton = nullptr; + QPushButton* m_CancelButton = nullptr; + QPushButton* m_AddClipsButton = nullptr; + QPushButton* m_RemoveClipsButton = nullptr; - QLineEdit* exportPath; - QFileDialog* fileDialog; - QDialog* dialogPointer; + QCheckBox* m_ExportSelectedButton = nullptr; + QCheckBox* m_ExportAnimationsButton = nullptr; + QCheckBox* m_ExportMaterialButton = nullptr; + QLineEdit* m_ExportPath = nullptr; + QFileDialog* m_FileDialog = nullptr; + QDialog* m_DialogPointer = nullptr; + + Export m_Export; }; #endif \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/Mesh.cpp b/tools/MayaExporter/MayaExporter/Mesh.cpp new file mode 100644 index 00000000..6af8a557 --- /dev/null +++ b/tools/MayaExporter/MayaExporter/Mesh.cpp @@ -0,0 +1,402 @@ +#pragma once +#include "Mesh.h" + +using namespace std; + +MeshClass::MeshClass() +{ + +} + +std::map MeshClass::GetWeightData() +{ + MS status; + map weightMap; + + MItDependencyNodes it(MFn::kSkinClusterFilter); + + while (!it.isDone()) { + + MObject object = it.thisNode(&status); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + " it.thisNode() ERROR: " + status.errorString()); + break; + } + MFnSkinCluster skinCluster(object, &status); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "skinCluster() ERROR: " + status.errorString()); + break; + } + MDagPathArray influences; + + unsigned int nrOfInfluences = skinCluster.influenceObjects(influences,&status); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "skinCluster.influenceObjects() ERROR: " + status.errorString()); + break; + } + + unsigned int index; + index = skinCluster.indexForOutputConnection(0,&status); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "skinCluster.indexForOutputConnection() ERROR: " + status.errorString()); + break; + } + MDagPath skinPath; + status = skinCluster.getPathAtIndex(index, skinPath); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "skinCluster.getPathAtIndex() ERROR: " + status.errorString()); + break; + } + + MItGeometry geomIter(skinPath); + //for (unsigned int i = 0; i < nrOfInfluences; i++) { + // MGlobal::displayInfo(MString() + " Influence object name: " + influences[i].partialPathName().asChar()); + //} + WeightInfo weightInfo; + + while (!geomIter.isDone()) { + MObject comp = geomIter.component(&status); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "geomIter.component() ERROR: " + status.errorString()); + break; + } + MFloatArray weights; + unsigned int influenceCount; + status = skinCluster.getWeights(skinPath, comp, weights, influenceCount); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "skinCluster.getWeights() ERROR: " + status.errorString()); + break; + } + MFnDependencyNode test(comp); + unsigned int nrOfWeights = 0; + + for (unsigned int j = 0; j < weights.length() && nrOfWeights != 4; j++) { + if (weights[j] > 0.00001) { + weightInfo.BoneWeights[nrOfWeights] = weights[j]; + weightInfo.BoneIndices[nrOfWeights] = j; + nrOfWeights++; + } + } + + float totalWeight = 0.0f; + for (unsigned int i = 0; i < 4; i++) { + totalWeight += weightInfo.BoneWeights[i]; + } + for (unsigned int i = 0; i < 4; i++) { + weightInfo.BoneWeights[i] /= totalWeight; + } + weightMap[geomIter.index()] = weightInfo; + + + for (unsigned int k = 0; k!=nrOfWeights; k++) { + MGlobal::displayInfo(MString() + "influence: " + weightInfo.BoneIndices[k] + " weight: " + weightInfo.BoneWeights[k]); + } + geomIter.next(); + } + it.next(); + } + return weightMap; +} + +Mesh MeshClass::GetMeshData(MObjectArray object) +{ + MS status; + Mesh newMesh; + vector& vertexList = newMesh.Vertices; + map>& indexLists = newMesh.Indices; + for (int ObjectID = 0; ObjectID < object.length(); ObjectID++) { + if (!object[ObjectID].hasFn(MFn::kMesh)) + continue; + + MObject node = object[ObjectID]; + MFnDependencyNode thisNode(node); + MPlugArray connections; + thisNode.findPlug("inMesh").connectedTo(connections, true, true); + MGlobal::displayInfo(MString() + "inMesh"); + bool hasSkin = false; + MPlug weightList, weights; + MObject weightListObject; + for (unsigned int i = 0; i < connections.length(); i++) { + if (connections[i].node().apiType() == MFn::kSkinClusterFilter) { + MFnSkinCluster skinCluster(connections[i].node()); + weightList = skinCluster.findPlug("weightList", &status); + weightListObject = weightList.attribute(); + weights = skinCluster.findPlug("weights"); + hasSkin = true; + break; + } + } + + + // In here, we retrieve triangulated polygons from the mesh + MFnMesh mesh(object[ObjectID]); + MDagPathArray dagPaths; + status = MDagPath::getAllPathsTo(object[ObjectID], dagPaths); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "MDagPath::getAllPathsTo() ERROR: " + status.errorString()); + break; + } + + for (int pathID = 0; pathID < dagPaths.length(); pathID++) { + MGlobal::displayInfo(dagPaths[pathID].fullPathName()); + MDagPath thisMeshPath(dagPaths[pathID]); + + MMatrix transformMatrix = thisMeshPath.inclusiveMatrix(&status); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "thisMeshPath.inclusiveMatrix() ERROR: " + status.errorString() + " for " + thisMeshPath.fullPathName()); + break; + } + + map> vertexToIndex;; + + MIntArray intdexOffsetVertexCount, vertices, triangleList; + MPointArray dummy; + unsigned int vertexIndex; + MVector normal; + MPoint pos; + float2 UV; + double biTangent[3]; + double biNormal[3]; + MFloatVectorArray Tangents; + MFloatVectorArray biNormals; + + MObjectArray shaderList; + MIntArray shaderIndexList; + status = mesh.getConnectedShaders(0, shaderList, shaderIndexList); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "mesh.getConnectedShaders() ERROR: " + status.errorString() + " for " + thisMeshPath.fullPathName()); + break; + } + + if (shaderList.length() == 0) { + MGlobal::displayError(MString() + "Object: \"" + thisMeshPath.fullPathName() + "\" have no material and will not be exported"); + break; + } + map> materialFaceIDs; + MGlobal::displayInfo(MString() + "shaderIndexList: " + shaderIndexList.length()); + MGlobal::displayInfo(MString() + "shaderList: " + shaderList.length()); + MPlugArray plugArray; + for (int i = 0; i < shaderIndexList.length(); i++) { + MFnDependencyNode shader(shaderList[shaderIndexList[i]]); + MPlug p_Plug = shader.findPlug("surfaceShader", status); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "shader.findPlug(\"surfaceShader\") ERROR: " + status.errorString() + " for " + thisMeshPath.fullPathName()); + continue; + } + if (p_Plug.connectedTo(plugArray, true, false, &status)) { + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "p_Plug.connectedTo() ERROR in if: " + status.errorString() + " for " + thisMeshPath.fullPathName()); + continue; + } + MFnDependencyNode node = plugArray[0].node(); + materialFaceIDs[node.name().asChar()].push_back(i); + } + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "p_Plug.connectedTo() ERROR: " + status.errorString() + " for " + thisMeshPath.fullPathName()); + continue; + } + } + + // map vertexWeights = GetWeightData(); + status = mesh.getTangents(Tangents, MSpace::kObject); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "mesh.getTangents ERROR: " + status.errorString() + " for " + thisMeshPath.fullPathName()); + continue; + } + status = mesh.getBinormals(biNormals, MSpace::kObject); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "mesh.getBinormals ERROR: " + status.errorString() + " for " + thisMeshPath.fullPathName()); + continue; + } + if(Tangents.length() == 0 || biNormals.length() == 0){ + MGlobal::displayError(MString() + "Unknown ERROR with " + thisMeshPath.fullPathName()); + continue; + } + MItMeshFaceVertex faceVert(object[ObjectID]); + int intDummy = 0; + + MItMeshPolygon meshPolyIter(object[ObjectID]); + MFloatPointArray positions; + + mesh.getPoints(positions); + + for (auto aMaterial : materialFaceIDs) { + for (auto faceID : aMaterial.second) { + vector> localVertexToGlobalIndex; + + status = meshPolyIter.setIndex(faceID, intDummy); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + " meshPolyIter.setIndex() ERROR: " + status.errorString() + " for faceID " + faceID + " in mesh " + thisMeshPath.fullPathName()); + break; + } + + status = meshPolyIter.getVertices(vertices); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + " meshPolyIter.getVertices() ERROR: " + status.errorString() + " for faceID " + faceID + " in mesh " + thisMeshPath.fullPathName()); + break; + } + + status = meshPolyIter.getTriangles(dummy, triangleList); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + " meshPolyIter.getTriangles() ERROR: " + status.errorString() + " for faceID " + faceID + " in mesh " + thisMeshPath.fullPathName()); + break; + } + //MGlobal::displayInfo("Befor Second Loop"); + for (unsigned int i = 0; i < vertices.length(); i++) { + VertexLayout thisVertex; + + vertexIndex = meshPolyIter.vertexIndex(i, &status); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + " meshPolyIter.vertexIndex() ERROR: " + status.errorString() + "for local vertex " + i + " in " + faceID + " in mesh " + thisMeshPath.fullPathName()); + break; + } + + status = faceVert.setIndex(meshPolyIter.index(), i, intDummy, intDummy); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "faceVert.setIndex() ERROR: " + status.errorString() + "for local vertex " + i + " in " + faceID + " in mesh " + thisMeshPath.fullPathName()); + break; + } + //MGlobal::displayInfo("In Second Loop"); + //pos = faceVert.position(MSpace::kTransform); + //mesh.getPoint(vertexIndex, pos, MSpace::kPostTransform); + pos = positions[vertexIndex]; + pos = pos * transformMatrix; + if (abs(pos.x) > 0.0001) + thisVertex.Pos[0] = pos.x; + if (abs(pos.y) > 0.0001) + thisVertex.Pos[1] = pos.y; + if (abs(pos.z) > 0.0001) + thisVertex.Pos[2] = pos.z; + + status = faceVert.getNormal(normal, MSpace::kObject); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "faceVert.getNormal() ERROR: " + status.errorString() + "for local vertex " + i + " in " + faceID + " in mesh " + thisMeshPath.fullPathName()); + break; + } + if (abs(normal[0]) > 0.0001) + thisVertex.Normal[0] = normal[0]; + if (abs(normal[1]) > 0.0001) + thisVertex.Normal[1] = normal[1]; + if (abs(normal[2]) > 0.0001) + thisVertex.Normal[2] = normal[2]; + + MFloatVector Tangent = Tangents[faceVert.tangentId()]; + //MVector tmp = faceVert.getTangent(MSpace::kObject, NULL); + //tmp.get(biTangent); + if (abs(Tangent[0]) > 0.0001) + thisVertex.Tangent[0] = Tangent[0]; + if (abs(Tangent[1]) > 0.0001) + thisVertex.Tangent[1] = Tangent[1]; + if (abs(Tangent[2]) > 0.0001) + thisVertex.Tangent[2] = Tangent[2]; + + MFloatVector biNormal = biNormals[faceVert.tangentId()]; + //faceVert.getBinormal().get(biNormal); + if (abs(biNormal[0]) > 0.0001) + thisVertex.BiNormal[0] = biNormal[0]; + if (abs(biNormal[1]) > 0.0001) + thisVertex.BiNormal[1] = biNormal[1]; + if (abs(biNormal[2]) > 0.0001) + thisVertex.BiNormal[2] = biNormal[2]; + + status = faceVert.getUV(UV); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + " faceVert.getUV() ERROR: " + status.errorString() + "for local vertex " + i + " in " + faceID + " in mesh " + thisMeshPath.fullPathName()); + break; + } + thisVertex.Uv[0] = UV[0]; + thisVertex.Uv[1] = UV[1]; + + + if (hasSkin) { + float totalWeight = 0.0f; + unsigned int totalBones = 0; + MIntArray jointIDs /* ??? */; + weights.selectAncestorLogicalIndex(vertexIndex, weightListObject); + weights.getExistingArrayAttributeIndices(jointIDs); + for (unsigned int i = 0; i < jointIDs.length() && i < 4; i++) { + if (weights[i].asFloat() > 0.001f) { + thisVertex.BoneIndices[totalBones] = jointIDs[i]; + thisVertex.BoneWeights[totalBones] = weights[i].asFloat(); + totalWeight = totalWeight + weights[i].asFloat(); + totalBones++; + } + } + + for (unsigned int i = 0; i < 4; i++) { + thisVertex.BoneWeights[i] = thisVertex.BoneWeights[i] / totalWeight; + } + } + + //float totalWeight = thisVertex.BoneWeights[0] + thisVertex.BoneWeights[1] + thisVertex.BoneWeights[2] + thisVertex.BoneWeights[3]; + //if (totalWeight > 0.0001f) { + // thisVertex.BoneWeights[0] /= totalWeight; + // thisVertex.BoneWeights[1] /= totalWeight; + // thisVertex.BoneWeights[2] /= totalWeight; + // thisVertex.BoneWeights[3] /= totalWeight; + //} + + std::vector::iterator it = std::find(vertexList.begin(), vertexList.end(), thisVertex); + array tmp; + if (it != vertexList.end()) { + tmp[0] = vertexIndex; + tmp[1] = it - vertexList.begin(); + localVertexToGlobalIndex.push_back(tmp); + } else { + tmp[0] = vertexIndex; + tmp[1] = vertexList.size(); + localVertexToGlobalIndex.push_back(tmp); + vertexList.push_back(thisVertex); + } + //MGlobal::displayInfo(MString() + "localVertexToGlobalIndex[localVertexToGlobalIndex.size()-1]: " + localVertexToGlobalIndex[localVertexToGlobalIndex.size()-1][0] + " " + localVertexToGlobalIndex[localVertexToGlobalIndex.size()-1][1]); + //cout << "Pos: " << thisVertex.Pos[0] << "/" << thisVertex.Pos[1] << "/" << thisVertex.Pos[2] << endl; + //cout << "Normals: " << thisVertex.Normal[0] << "/" << thisVertex.Normal[1] << "/" << thisVertex.Normal[2] << endl; + //cout << "Bi-Normals: " << thisVertex.BiNormal[0] << "/" << thisVertex.BiNormal[1] << "/" << thisVertex.BiNormal[2] << endl; + //cout << "Bi-Tangents: " << thisVertex.BiTangent[0] << "/" << thisVertex.BiTangent[1] << "/" << thisVertex.BiTangent[2] << endl; + //cout << "UV: " << thisVertex.Uv[0] << "/" << thisVertex.Uv[1] << endl; + } + for (unsigned int i = 0; i < triangleList.length(); i++) { + unsigned int k = 0; + if (localVertexToGlobalIndex.size() > 0) { + //MGlobal::displayInfo(MString() + "triangleList[i] : " + triangleList[i]); + while (localVertexToGlobalIndex[k][0] != triangleList[i] && k < localVertexToGlobalIndex.size()) { + k++; + } + //MGlobal::displayInfo(MString() + "localVertexToGlobalIndex[k] : " + localVertexToGlobalIndex[k][0] + " " + localVertexToGlobalIndex[k][1]); + indexLists[aMaterial.first.c_str()].push_back(localVertexToGlobalIndex[k][1]); + } + } + } + // MGlobal::displayInfo( MString() + "localVertexToGlobalIndex.size(): " + localVertexToGlobalIndex.size()); + // if (localVertexToGlobalIndex.size() > 0) { + // MGlobal::displayInfo(MString() + "triangleList.length(): " + triangleList.length()); + // for (unsigned int i = triangleList.length() - 1; i >= 0; i--) { + // MGlobal::displayInfo(MString() + "i: " + i); + // unsigned int k = localVertexToGlobalIndex.size() - 1; + // MGlobal::displayInfo(MString() + "triangleList[i] : " + triangleList[i]); + // while (localVertexToGlobalIndex[k] != triangleList[i] && k >= 0) { + // MGlobal::displayInfo(MString() + "k: " + k); + // k--; + // } + // MGlobal::displayInfo(MString() + "localVertexToGlobalIndex[k] : " + localVertexToGlobalIndex[k]); + // indexList.push_back(indexOffset + k); + // } + // } + } + } + } + + int totalIndices = 0; + for (auto aList : newMesh.Indices) { + totalIndices += aList.second.size(); + } + newMesh.NumIndices = totalIndices; + newMesh.NumVertices = newMesh.Vertices.size(); + + return newMesh; +} + +MeshClass::~MeshClass() +{ + +} \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/Mesh.h b/tools/MayaExporter/MayaExporter/Mesh.h new file mode 100644 index 00000000..11f6bcf8 --- /dev/null +++ b/tools/MayaExporter/MayaExporter/Mesh.h @@ -0,0 +1,117 @@ +#ifndef Mesh_Mesh_h__ +#define Mesh_Mesh_h__ + +#include +#include +#include + +#include "OutputData.h" +#include "MayaIncludes.h" + +class VertexLayout : public OutputData +{ +public: + float Pos[3]{ 0 }; + float Normal[3]{ 0 }; + float Tangent[3]{ 0 }; + float BiNormal[3]{ 0 }; + float Uv[2]{ 0 }; + float BoneIndices[4]{ 0 }; + float BoneWeights[4]{ 0 }; + + virtual void WriteBinary(std::ostream& out) + { + out.write((char*)&Pos, sizeof(float) * 3); + out.write((char*)&Normal, sizeof(float) * 3); + out.write((char*)&Tangent, sizeof(float) * 3); + out.write((char*)&BiNormal, sizeof(float) * 3); + out.write((char*)&Uv, sizeof(float) * 2); + out.write((char*)&BoneIndices, sizeof(float) * 4); + out.write((char*)&BoneWeights, sizeof(float) * 4); + } + + virtual void WriteASCII(std::ostream& out) const + { + out << Pos[0] << " " << Pos[1] << " " << Pos[2] << endl; + out << Normal[0] << " " << Normal[1] << " " << Normal[2] << endl; + out << Tangent[0] << " " << Tangent[1] << " " << Tangent[2] << endl; + out << BiNormal[0] << " " << BiNormal[1] << " " << BiNormal[2] << endl; + out << Uv[0] << " " << Uv[1] << endl; + out << BoneIndices[0] << " " << BoneIndices[1] << " " << BoneIndices[2] << " " << BoneIndices[3] << endl; + out << BoneWeights[0] << " " << BoneWeights[1] << " " << BoneWeights[2] << " " << BoneWeights[3] << endl; + + } + bool operator==(const VertexLayout& right) + { + return + this->Pos[0] == right.Pos[0] && this->Pos[1] == right.Pos[1] && this->Pos[2] == right.Pos[2] && + this->Normal[0] == right.Normal[0] && this->Normal[1] == right.Normal[1] && this->Normal[2] == right.Normal[2] && + this->Tangent[0] == right.Tangent[0] && this->Tangent[1] == right.Tangent[1] && this->Tangent[2] == right.Tangent[2] && + this->BiNormal[0] == right.BiNormal[0] && this->BiNormal[1] == right.BiNormal[1] && this->BiNormal[2] == right.BiNormal[2] && + this->Uv[0] == right.Uv[0] && this->Uv[1] == right.Uv[1] && + this->BoneIndices[0] == right.BoneIndices[0] && this->BoneIndices[1] == right.BoneIndices[1] && this->BoneIndices[2] == right.BoneIndices[2] && this->BoneIndices[3] == right.BoneIndices[3] && + this->BoneWeights[0] == right.BoneWeights[0] && this->BoneWeights[1] == right.BoneWeights[1] && this->BoneWeights[2] == right.BoneWeights[2] && this->BoneWeights[3] == right.BoneWeights[3] + ; + } +}; + +class Mesh : public OutputData { +public: + unsigned int NumVertices; + unsigned int NumIndices; + std::vector Vertices; + std::map> Indices; + + virtual void WriteBinary(std::ostream& out) + { + out.write((char*)&NumVertices, sizeof(int)); + out.write((char*)&NumIndices, sizeof(int)); + for (auto aVertex : Vertices) { + aVertex.WriteBinary(out); + } + for (auto aIndex : Indices) { + //for (std::map>::reverse_iterator aIndex = Indices.rbegin(); aIndex != Indices.rend(); aIndex++){ + //out.write((char*)(*aIndex).second.data(), sizeof(int) * (*aIndex).second.size()); + out.write((char*)aIndex.second.data(), sizeof(int) * aIndex.second.size()); + } + } + + virtual void WriteASCII(std::ostream& out) const + { + out << "New Mesh _ not in binary" << endl; + out << "Number of vertices: " << NumVertices << endl; + out << "number of indices: " << NumIndices << endl; + int vertexNumber = 0; + for (auto aVertex : Vertices) { + out << "New vertex number: " << vertexNumber << " _ not in binary" << endl; + aVertex.WriteASCII(out); + vertexNumber++; + } + out << "New vertex Triangels: " << NumIndices/3 << " _ not in binary" << endl; + for (auto aIndexList : Indices) { + out << "Using Material: " << aIndexList.first << " _ not in binary" << endl; + for (int i = 0; i < aIndexList.second.size(); i += 3) { + out << aIndexList.second[i] << " " << aIndexList.second[i+1] << " " << aIndexList.second[i + 2] << endl; + } + } + } +}; + + + +class MeshClass +{ +public: + MeshClass(); + Mesh GetMeshData(MObjectArray Object); + ~MeshClass(); +private: + struct WeightInfo { + float BoneIndices[4] = { 0 }; + float BoneWeights[4] = { 0 }; + }; + std::map GetWeightData(); + +}; + +#endif \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/OutputData.h b/tools/MayaExporter/MayaExporter/OutputData.h new file mode 100644 index 00000000..60159340 --- /dev/null +++ b/tools/MayaExporter/MayaExporter/OutputData.h @@ -0,0 +1,23 @@ +#ifndef OutputData_OutputData_h__ +#define OutputData_OutputData_h__ +#include +//template +class OutputData +{ +public://std::ostream& out, const OutputData& obj + //OutputData(T& object) + // : m_Object(object) + //{}; + + friend std::ostream& operator<<(std::ostream& out, const OutputData& obj) + { + obj.WriteASCII(out); + return out; + }; + virtual void WriteBinary(std::ostream& out) = 0; + virtual void WriteASCII(std::ostream& out) const = 0; + + //T& m_Object = nullptr; +}; + +#endif \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/Skeleton.cpp b/tools/MayaExporter/MayaExporter/Skeleton.cpp new file mode 100644 index 00000000..c30311b8 --- /dev/null +++ b/tools/MayaExporter/MayaExporter/Skeleton.cpp @@ -0,0 +1,342 @@ +#include "Skeleton.h" + + + + +//std::vector Skeleton::DoIt() +//{ +// std::vector m_AllSkeletons; +// +// MItDag jointIt(MItDag::TraversalType::kDepthFirst, MFn::kJoint); +// SkeletonNode SkeletonStorage; +// +// while (!jointIt.isDone()) { +// MFnTransform TransformNode(jointIt.currentItem()); +// Joint NewJoint; +// +// if (MFnDependencyNode(TransformNode.parent(0)).name() == "world") { +// if (SkeletonStorage.Joints.size() != 0) { +// m_AllSkeletons.push_back(SkeletonStorage); +// +// SkeletonStorage.Joints.clear(); +// SkeletonStorage.Name.clear(); +// } +// +// SkeletonStorage.Name = TransformNode.name().asChar(); +// +// //NewJoint.ParentIndex = -1; // This joint is root +// } +// +// //NewJoint.Name = TransformNode.name().asChar(); +// +// MMatrix Matrix = TransformNode.transformationMatrix(); +// +// //double tmp[3]; +// //((MTransformationMatrix)Matrix).eulerRotation().asVector().get(tmp); +// //NewJoint.Rotation[0] = tmp[0]; +// //NewJoint.Rotation[1] = tmp[1]; +// //NewJoint.Rotation[2] = tmp[2]; +// //((MTransformationMatrix)Matrix).getScale(tmp, MSpace::Space::kTransform); +// //NewJoint.Scale[0] = tmp[0]; +// //NewJoint.Scale[1] = tmp[1]; +// //NewJoint.Scale[2] = tmp[2]; +// //((MTransformationMatrix)Matrix).getTranslation(MSpace::Space::kTransform).get(tmp); +// //NewJoint.Translation[0] = tmp[0]; +// //NewJoint.Translation[1] = tmp[1]; +// //NewJoint.Translation[2] = tmp[2]; +// +// for (int i = 0; i < 4; i++) { +// for (int j = 0; j < 4; j++) { +// NewJoint.OffsetMatrix[i][j] = Matrix.matrix[i][j]; +// } +// } +// +// SkeletonStorage.Joints.push_back(NewJoint); +// +// jointIt.next(); +// } +// +// m_AllSkeletons.push_back(SkeletonStorage); +// +// return m_AllSkeletons; +//} +std::string attr[9] = { "scaleX", "scaleY", "scaleZ", "translateX", "translateY", "translateZ", "rotateX", "rotateY", "rotateZ" }; + +Animation Skeleton::GetAnimData(std::string animationName, int startFrame, int endFrame) +{ + MStatus status; + std::vector animatedJoints; + std::vector m_Hierarchy; + + Animation returnData; + double oneDivSixty = 1 / 60.0; + returnData.Name = animationName; + returnData.nameLength = animationName.size() + 1; + returnData.Duration = (endFrame - startFrame) * oneDivSixty; + + MItDag jointIt(MItDag::TraversalType::kDepthFirst, MFn::kJoint); + while (!jointIt.isDone()) + { + m_Hierarchy.push_back(jointIt.item()); + + MFnDependencyNode depNode(jointIt.item()); + for (int i = 0; i < 9; i++) + { + MStatus tmp; + MPlug plug = depNode.findPlug(attr[i].c_str(), &tmp); + + MPlugArray connections; + plug.connectedTo(connections, true, false, 0); + for (int j = 0; j != connections.length(); j++) { + MObject connected = connections[j].node(); + + if (connected.hasFn(MFn::kAnimCurve)) { + + MFnAnimCurve jointAnim(connected); + + unsigned int startKeyFrameIndex = jointAnim.findClosest(MTime(startFrame, MTime::kNTSCField), &tmp); + + if (tmp == MStatus::kFailure) + MGlobal::displayInfo(MString() + "Fail :c"); + + if (startFrame * oneDivSixty <= jointAnim.time(startKeyFrameIndex).value() && jointAnim.time(startKeyFrameIndex).value() <= endFrame * oneDivSixty) { + animatedJoints.push_back(jointIt.item()); + i = 9; + break; + } + + unsigned int endKeyFrameIndex = jointAnim.findClosest(MTime(endFrame, MTime::kNTSCField)); + MGlobal::displayInfo(MString() + startKeyFrameIndex + " " + endKeyFrameIndex); + + if (startFrame * oneDivSixty <= jointAnim.time(endKeyFrameIndex).value() && jointAnim.time(endKeyFrameIndex).value() <= endFrame * oneDivSixty || endKeyFrameIndex - startKeyFrameIndex > 0) { + animatedJoints.push_back(jointIt.item()); + i = 9; + break; + } + + MFnTransform MayaJoint(jointIt.item()); + + MPlug BindPose = MayaJoint.findPlug("bindPose"); + MDataHandle DataHandle; + BindPose.getValue(DataHandle); + MFnMatrixData MartixFn(DataHandle.data()); + MMatrix BindPoseMatrix = MartixFn.matrix(); + + if (!BindPoseMatrix.isEquivalent(MayaJoint.transformationMatrix())) + { + MGlobal::displayError(MString() + animationName.c_str() + " is using " + MayaJoint.name() + " that is not in bind pose nor is it key framed in the animation, the exported animation will NOT correspond to the animation in Maya"); + } + } + } + } + + jointIt.next(); + } + + int currentFrame = startFrame; + while (currentFrame != endFrame + 1) { // ANDREAS + Animation::Keyframe thisKeyFrame; + thisKeyFrame.Index = currentFrame - startFrame; + thisKeyFrame.Time = thisKeyFrame.Index * oneDivSixty; + + MAnimControl::setCurrentTime(MTime(currentFrame, MTime::kNTSCField)); + MTime time = MAnimControl::currentTime(); + + for (auto aJoint : animatedJoints){ + MFnTransform thisJoint(aJoint); + Animation::Keyframe::JointProperty joint; + + auto it = std::find(m_Hierarchy.begin(), m_Hierarchy.end(), thisJoint.object()); + if (it != m_Hierarchy.end()) { + joint.ID = it - m_Hierarchy.begin(); + } + else { + MGlobal::displayError(MString() + "Could not find joint ID for: " + thisJoint.name()); + } + + MTransformationMatrix Matrix = thisJoint.transformation(); + MPlug BindPose = thisJoint.findPlug("bindPose"); + MDataHandle DataHandle; + BindPose.getValue(DataHandle); + MFnMatrixData MartixFn(DataHandle.data()); + MMatrix BindPoseMatrix = MartixFn.matrix(); + Matrix = Matrix.asMatrix(); + + MObject jointOrientObj = thisJoint.attribute("jointOrient"); + MFnNumericAttribute jointOrient(jointOrientObj); + double jointOrientDouble[3]; + jointOrient.getDefault(jointOrientDouble[0], jointOrientDouble[1], jointOrientDouble[2]); + //MGlobal::displayError(MString() + "Joint Matrix: "); + //MGlobal::displayError(MString() + Matrix.asMatrix()[0][0] + " " + Matrix.asMatrix()[0][1] + " " + Matrix.asMatrix()[0][2] + " " + Matrix.asMatrix()[0][3]); + //MGlobal::displayError(MString() + Matrix.asMatrix()[1][0] + " " + Matrix.asMatrix()[1][1] + " " + Matrix.asMatrix()[1][2] + " " + Matrix.asMatrix()[1][3]); + //MGlobal::displayError(MString() + Matrix.asMatrix()[2][0] + " " + Matrix.asMatrix()[2][1] + " " + Matrix.asMatrix()[2][2] + " " + Matrix.asMatrix()[2][3]); + //MGlobal::displayError(MString() + Matrix.asMatrix()[3][0] + " " + Matrix.asMatrix()[3][1] + " " + Matrix.asMatrix()[3][2] + " " + Matrix.asMatrix()[3][3]); + + MEulerRotation joEuler(jointOrientDouble[0], jointOrientDouble[1], jointOrientDouble[2]); + MQuaternion jo = joEuler.asQuaternion(); + + double tmp[4]; + Matrix.getRotationQuaternion(tmp[0], tmp[1], tmp[2], tmp[3]); + MQuaternion rotation(tmp); + + rotation = rotation * jo; + rotation.get(tmp); + + joint.Rotation[0] = tmp[0]; + joint.Rotation[1] = tmp[1]; + joint.Rotation[2] = tmp[2]; + joint.Rotation[3] = tmp[3]; + Matrix.getTranslation(MSpace::kTransform).get(tmp); + joint.Position[0] = tmp[0]; + joint.Position[1] = tmp[1]; + joint.Position[2] = tmp[2]; + Matrix.getScale(tmp, MSpace::kTransform); + joint.Scale[0] = tmp[0]; + joint.Scale[1] = tmp[1]; + joint.Scale[2] = tmp[2]; + + thisKeyFrame.JointProperties.push_back(joint); + } + returnData.Keyframes.push_back(thisKeyFrame); + currentFrame++; + } + + returnData.NumKeyFrames = returnData.Keyframes.size(); + returnData.NumberOfJoints = animatedJoints.size(); + + return returnData; +} + +std::vector Skeleton::GetBindPoses() +{ + MStatus status; + std::vector m_AllSkeletons; + std::vector m_Hierarchy; + + MItDag jointIt(MItDag::TraversalType::kDepthFirst, MFn::kJoint); + BindPoseSkeletonNode SkeletonStorage; + while (!jointIt.isDone()) { + MFnTransform MayaJoint(jointIt.currentItem()); + BindPoseSkeletonNode::BindPoseJoint NewJoint; + + if (MFnDependencyNode(MayaJoint.parent(0)).name() == "world") { + if (SkeletonStorage.Joints.size() != 0) { + m_AllSkeletons.push_back(SkeletonStorage); + + SkeletonStorage.Joints.clear(); + SkeletonStorage.Name.clear(); + } + SkeletonStorage.Name = std::string(MayaJoint.name().asChar()); + NewJoint.ParentID = -1; // This joint is root + } + else { + auto it = std::find(m_Hierarchy.begin(), m_Hierarchy.end(), MayaJoint.parent(0)); + if (it != m_Hierarchy.end()) { + NewJoint.ParentID = it - m_Hierarchy.begin(); + } + else { + MGlobal::displayError(MString() + "Could not find joint parent for: " + MayaJoint.name()); + } + } + m_Hierarchy.push_back(MayaJoint.object()); + + MPlug BindPose = MayaJoint.findPlug("bindPose", &status); + if (status != MS::kSuccess) { + MGlobal::displayError(MString() + "Could not find bindPose plug: " + status.errorString()); + } + MDataHandle DataHandle; + BindPose.getValue(DataHandle); + MFnMatrixData MartixFn(DataHandle.data()); + MMatrix Matrix = MartixFn.matrix(); + + + MVector tmp = MayaJoint.transformation().getTranslation(MSpace::kObject); + MGlobal::displayError(MString() + "translation befor: " + tmp[0] + " " + tmp[1] + " " + tmp[2]); + //Matrix[3][0] *= -1; + //Matrix[3][2] *= -1; + //Matrix[3][1] *= -1; + + double test[3]; + MayaJoint.transformation().getScale(test, MSpace::kObject); + MGlobal::displayError(MString() + "scale: " + test[0] + " " + test[1] + " " + test[2]); + + MTransformationMatrix::RotationOrder order = MTransformationMatrix::RotationOrder::kXYZ; + MayaJoint.transformation().getRotation(test, order); + MGlobal::displayError(MString() + "rotation: " + test[0] + " " + test[1] + " " + test[2]); + + //----- test + + + //MDataHandle DataHandle; + //MObject jointObject(jointIt.currentItem()); + //MFnDependencyNode jointDependNode(jointObject); + //MPlug worldMatrixArray(jointObject, jointDependNode.attribute("worldMatrix")); + + //MMatrix Matrix; + //for (int i = 0; i < worldMatrixArray.numElements(); i++) { + // MPlugArray connections; + + // MPlug element = worldMatrixArray[i]; + // unsigned int logicalIndex = element.logicalIndex(); + + // MItDependencyGraph it(element, MFn::kSkinClusterFilter); + + // for (; !it.isDone(); it.next()) { + // MFnSkinCluster skinCluster(it.thisNode()); + + // MPlug bindPreMatrixArrayPlug = + // skinCluster.findPlug("bindPreMatrix", &status); + + // if (status != MS::kSuccess) { + // MGlobal::displayError(MString() + "Could not find bindPreMatrix plug: " + status.errorString()); + // break; + // } + + // MPlug bindPreMatrixPlug = + // bindPreMatrixArrayPlug.elementByLogicalIndex(logicalIndex); + // MObject dataObject; + // bindPreMatrixPlug.getValue(dataObject); + + // MFnMatrixData matDataFn(dataObject); + + // MMatrix invMat = matDataFn.matrix(); + // Matrix = invMat.inverse(); + // } + //} + + + //----- end test + + + Matrix = Matrix.inverse(); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + NewJoint.OffsetMatrix[i][j] = Matrix.matrix[i][j]; + } + } + + NewJoint.Name = MayaJoint.name().asChar(); + NewJoint.NameLength = MayaJoint.name().length() + 1; + NewJoint.ID = SkeletonStorage.Joints.size(); + //double tmp[3]; + //((MTransformationMatrix)Matrix).eulerRotation().asVector().get(tmp); + //NewJoint.Rotation[0] = tmp[0]; + //NewJoint.Rotation[1] = tmp[1]; + //NewJoint.Rotation[2] = tmp[2]; + //((MTransformationMatrix)Matrix).getScale(tmp, MSpace::Space::kTransform); + //NewJoint.Scale[0] = tmp[0]; + //NewJoint.Scale[1] = tmp[1]; + //NewJoint.Scale[2] = tmp[2]; + //((MTransformationMatrix)Matrix).getTranslation(MSpace::Space::kTransform).get(tmp); + //NewJoint.Translation[0] = tmp[0]; + //NewJoint.Translation[1] = tmp[1]; + //NewJoint.Translation[2] = tmp[2]; + SkeletonStorage.Joints.push_back(NewJoint); + SkeletonStorage.numBones++; + jointIt.next(); + } + m_AllSkeletons.push_back(SkeletonStorage); + + return m_AllSkeletons; +} \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/Skeleton.h b/tools/MayaExporter/MayaExporter/Skeleton.h new file mode 100644 index 00000000..6a288c8a --- /dev/null +++ b/tools/MayaExporter/MayaExporter/Skeleton.h @@ -0,0 +1,129 @@ +#ifndef Skeleton_Skeleton_h__ +#define Skeleton_Skeleton_h__ + +#include +#include +#include +#include "MayaIncludes.h" +#include "OutputData.h" + +class Animation : public OutputData { +public: + struct Keyframe + { + struct JointProperty + { + int ID = 0; + float Position[3]{ 0 }; + float Rotation[4]{ 0 }; + float Scale[3]{ 0 }; + }; + + int Index = 0; + float Time = 0; + std::vector JointProperties; + }; + + std::string Name; + int nameLength = 0; + float Duration = 0; + int NumKeyFrames = 0; + int NumberOfJoints = 0; + std::vector Keyframes; + + virtual void WriteBinary(std::ostream& out) + { + out.write((char*)&nameLength, sizeof(int)); + out.write(Name.c_str(), Name.size() + 1); + out.write((char*)&Duration, sizeof(float)); + out.write((char*)&NumKeyFrames, sizeof(int)); + out.write((char*)&NumberOfJoints, sizeof(int)); + //Här under loopas alla key frames igenom + for (auto aKeyframe : Keyframes) { + out.write((char*)&aKeyframe.Index, sizeof(int)); + out.write((char*)&aKeyframe.Time, sizeof(float)); + for (auto aJoint : aKeyframe.JointProperties) { + out.write((char*)&aJoint.ID, sizeof(int)); + out.write((char*)aJoint.Position, sizeof(float) * 3); + out.write((char*)aJoint.Rotation, sizeof(float) * 4); + out.write((char*)aJoint.Scale, sizeof(float) * 3); + } + } + } + + virtual void WriteASCII(std::ostream& out) const + { + out << "Animation Name: " << Name << endl; + out << "Duration: " << Duration << endl; + out << "Number of KeyFrames: " << NumKeyFrames << endl; + out << "Number of Joints: " << NumberOfJoints << endl; + for (auto aKeyframe : Keyframes) { + out << "Frame: " << aKeyframe.Index << endl; + out << "Time: " << aKeyframe.Time << endl; + for (auto aJoint : aKeyframe.JointProperties) { + out << "Joint ID: " << aJoint.ID << endl; + out << aJoint.Position[0] << " " << aJoint.Position[1] << " " << aJoint.Position[2] << endl; + out << aJoint.Rotation[0] << " " << aJoint.Rotation[1] << " " << aJoint.Rotation[2] << " " << aJoint.Rotation[3] << endl; + out << aJoint.Scale[0] << " " << aJoint.Scale[1] << " " << aJoint.Scale[2] << endl; + } + } + + } +}; + +class BindPoseSkeletonNode : public OutputData { +public: + struct BindPoseJoint + { + int NameLength; + std::string Name; + float OffsetMatrix[4][4]{ 0 }; + int ID = 0; + int ParentID = 0; + }; + int numBones = 0; + std::string Name; + std::vector Joints; + virtual void WriteBinary(std::ostream& out) + { + out.write((char*)&numBones, sizeof(int)); + for (auto Joint:Joints) + { + out.write((char*)&Joint.NameLength, sizeof(int)); + out.write(Joint.Name.c_str(), Joint.Name.size() + 1); + out.write((char*)&Joint.OffsetMatrix, sizeof(float) * 4 * 4); + out.write((char*)&Joint.ID, sizeof(int)); + out.write((char*)&Joint.ParentID, sizeof(int)); + } + + } + + virtual void WriteASCII(std::ostream& out) const + { + out << "Bind Pose: " << Name << " _ not in binary" << endl; + out << "numberOfBones: " << numBones << endl; + for (auto Joint : Joints) + { + out << "Joint NameLength " << Joint.NameLength << endl; + out << "Joint name " << Joint.Name << endl; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++){ + out << Joint.OffsetMatrix[i][j] << " "; + } + out << endl; + } + out << Joint.ID << endl; + out << Joint.ParentID << endl; + } + }; +}; + +class Skeleton { +public: + //std::vector DoIt(); + Animation GetAnimData(std::string animationName, int startFrame, int endFrame); + std::vector GetBindPoses(); +private: +}; + +#endif //Skeleton_Skeleton_h__ \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/WriteToFile.cpp b/tools/MayaExporter/MayaExporter/WriteToFile.cpp new file mode 100644 index 00000000..a0368c4f --- /dev/null +++ b/tools/MayaExporter/MayaExporter/WriteToFile.cpp @@ -0,0 +1,41 @@ +#include "WriteToFile.h" + +WriteToFile::~WriteToFile() +{ + CloseFiles(); +} + +bool WriteToFile::binaryFilePath(string filePathAndFileName) +{ + binFileName = filePathAndFileName; + ofstream binFile(filePathAndFileName, ofstream::binary); + if (!binFile) + return false; + return true; +} + +bool WriteToFile::ASCIIFilePath(string filePathAndFileName) +{ + ASCIIFileName = filePathAndFileName; + ofstream ASCIIFile(filePathAndFileName); + if (!ASCIIFile) + return false; + return true; +} + +void WriteToFile::OpenFiles() +{ + if (binFile) { + binFile.open(binFileName, ofstream::binary); + } + if (ASCIIFile){ + ASCIIFile.open(ASCIIFileName); + ASCIIFile << std::fixed << std::setprecision(3); + } +} + +void WriteToFile::CloseFiles() +{ + binFile.close(); + ASCIIFile.close(); +} \ No newline at end of file diff --git a/tools/MayaExporter/MayaExporter/WriteToFile.h b/tools/MayaExporter/MayaExporter/WriteToFile.h new file mode 100644 index 00000000..216fcfc9 --- /dev/null +++ b/tools/MayaExporter/MayaExporter/WriteToFile.h @@ -0,0 +1,64 @@ +#ifndef WriteToFile_WriteToFile_h__ +#define WriteToFile_WriteToFile_h__ + +#include "MayaIncludes.h" +#include "OutputData.h" +#include +#include +#include + +using namespace std; + +class WriteToFile +{ +public: + ~WriteToFile(); + bool binaryFilePath(string filePathAndFileName); + bool ASCIIFilePath(string filePathAndFileName); + + void writeToFiles(OutputData* toWrite, unsigned int numOfElementToWrite = 1, unsigned int startIndex = 0) + { + MGlobal::displayInfo("WriteToFile::writeToFiles(OutputData*)"); + if (ASCIIFile.is_open()) + { + for (unsigned int i = startIndex; i < numOfElementToWrite + startIndex; i++) + ASCIIFile << toWrite[i] << endl; + } + + if (binFile.is_open()) + { + for (unsigned int i = startIndex; i < numOfElementToWrite + startIndex; i++) + toWrite[i].WriteBinary(binFile); + } + + } + + template + void writeToFiles(T* toWrite, unsigned int numOfElementToWrite = 1, unsigned int startIndex = 0) + { + MGlobal::displayInfo("WriteToFile::writeToFiles(T*) - Template T"); + if (ASCIIFile.is_open()) { + for (unsigned int i = startIndex; i < numOfElementToWrite + startIndex; i++) + ASCIIFile << toWrite[i] << endl; + } + + if (binFile.is_open()) { + for (unsigned int i = startIndex; i < numOfElementToWrite + startIndex; i++) + binFile.write((char*)toWrite, sizeof(T)); + } + + } + + void OpenFiles(); + void CloseFiles(); + +private: + string binFileName; + string ASCIIFileName; + ofstream binFile; + ofstream ASCIIFile; +}; + + + +#endif \ No newline at end of file