From f0dd9e4e4d9eff4da55d4c078b35b25d72bc1fc2 Mon Sep 17 00:00:00 2001 From: Joey Pollack Date: Wed, 4 Jan 2023 19:00:48 -0500 Subject: [PATCH] Physics components and physics sim working Cleaned up tree view panels (no arrows on items with no children) --- docs/tasks/core.todo | 4 +- docs/tasks/editor.todo | 2 +- docs/tasks/minimum_usable_requirements.todo | 35 +++ src/gui/imgui_ext.cpp | 9 + src/gui/imgui_ext.h | 1 + src/run_modes/editor/component_guis.cpp | 116 +++++++++ src/run_modes/editor/component_guis.h | 2 + src/run_modes/editor/contents/World.cpp | 23 +- src/run_modes/editor/editor.cpp | 27 ++ src/run_modes/editor/editor.h | 1 + src/run_modes/editor/panels/asset_browser.cpp | 57 +++-- .../editor/panels/properties_view.cpp | 5 + src/run_modes/editor/panels/world_tree.cpp | 19 +- src/world/components.h | 66 +++++ src/world/entity.cpp | 65 ++++- src/world/world.cpp | 231 +++++++++++++++--- src/world/world.h | 21 +- 17 files changed, 619 insertions(+), 65 deletions(-) create mode 100644 docs/tasks/minimum_usable_requirements.todo diff --git a/docs/tasks/core.todo b/docs/tasks/core.todo index 85906d8..e5eabba 100644 --- a/docs/tasks/core.todo +++ b/docs/tasks/core.todo @@ -120,7 +120,8 @@ ECS: ✔ Velocity @done(22-09-07 14:49) ✔ Camera @done(22-09-07 14:49) ✔ BlockOut @done(22-09-08 15:41) - ☐ SpriteRenderer + ✔ SpriteRenderer @done(22-11-28 20:09) + ☐ TextRenderer ☐ Animation Controller ✔ Script @done(22-11-14 18:19) ☐ Audio Listener @@ -181,6 +182,7 @@ Assets: ✔ Decouple Image class from OGLRenderer @high @done (10/27/2021, 7:41:50 PM) ☐ Font class ☐ Sound class + ☐ Animation ✔ Script class @done (1/25/2022, 3:58:28 PM) Loaders: diff --git a/docs/tasks/editor.todo b/docs/tasks/editor.todo index 29cfaa0..3c62f8f 100644 --- a/docs/tasks/editor.todo +++ b/docs/tasks/editor.todo @@ -3,7 +3,7 @@ Editor: ☐ Generate boiler-plate code when a new script is created ☐ Include setting the entity ID - ☐ Add pure virtual ShowProperties method to EditorAsset + ✔ Add pure virtual ShowProperties method to EditorAsset @done(22-11-28 20:11) ✔ Script Editor Asset @done(22-11-14 18:19) ✔ Editor Assets need to switch to using UUIDs @critical @done(22-11-02 18:51) ✔ Remove Entity @done(22-10-14 18:28) diff --git a/docs/tasks/minimum_usable_requirements.todo b/docs/tasks/minimum_usable_requirements.todo new file mode 100644 index 0000000..7343829 --- /dev/null +++ b/docs/tasks/minimum_usable_requirements.todo @@ -0,0 +1,35 @@ +Tasks/Features needed to build and release a complete game: + ☐ Audio System + ☐ Implement Audio API into the core + ☐ Integrate OpenAL + ☐ Expose API methods + ☐ Integrate Audio API into the editor + ☐ Audio listener component + ☐ Audio source component + + ☐ Entity Template System + ☐ Entity template asset + ☐ Entity spawning in scripts + ☐ Entity destruction in scripts + + ☐ Build and Release System + ☐ Asset Compiling + ☐ Binary data formats + ☐ Implement binary asset use in the World + + ☐ Game playback Module + ☐ Binary asset manager + ☐ Binary asset loader + + ☐ Basic Physics + ✔ Physics sim implemented in World @done(23-01-04 18:22) + ✔ Expose physics settings of the world @done(23-01-04 14:43) + ✔ Gravity setting @done(23-01-04 14:43) + ✔ Scale factor @done(23-01-04 14:43) + ✔ Rigid Body Component @done(23-01-04 15:08) + ✔ Collider Components @done(23-01-04 15:08) + ☐ Collision Listeners + +Nice-To-Haves: + ☐ ImGizmo Implemented + ☐ Basic Sprite Animation diff --git a/src/gui/imgui_ext.cpp b/src/gui/imgui_ext.cpp index 0051f26..f553793 100644 --- a/src/gui/imgui_ext.cpp +++ b/src/gui/imgui_ext.cpp @@ -81,6 +81,15 @@ namespace lunarium return values_updated; } + bool ImGuiExt::Vec2Control(const std::string& label, glm::vec2& values, float resetValue, float columnWidth) + { + glm::vec3 temp = { values.x, values.y, 0.0 }; + bool result = Vec2Control(label, temp, resetValue, columnWidth); + values.x = temp.x; + values.y = temp.y; + return result; + } + bool ImGuiExt::Vec2Control(const std::string& label, glm::vec3& values, float resetValue, float columnWidth) { diff --git a/src/gui/imgui_ext.h b/src/gui/imgui_ext.h index 8d61a81..d327328 100644 --- a/src/gui/imgui_ext.h +++ b/src/gui/imgui_ext.h @@ -23,6 +23,7 @@ namespace lunarium /// https://github.com/TheCherno/Hazel/blob/master/Hazelnut/src/Panels/SceneHierarchyPanel.cpp static bool Vec3Control(const std::string& label, glm::vec3& values, float resetValue = 0.0f, float columnWidth = 100.0f); static bool Vec2Control(const std::string& label, glm::vec3& values, float resetValue = 0.0f, float columnWidth = 100.0f); + static bool Vec2Control(const std::string& label, glm::vec2& values, float resetValue = 0.0f, float columnWidth = 100.0f); static bool FloatControl(const std::string& label, float& values, float resetValue = 0.0f, float columnWidth = 100.0f); static void TextCentered(const std::string text); static bool ButtonCentered(const char* label, float alignment = 0.5f); diff --git a/src/run_modes/editor/component_guis.cpp b/src/run_modes/editor/component_guis.cpp index 1d9b63f..eaddb71 100644 --- a/src/run_modes/editor/component_guis.cpp +++ b/src/run_modes/editor/component_guis.cpp @@ -282,6 +282,122 @@ namespace lunarium { namespace editor ImGui::EndCombo(); } + return remove; + } + + bool CompGui::RenderRigidBodyComp(RigidBodyComponent& comp) + { + bool remove = false; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f)); + remove = DrawTitle("Rigid Body Component"); + ImGui::PopStyleVar(); + + bool is_static = comp.IsStatic; + if (ImGuiExt::CheckBoxLeft("Is Static:", &is_static)) + { + comp.IsStatic = is_static; + } + + float mass = comp.Mass; + if (ImGuiExt::FloatControl("Mass:", mass, 1.0f, 85.0f)) + { + comp.Mass = mass; + } + + bool freeze_rotation = comp.Defintion.fixedRotation; + if (ImGuiExt::CheckBoxLeft("Freeze Rotation: ", &freeze_rotation)) + { + comp.Defintion.fixedRotation = freeze_rotation; + } + + bool is_high_speed = comp.Defintion.bullet; + if (ImGuiExt::CheckBoxLeft("Is High Speed: ", &is_high_speed)) + { + comp.Defintion.bullet = is_high_speed; + } + + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("Is this a fast moving body that should be prevented from tunneling through other moving bodies? Note that all bodies are prevented from tunneling through kinematic and static bodies. This setting is only considered on dynamic bodies. warning: You should use this flag sparingly since it increases processing time."); + } + + // TODO: Expose other body parameters (damping, gravity scale, sleeping, etc) + // Maybe as "advanced" parameters hidden in a collapsable area + + + return remove; + } + + bool CompGui::RenderCollisionComp(CollisionComponent& comp) + { + bool remove = false; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, ImGui::GetStyle().ItemSpacing.y + 10.0f)); + remove = DrawTitle("Collision Component"); + ImGui::PopStyleVar(); + + // Shape Type + std::string preview = "NOT SET"; + switch (comp.Type) + { + case CollisionComponent::ShapeType::Box: preview = "Box"; break; + case CollisionComponent::ShapeType::Circle: preview = "Circle"; break; + } + + ImGui::SetNextItemWidth(85); + if (ImGui::BeginCombo("Shape Type", preview.c_str())) + { + if (ImGui::Selectable("Box")) + { + comp.Type = CollisionComponent::ShapeType::Box; + } + + if (ImGui::Selectable("Circle")) + { + comp.Type = CollisionComponent::ShapeType::Circle; + } + + ImGui::EndCombo(); + } + + // Is Sensor + bool Is_sensor = comp.IsSensor; + if (ImGuiExt::CheckBoxLeft("Is Sensor: ", &Is_sensor)) + { + comp.IsSensor = Is_sensor; + } + + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("A sensor does not generate collision responses (aka a trigger)"); + } + + if (comp.Type == CollisionComponent::ShapeType::Circle) + { + ImGuiExt::FloatControl("Radius:", comp.Radius, 1.0f, 85.0f); + } + + if (comp.Type == CollisionComponent::ShapeType::Box) + { + ImGuiExt::FloatControl("Width:", comp.Width, 1.0f, 85.0f); + ImGuiExt::FloatControl("Height:", comp.Height, 1.0f, 85.0f); + } + + // Collision Group + ImGui::Text("Collision Group: "); + ImGui::SameLine(); + ImGui::SetNextItemWidth(80); + ImGui::InputInt("##Collision Group", &comp.CollisionGroup, 1, 10); + if (ImGui::IsItemHovered()) + { + ImGui::SetTooltip("Only objects in the same Collision Group can collide"); + } + + // Physics properties + ImGuiExt::FloatControl("Density:", comp.Density, 1.0f, 85.0f); + ImGuiExt::FloatControl("Friction:", comp.Friction, 0.25f, 85.0f); + ImGuiExt::FloatControl("Restitution:", comp.Restitution, 0.25f, 85.0f); + + return remove; } }} \ No newline at end of file diff --git a/src/run_modes/editor/component_guis.h b/src/run_modes/editor/component_guis.h index 933c24f..26f7ddc 100644 --- a/src/run_modes/editor/component_guis.h +++ b/src/run_modes/editor/component_guis.h @@ -23,6 +23,8 @@ namespace lunarium { namespace editor static bool RenderBlockOutComp(BlockOutComponent& comp); static bool RenderSpriteRendererComp(SpriteRendererComponent& comp); static bool RenderScriptComp(ScriptComponent& comp); + static bool RenderRigidBodyComp(RigidBodyComponent& comp); + static bool RenderCollisionComp(CollisionComponent& comp); }; }} diff --git a/src/run_modes/editor/contents/World.cpp b/src/run_modes/editor/contents/World.cpp index aa776e1..9ecc3c1 100644 --- a/src/run_modes/editor/contents/World.cpp +++ b/src/run_modes/editor/contents/World.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -121,7 +123,26 @@ namespace lunarium { namespace editor bool World::DrawProperties() { - return false; + std::string name = mpWorld->GetName(); + char buffer[256]; + strcpy(buffer, name.c_str()); + ImGui::Text("World Name: "); + ImGui::SameLine(); + bool result0 = ImGui::InputText("##World Name", buffer, 256); + if (result0) mpWorld->SetName(buffer); + + // Show gravity and Physics scale value + glm::vec2 gravity = mpWorld->GetGravity(); + bool result1 = ImGuiExt::Vec2Control("World Gravity", gravity); + if (result1) mpWorld->SetGravity(gravity); + + float scale = mpWorld->GetPhysicsScaleFactor(); + ImGui::Text("World Physics Scale Factor:"); + ImGui::SameLine(); + bool result2 = ImGui::DragFloat("##Scale Factor", &scale); + if (result2) mpWorld->SetPhysicsScaleFactor(scale); + + return (result0 || result1 || result2); } }} \ No newline at end of file diff --git a/src/run_modes/editor/editor.cpp b/src/run_modes/editor/editor.cpp index 0c65071..881d75d 100644 --- a/src/run_modes/editor/editor.cpp +++ b/src/run_modes/editor/editor.cpp @@ -480,6 +480,33 @@ namespace editor // } + void Editor::OnComponentAdd(lunarium::Entity* pEnt, void* pComp, lunarium::ComponentType type) + { + if (lunarium::ComponentType::RigidBodyComponent == type || lunarium::ComponentType::CollisionComponent == type) + { + if (pEnt->HasComponent()) + { + Logger::Warn(LogCat, "Entity with ID %llu has both a physics component and a VelocityComponent. These will not work together, the VelocityComponent will be ignored.", pEnt->GetUUID()); + } + } + + if (lunarium::ComponentType::CollisionComponent == type) + { + if (!pEnt->HasComponent()) + { + Logger::Warn(LogCat, "Entity with ID %llu has a CollisionComponent but does not have a RigidBodyComponent. The CollisionComponent will not do anything."); + } + } + + if (lunarium::ComponentType::VelocityComponent == type) + { + if (pEnt->HasComponent() || pEnt->HasComponent()) + { + Logger::Warn(LogCat, "Entity with ID %llu has both a physics component and a VelocityComponent. These will not work together, the VelocityComponent will be ignored.", pEnt->GetUUID()); + } + } + } + void Editor::OnAssetOpen(EditorAsset* pAsset) { switch (pAsset->GetType()) diff --git a/src/run_modes/editor/editor.h b/src/run_modes/editor/editor.h index 7c4ba72..d78f1a2 100644 --- a/src/run_modes/editor/editor.h +++ b/src/run_modes/editor/editor.h @@ -58,6 +58,7 @@ namespace lunarium { namespace editor Project* GetProject(); // Panel events + void OnComponentAdd(lunarium::Entity* pEnt, void* pComp, lunarium::ComponentType type); void OnEntitySelect(lunarium::Entity* pEnt); //void ChangeWorld(lunarium::World* pWorld); void OnAssetOpen(EditorAsset* pAsset); diff --git a/src/run_modes/editor/panels/asset_browser.cpp b/src/run_modes/editor/panels/asset_browser.cpp index 4399eb7..8ea6b2b 100644 --- a/src/run_modes/editor/panels/asset_browser.cpp +++ b/src/run_modes/editor/panels/asset_browser.cpp @@ -105,30 +105,53 @@ namespace editor } } - if (ImGui::TreeNode(next_node_name.c_str())) - { - if (!mSyncTree) + // Doing this to prevent arrows from showing up on items that have no child directories + // Seems a bit clumsy to iterate the directory twice though so this can probably + // be cleaned up a bit + int num_child_dirs = 0; + for(auto const& dir_entry: std::filesystem::directory_iterator{dir}) + { + if (std::filesystem::is_directory(dir_entry)) { - if (ImGui::IsItemClicked()) - { - mSelectedDir = dir; - ImGui::SetNextItemOpen(false); - } - HandleAssetDrop(dir); + num_child_dirs++; } - - for(auto const& dir_entry: std::filesystem::directory_iterator{dir}) + } + + if (num_child_dirs < 1) + { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); + if (ImGui::Selectable(next_node_name.c_str())) { - DoDirTree(dir_entry); + mSelectedDir = dir; } - ImGui::TreePop(); } - else + + else { - if (ImGui::IsItemClicked()) + if (ImGui::TreeNode(next_node_name.c_str())) { - mSelectedDir = dir; - ImGui::SetNextItemOpen(false); + if (!mSyncTree) + { + if (ImGui::IsItemClicked()) + { + mSelectedDir = dir; + ImGui::SetNextItemOpen(false); + } + HandleAssetDrop(dir); + } + for(auto const& dir_entry: std::filesystem::directory_iterator{dir}) + { + DoDirTree(dir_entry); + } + ImGui::TreePop(); + } + else + { + if (ImGui::IsItemClicked()) + { + mSelectedDir = dir; + ImGui::SetNextItemOpen(false); + } } } } diff --git a/src/run_modes/editor/panels/properties_view.cpp b/src/run_modes/editor/panels/properties_view.cpp index e660679..7d8c61a 100644 --- a/src/run_modes/editor/panels/properties_view.cpp +++ b/src/run_modes/editor/panels/properties_view.cpp @@ -30,6 +30,7 @@ if (ImGui::Selectable(str_name)){\ if (!pv->mpSelectedEntity->HasComponent()) {\ Logger::Error(Editor::LogCat, "Component not added to entity - unknown error");\ }\ + pv->mpEditor->OnComponentAdd(pv->mpSelectedEntity, (void*)&pv->mpSelectedEntity->GetComponent(), lunarium::ComponentType::type_name);\ Logger::Info(Editor::LogCat, "Component added to entity: %ll", pv->mpSelectedEntity->GetUUID());\ return false;\ } @@ -70,6 +71,8 @@ namespace lunarium { namespace editor PRESENT_COMP_CHOICE("Block Out Component", BlockOutComponent, pv) PRESENT_COMP_CHOICE("Sprite Renderer Component", SpriteRendererComponent, pv) PRESENT_COMP_CHOICE("Script Component", ScriptComponent, pv) + PRESENT_COMP_CHOICE("Rigid Body Component", RigidBodyComponent, pv) + PRESENT_COMP_CHOICE("Collision Component", CollisionComponent, pv) if ((ImGui::IsMouseClicked(ImGuiMouseButton_Left) && is_hover < 1)) @@ -191,6 +194,8 @@ namespace lunarium { namespace editor DRAW_COMP_GUI(BlockOutComponent, RenderBlockOutComp) DRAW_COMP_GUI(SpriteRendererComponent, RenderSpriteRendererComp) DRAW_COMP_GUI(ScriptComponent, RenderScriptComp) + DRAW_COMP_GUI(RigidBodyComponent, RenderRigidBodyComp) + DRAW_COMP_GUI(CollisionComponent, RenderCollisionComp) // After all components rendered if (ImGuiExt::ButtonCentered("Add Component")) diff --git a/src/run_modes/editor/panels/world_tree.cpp b/src/run_modes/editor/panels/world_tree.cpp index bb6f2c7..923618a 100644 --- a/src/run_modes/editor/panels/world_tree.cpp +++ b/src/run_modes/editor/panels/world_tree.cpp @@ -141,10 +141,21 @@ namespace lunarium { namespace editor bool was_clicked = false; bool was_right_clicked = false; - bool node_open = ImGui::TreeNode(pEnt->GetName().c_str()); + bool node_open = false; + if (pEnt->HasChildren()) + { + node_open = ImGui::TreeNode(pEnt->GetName().c_str()); + } + else + { + ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing()); + ImGui::Selectable(pEnt->GetName().c_str()); + was_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left); + was_right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right); + } // Handle drag and drop even if the node is closed - if (ImGui::BeginDragDropSource()) + if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) { // Need to pass a pointer to the payload data. This means we need to pass a // pointer to the Entity pointer (Entity**) which &pEnt becomes @@ -210,8 +221,8 @@ namespace lunarium { namespace editor } else { - was_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left); - was_right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right); + // was_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left); + // was_right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right); } if (was_clicked) diff --git a/src/world/components.h b/src/world/components.h index e3c3924..62f4d72 100644 --- a/src/world/components.h +++ b/src/world/components.h @@ -6,6 +6,20 @@ * Description - ECS component declarations ******************************************************************************/ +/* + STEPS FOR CREATING A NEW COMPONENT: + + 1) add the struct to components.h + 2) add the gui code to component_guis.h + 3) add the code to call the gui function to properties_view.cpp + 4) add the code to add a new instance of the component to selected entity in properties_view's ADD_COMPONENT popup (using PRESENT_COMP_CHOICE) + 5) add serialize/deserialize code to entity.cpp + + Situational: + 6) add code (system) to use the component in World + 7) add a foreign class to the scripting system to access/manipulate the component +*/ + #ifndef LUNARIUM_COMPONENTS_H_ #define LUNARIUM_COMPONENTS_H_ @@ -21,8 +35,27 @@ #include #include + namespace lunarium { + enum class ComponentType + { + TagComponent, + TransformComponent, + VelocityComponent, + CameraComponent, + BlockOutComponent, + SpriteRendererComponent, + ScriptComponent, + RigidBodyComponent, + CollisionComponent, + + UUIDComponent, + ParentEntityComponent, + ChildrenComponent, + + }; + struct TagComponent { std::string Info; @@ -58,6 +91,8 @@ namespace lunarium } }; + // TODO: Deprecate in favor of using the physics system for all motion + // Or perhaps manually control the Transform component struct VelocityComponent { glm::vec3 Velocity = { 0.0f, 0.0f, 0.0f}; @@ -104,6 +139,37 @@ namespace lunarium ScriptComponent(const ScriptComponent&) = default; }; + struct RigidBodyComponent + { + b2Body* pBody = nullptr; + bool IsStatic = false; + float Mass = 1.0f; + b2BodyDef Defintion; + + RigidBodyComponent() = default; + RigidBodyComponent(const RigidBodyComponent&) = default; + }; + + struct CollisionComponent + { + b2Shape* pShape = nullptr; + enum class ShapeType { Circle, Box }; + ShapeType Type; + float Radius = 1.0f; // only applies to circles + float Width = 1.0f; // only applies to boxes + float Height = 1.0f; // only applies to boxes + + bool IsSensor = false; // Does not generate collision responses (a trigger) + int CollisionGroup = 0; // Only objects in the same Collision Group can collide + float Density = 1.0f; + float Friction = 0.25f; + float Restitution = 0.25f; + + + CollisionComponent() = default; + CollisionComponent(const CollisionComponent&) = default; + }; + ///////////////////////////////////////////////////////////////////// // UTILITY COMPONENTS ///////////////////////////////////////////////////////////////////// diff --git a/src/world/entity.cpp b/src/world/entity.cpp index ff6f1e9..d0b1f6f 100644 --- a/src/world/entity.cpp +++ b/src/world/entity.cpp @@ -256,7 +256,7 @@ namespace lunarium auto& color = blockout["color"]; color["r"] = comp.Color.x; color["g"] = comp.Color.y; - color["b"] = comp.Color.a; + color["b"] = comp.Color.z; color["a"] = comp.Color.w; auto& size = blockout["size"]; @@ -306,6 +306,40 @@ namespace lunarium components.emplace_back(script); } + if (HasComponent()) + { + nlohmann::ordered_json rigid_body; + RigidBodyComponent& comp = GetComponent(); + rigid_body["type_name"] = "RigidBodyComponent"; + + rigid_body["is_static"] = comp.IsStatic; + rigid_body["mass"] = comp.Mass; + rigid_body["freeze_rotation"] = comp.Defintion.fixedRotation; + rigid_body["is_high_speed"] = comp.Defintion.bullet; + + components.emplace_back(rigid_body); + } + + if (HasComponent()) + { + nlohmann::ordered_json collision; + CollisionComponent& comp = GetComponent(); + collision["type_name"] = "CollisionComponent"; + + collision["radius"] = comp.Radius; + collision["width"] = comp.Width; + collision["height"] = comp.Height; + + collision["shape_type"] = (int)comp.Type; + collision["is_sensor"] = comp.IsSensor; + collision["collision_group"] = comp.CollisionGroup; + collision["density"] = comp.Density; + collision["friction"] = comp.Friction; + collision["restitution"] = comp.Restitution; + + components.emplace_back(collision); + } + if (HasComponent()) { nlohmann::ordered_json parent; @@ -474,6 +508,35 @@ namespace lunarium AddComponent(id); } + if ("RigidBodyComponent" == comp_type_name) + { + bool is_static = comp["is_static"].get(); + float mass = comp["mass"].get(); + bool freeze_rot = comp["freeze_rotation"].get(); + bool is_high_speed = comp["is_high_speed"].get(); + + b2BodyDef definition; + definition.fixedRotation = freeze_rot; + definition.bullet = is_high_speed; + AddComponent(nullptr, is_static, mass, definition); + } + + if ("CollisionComponent" == comp_type_name) + { + CollisionComponent::ShapeType type = (CollisionComponent::ShapeType)comp["shape_type"].get(); + float radius = comp["radius"].get(); + float width = comp["width"].get(); + float height = comp["height"].get(); + bool is_sensor = comp["is_sensor"].get(); + int collision_group = comp["collision_group"].get(); + float density = comp["density"].get(); + float friction = comp["friction"].get(); + float restitution = comp["restitution"].get(); + + AddComponent(nullptr, type, radius, width, height, is_sensor, collision_group, density, friction, restitution); + } + + if ("ParentEntityComponent" == comp_type_name) { LUUID parent = comp["UUID"].get(); diff --git a/src/world/world.cpp b/src/world/world.cpp index 2e94e56..e98e751 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "entity.h" //#define LOAD_ASSETS_FROM_EDITOR true @@ -38,19 +39,113 @@ namespace lunarium // } World::World(std::string name) - : mUUID(UUID::GetNewID()), mName(name), mpActiveCamera(nullptr), mFrameBuffer(nullptr) + : mUUID(UUID::GetNewID()), mName(name), mpActiveCamera(nullptr), mFrameBuffer(nullptr), mpPhysicsWorld(nullptr), mDrawDebugObjects(false) //mGetAssetsFromEditor(EDITOR_MODE) { + WorldSettings.Physics.PhysicsScaleFactor = 16.0f; } + + std::string World::GetName() const + { + return mName; + } - void World::OnLoad() + void World::SetName(std::string name) + { + mName = name; + } + + glm::vec2 World::GetGravity() const + { + return { WorldSettings.Physics.Gravity.x, WorldSettings.Physics.Gravity.y }; + } + + void World::SetGravity(glm::vec2 gravity) { + WorldSettings.Physics.Gravity.x = gravity.x; + WorldSettings.Physics.Gravity.y = gravity.y; + } + + float World::GetPhysicsScaleFactor() const + { + return WorldSettings.Physics.PhysicsScaleFactor; + } + + void World::SetPhysicsScaleFactor(float factor) + { + WorldSettings.Physics.PhysicsScaleFactor = factor; + } + + void World::OnLoad() + { + // INITIALIZE SCRIPTS WorldAPI::Initialize(this).LogIfFailed(LogCategory::GAME_SYSTEM, "Failed to initialized the world scripting api"); + // Initialize Physics + mpPhysicsWorld = new b2World({WorldSettings.Physics.Gravity.x, WorldSettings.Physics.Gravity.y}); + + // Iterate all entities with physics components and initialize them + // Only checking for RigidBody because any physics object needs at least that + auto group_phys = mECSRegistry.view(); + for (auto entity: group_phys) + { + auto& transform = group_phys.get(entity); + auto& rigid_body = group_phys.get(entity); + float scale_factor = WorldSettings.Physics.PhysicsScaleFactor; + + rigid_body.Defintion.position.Set(transform.Position.x / scale_factor, transform.Position.y / scale_factor); + rigid_body.Defintion.angle = transform.Rotation.z; + if (!rigid_body.IsStatic) + { + rigid_body.Defintion.type = b2_dynamicBody; + } + + rigid_body.pBody = mpPhysicsWorld->CreateBody(&rigid_body.Defintion); + + // Handle collision components + if (mECSRegistry.all_of(entity)) + { + auto& comp = mECSRegistry.get(entity); + switch (comp.Type) + { + case CollisionComponent::ShapeType::Box: + { + b2PolygonShape Shape; + Shape.SetAsBox(comp.Width / scale_factor, comp.Height / scale_factor); + b2FixtureDef fixtureDef; + fixtureDef.shape = &Shape; + fixtureDef.isSensor = comp.IsSensor; + fixtureDef.density = comp.Density; + fixtureDef.friction = comp.Friction; + fixtureDef.restitution = comp.Restitution; + + rigid_body.pBody->CreateFixture(&fixtureDef); + } break; + + case CollisionComponent::ShapeType::Circle: + { + b2CircleShape Shape; + Shape.m_radius = comp.Radius / scale_factor; + + b2FixtureDef fixtureDef; + fixtureDef.shape = &Shape; + fixtureDef.isSensor = comp.IsSensor; + fixtureDef.density = comp.Density; + fixtureDef.friction = comp.Friction; + fixtureDef.restitution = comp.Restitution; + + rigid_body.pBody->CreateFixture(&fixtureDef); + } break; + + } + } + } + + // Load scripts from all ScriptComponents in the world - auto group_scripts = mECSRegistry.view(); + auto group_scripts = mECSRegistry.view(); for(auto entity: group_scripts) { auto& script_comp = group_scripts.get(entity); @@ -62,6 +157,8 @@ namespace lunarium WrenScript script(pScript->GetScriptFile().filename().string().c_str(), pScript->GetScript()); // mScriptState.RunScript(&script); WorldAPI::RunScript(script); +#else + // Get Binary assets #endif } @@ -74,6 +171,9 @@ namespace lunarium // Call OnUnLoad for each registered EntityBehavior class object WorldAPI::InvokeEvent(WorldAPI::Event::ON_UNLOAD); WorldAPI::Shutdown(); + + delete mpPhysicsWorld; + mpPhysicsWorld = nullptr; } void World::SetRunMode(RunMode mode) @@ -94,21 +194,50 @@ namespace lunarium // Update Transforms for any enity with a velocity + // NOTE: Might remove the VelocityComponent in favor of using the physics system auto group = mECSRegistry.group<>(entt::get); for(auto entity: group) - { + { + // Skip any entity that has a physics component + if (mECSRegistry.any_of(entity)) + { + continue; + } + auto &transform = group.get(entity); auto &velocity = group.get(entity); transform.Position += velocity.Velocity * dt; } + // Update the physics sim and then use the results to update objects Transforms + float timeStep = 1.0f / 60.0f; + int32 velocityIterations = 6; + int32 positionIterations = 2; + + mpPhysicsWorld->Step(timeStep, velocityIterations, positionIterations); + auto group_phys = mECSRegistry.view(); + for (auto entity: group_phys) + { + auto& transform = group_phys.get(entity); + auto& rigid_body = group_phys.get(entity); + float scale_factor = WorldSettings.Physics.PhysicsScaleFactor; + + transform.Position = glm::vec3(rigid_body.pBody->GetPosition().x * scale_factor, rigid_body.pBody->GetPosition().y * scale_factor, 0.0f); + transform.Rotation.z = rigid_body.pBody->GetAngle(); + } + } void World::Render(lunarium::Renderer2D* pGraphics) { - RenderBlockouts(pGraphics); - RenderSprites(pGraphics); + RenderBlockouts(pGraphics); + RenderSprites(pGraphics); + + if (mDrawDebugObjects) + { + RenderDebugObjects(pGraphics); + } } @@ -271,40 +400,36 @@ namespace lunarium // pGraphics->DrawQuad(rect, color, nullptr, transform.Rotation.z, Rectangle(-1, -1, -1, -1), parent_transform); } } - - // void World::DrawHeirarchy(lunarium::Renderer2D* g, entt::entity& entity, TransformComponent& my_trans, BlockOutComponent& bo_comp, glm::mat4 current_transform) - // { - // // Draw current entity - // Rectangle rect(my_trans.Position.x, my_trans.Position.y, bo_comp.Size.x, bo_comp.Size.y); - // Color color(bo_comp.Color.x, bo_comp.Color.y, bo_comp.Color.z, bo_comp.Color.w); - // g->DrawQuad(rect, color, nullptr, -my_trans.Rotation.z, Rectangle(-1, -1, -1, -1), current_transform); - - // // Apply transform to children's transforms - // current_transform *= my_trans.GetTransform(); - - // // Iterate and draw children - // if (mECSRegistry.all_of(entity)) - // { - // ChildrenComponent& children_comp = mECSRegistry.get(entity); - // for (int i = 0; i < children_comp.Children.size(); i++) - // { - // Entity* pEnt = GetEntity(children_comp.Children[i]); - // entt::entity handle = pEnt->GetEnttHandle(); - - // if (pEnt->HasComponent() && pEnt->HasComponent()) - // { - // TransformComponent& trans_comp = pEnt->GetComponent(); - // BlockOutComponent& bo_comp = pEnt->GetComponent(); - - // DrawHeirarchy(g, handle, trans_comp, bo_comp, current_transform); - // } - - // } - // } - - // } + void World::RenderDebugObjects(lunarium::Renderer2D* pGraphics) + { + auto group_phys = mECSRegistry.view(); + for (auto entity : group_phys) + { + auto& rigid_body = group_phys.get(entity); + auto& collision = group_phys.get(entity); + + if (!rigid_body.pBody) + continue; + + if (collision.Type == CollisionComponent::ShapeType::Box) + { + b2AABB box; + b2Transform transform = { rigid_body.pBody->GetTransform().p, b2Rot(0.0f) }; + rigid_body.pBody->GetFixtureList()->GetShape()->ComputeAABB(&box, transform, 0); + float scale_factor = WorldSettings.Physics.PhysicsScaleFactor; + + float angle = rigid_body.pBody->GetAngle(); + Rectangle rect(box); + rect.Scale(scale_factor, scale_factor); + rect.X *= scale_factor; + rect.Y *= scale_factor; + pGraphics->DrawBox(rect, Color(0.1f, 0.2f, 1.0f, 1.0f), 1.5f, angle); + } + } + } + glm::mat4 World::GetParentTransform(LUUID parent) { glm::mat4 transform(1.0f); @@ -360,8 +485,11 @@ namespace lunarium { #if !BUILD_NO_EDITOR // Only does this when this is an editor build + // General data node["UUID"] = mUUID; + node["Name"] = mName; + // Game entities node["NumberOfEntities"] = mEntities.size(); auto& ents = node["Entities"]; for (int i = 0; i < mEntities.size(); i++) @@ -371,6 +499,17 @@ namespace lunarium ents.emplace_back(e); } + // World settings + auto& settings = node["Settings"]; + + auto& physics = settings["Physics"]; + physics["scale_factor"] = WorldSettings.Physics.PhysicsScaleFactor; + + auto& gravity = physics["gravity"]; + gravity["x"] = WorldSettings.Physics.Gravity.x; + gravity["y"] = WorldSettings.Physics.Gravity.y; + gravity["z"] = WorldSettings.Physics.Gravity.z; + #endif return OpRes::OK(); } @@ -379,7 +518,11 @@ namespace lunarium { #if !BUILD_NO_EDITOR // Only does this when this is an editor build + // General Data mUUID = node["UUID"].get(); + mName = node["Name"].get(); + + // Game entities int num_entities = node["NumberOfEntities"].get(); auto& ents = node["Entities"]; for (auto it = ents.begin(); it != ents.end(); ++it) @@ -398,6 +541,18 @@ namespace lunarium mEntitiesByUUID[new_ent->GetUUID()] = new_ent; } + // World Settings + if (!node["Settings"].is_null()) + { + auto& settings = node["Settings"]; + + auto& physics = settings["Physics"]; + WorldSettings.Physics.PhysicsScaleFactor = physics["scale_factor"].get(); + auto& gravity = physics["gravity"]; + WorldSettings.Physics.Gravity = { gravity["x"].get(), gravity["y"].get(), gravity["z"].get() }; + } + + #endif return OpRes::OK(); } diff --git a/src/world/world.h b/src/world/world.h index 11e54c7..693440c 100644 --- a/src/world/world.h +++ b/src/world/world.h @@ -25,6 +25,7 @@ #include struct WrenHandle; +class b2World; namespace lunarium { @@ -96,6 +97,12 @@ namespace lunarium WorldState GetState(); void ResetState(WorldState& state); + std::string GetName() const; + void SetName(std::string name); + glm::vec2 GetGravity() const; + void SetGravity(glm::vec2 gravity); + float GetPhysicsScaleFactor() const; + void SetPhysicsScaleFactor(float factor); // Serializing [[nodiscard]] virtual OpRes Serialize(nlohmann::ordered_json& node); @@ -107,15 +114,25 @@ namespace lunarium private: LUUID mUUID; std::string mName; + RunMode mMode; + bool mDrawDebugObjects; // State critical Data entt::registry mECSRegistry; std::vector mEntities; std::map mEntitiesByUUID; - RunMode mMode; FrameBuffer* mFrameBuffer; OrthographicCamera* mpActiveCamera; + b2World* mpPhysicsWorld; + struct + { + struct + { + float PhysicsScaleFactor; + glm::vec3 Gravity; + } Physics; + } WorldSettings; // // TODO: Move these into a TileMap class? @@ -136,7 +153,7 @@ namespace lunarium glm::mat4 GetParentTransform(LUUID parent); void RenderBlockouts(lunarium::Renderer2D* pGraphics); void RenderSprites(lunarium::Renderer2D* pGraphics); - //void DrawHeirarchy(lunarium::Renderer2D* g, entt::entity& entity, TransformComponent& my_trans, BlockOutComponent& bo_comp, glm::mat4 current_transform); + void RenderDebugObjects(lunarium::Renderer2D* pGraphics); }; }