From 00cd8a2b62da98e32b23889751fdf8d22a5e9b3b Mon Sep 17 00:00:00 2001 From: Joeyrp Date: Thu, 28 Oct 2021 16:45:31 -0400 Subject: [PATCH] Box2D "hello world" example implemented. Graphics system does not support rotation yet. --- docs/core.todo | 4 +- src/graphics/opengl/defaultShaders.h | 2 +- src/graphics/opengl/glGraphics.cpp | 8 + src/internal_libs/assets/CMakeLists.txt | 1 + src/internal_libs/utils/CMakeLists.txt | 1 + src/internal_libs/utils/types.cpp | 10 + src/internal_libs/utils/types.h | 5 + src/run_modes/tester/CMakeLists.txt | 3 +- src/run_modes/tester/scenes/physicsScene.cpp | 172 ++++++++++++++++++ src/run_modes/tester/scenes/physicsScene.h | 38 ++++ .../tester/scenes/simpleRenderScene.cpp | 1 + src/run_modes/tester/tester.cpp | 11 +- 12 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 src/run_modes/tester/scenes/physicsScene.cpp create mode 100644 src/run_modes/tester/scenes/physicsScene.h diff --git a/docs/core.todo b/docs/core.todo index c922fc5..d2efa25 100644 --- a/docs/core.todo +++ b/docs/core.todo @@ -17,6 +17,7 @@ Core: ✔ Implement the Image creation methods @done (9/9/2021, 2:50:20 PM) ✔ Implement Render to Texture @done (9/15/2021, 7:00:33 PM) ✔ Adjust the font loading code to use the binary file buffer instead of ifstream @done (9/17/2021, 6:11:06 PM) + ☐ Find a way to add rotation to shapes and images GUI: @@ -33,7 +34,8 @@ Core: Physics: ✔ Research Box2D as a possible physics library @done (10/27/2021, 7:40:44 PM) ✔ Add Box2D to the project as an external library @done (10/27/2021, 7:40:46 PM) - ☐ Add a scene to the tester to test Box2D usage + ✔ Add a scene to the tester to test Box2D usage @done (10/28/2021, 2:42:45 PM) + ☐ Come up with a way to wrap Box2D into an API Audio: diff --git a/src/graphics/opengl/defaultShaders.h b/src/graphics/opengl/defaultShaders.h index 6e9a1d5..9114a03 100644 --- a/src/graphics/opengl/defaultShaders.h +++ b/src/graphics/opengl/defaultShaders.h @@ -25,7 +25,7 @@ namespace lunarium \ void main()\ {\ - gl_Position = projection * vec4(vertex.xy, 0.0, 1.0);\ + gl_Position = projection * model * vec4(vertex.xy, 0.0, 1.0);\ }"; const char* DefaultShapeFragment = "#version 450 core\n\ diff --git a/src/graphics/opengl/glGraphics.cpp b/src/graphics/opengl/glGraphics.cpp index 0a62fc7..04dae44 100644 --- a/src/graphics/opengl/glGraphics.cpp +++ b/src/graphics/opengl/glGraphics.cpp @@ -194,6 +194,8 @@ namespace lunarium { mShapeShader.MakeActive(); mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection)); + glm::mat4 model = glm::mat4(1.0f); + mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model)); mShapeShader.SetUniformf("shapeColor", { color.Red, color.Green, color.Blue, color.Alpha }); glBindVertexArray(mRectVAO); @@ -220,6 +222,8 @@ namespace lunarium { mShapeShader.MakeActive(); mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection)); + glm::mat4 model = glm::mat4(1.0f); + mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model)); mShapeShader.SetUniformf("shapeColor", { color.Red, color.Green, color.Blue, color.Alpha }); glBindVertexArray(mEllipseVAO); @@ -245,6 +249,8 @@ namespace lunarium { mShapeShader.MakeActive(); mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection)); + glm::mat4 model = glm::mat4(1.0f); + mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model)); mShapeShader.SetUniformf("shapeColor", { color.Red, color.Green, color.Blue, color.Alpha }); glBindVertexArray(mEllipseVAO); @@ -271,6 +277,8 @@ namespace lunarium { mShapeShader.MakeActive(); mShapeShader.SetUniformMatrix("projection", 1, glm::value_ptr(mProjection)); + glm::mat4 model = glm::mat4(1.0f); + mShapeShader.SetUniformMatrix("model", 1, glm::value_ptr(model)); mShapeShader.SetUniformf("shapeColor", { color.Red, color.Green, color.Blue, color.Alpha }); glBindVertexArray(mRectVAO); diff --git a/src/internal_libs/assets/CMakeLists.txt b/src/internal_libs/assets/CMakeLists.txt index fc30800..0768e80 100644 --- a/src/internal_libs/assets/CMakeLists.txt +++ b/src/internal_libs/assets/CMakeLists.txt @@ -9,4 +9,5 @@ target_include_directories(assets PUBLIC ../ PUBLIC ../../ PUBLIC ../../../external/glm + PUBLIC ../../../external/box2d/include ) \ No newline at end of file diff --git a/src/internal_libs/utils/CMakeLists.txt b/src/internal_libs/utils/CMakeLists.txt index 017410d..7bb1b3b 100644 --- a/src/internal_libs/utils/CMakeLists.txt +++ b/src/internal_libs/utils/CMakeLists.txt @@ -4,4 +4,5 @@ add_library(utils stb/stb_image_write.cpp stb/stb_image.cpp args.cpp binaryFileB target_include_directories(utils PUBLIC "${PROJECT_BINARY_DIR}" PUBLIC ../../../external/glm + PUBLIC ../../../external/box2d/include ) diff --git a/src/internal_libs/utils/types.cpp b/src/internal_libs/utils/types.cpp index eb013e5..2614d26 100644 --- a/src/internal_libs/utils/types.cpp +++ b/src/internal_libs/utils/types.cpp @@ -35,6 +35,16 @@ namespace lunarium } + Rectangle::Rectangle(b2AABB& box) + { + float hwidth = box.GetExtents().x; + float hheight = box.GetExtents().y; + Left = (box.GetCenter().x - hwidth); + Top = (box.GetCenter().y - hheight); + Width = hwidth * 2; + Height = hheight * 2; + } + Rectangle::Rectangle(float _left, float _top, float _width, float _height) : Left(_left), Top(_top), Width(_width), Height(_height) { diff --git a/src/internal_libs/utils/types.h b/src/internal_libs/utils/types.h index 9cdbee8..812c589 100644 --- a/src/internal_libs/utils/types.h +++ b/src/internal_libs/utils/types.h @@ -11,6 +11,7 @@ #include #include +#include namespace lunarium { @@ -66,7 +67,11 @@ namespace lunarium float right() const { return Left + Width; } float bottom() const { return Top + Height; } + glm::vec2 LeftTop() const { return glm::vec2(Left, Top); } + glm::vec2 RightBottom() const { return glm::vec2(right(), bottom()); } + Rectangle(); + Rectangle(b2AABB& box); Rectangle(float _left, float _top, float _width, float _height); float Area() const; diff --git a/src/run_modes/tester/CMakeLists.txt b/src/run_modes/tester/CMakeLists.txt index 4d816be..3d0ea9d 100644 --- a/src/run_modes/tester/CMakeLists.txt +++ b/src/run_modes/tester/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(tester tester.cpp scenes/baseScene.cpp scenes/simpleRenderScene.cpp) +add_library(tester tester.cpp scenes/baseScene.cpp scenes/simpleRenderScene.cpp scenes/physicsScene.cpp) target_include_directories(tester PUBLIC "${PROJECT_BINARY_DIR}" @@ -7,4 +7,5 @@ target_include_directories(tester PUBLIC ../../../external/glm PUBLIC ../../../external/glad/include PUBLIC ../../../external/glfw/include + PUBLIC ../../../external/box2d/include ) \ No newline at end of file diff --git a/src/run_modes/tester/scenes/physicsScene.cpp b/src/run_modes/tester/scenes/physicsScene.cpp new file mode 100644 index 0000000..4c6bb8c --- /dev/null +++ b/src/run_modes/tester/scenes/physicsScene.cpp @@ -0,0 +1,172 @@ +/****************************************************************************** +* File - physicsScene.h +* Author - Joey Pollack +* Date - 2021/10/27 (y/m/d) +* Mod Date - 2021/10/27 (y/m/d) +* Description - Displays a scene that tests the Box2D physics system +******************************************************************************/ + +#include "physicsScene.h" +#include +#include +#include +#include + +namespace lunarium +{ + PhysicsScene::PhysicsScene(uint32_t logCat) + : BaseScene(logCat), mb2World(nullptr) + { + + } + + PhysicsScene::~PhysicsScene() + { + delete mb2World; + } + + void PhysicsScene::OnLoad() + { + Logger::Log(mLogCat, LogLevel::INFO, "Running Simple Render Test Scene"); + + // Comments from the docs: https://box2d.org/documentation/md__d_1__git_hub_box2d_docs_hello.html + + // Every Box2D program begins with the creation of a b2World object. + // b2World is the physics hub that manages memory, objects, and simulation. + // You can allocate the physics world on the stack, heap, or data section. + + // It is easy to create a Box2D world. First, we define the gravity vector. + b2Vec2 gravity(0.0f, 10.0f); + mb2World = new b2World(gravity); + + /* CREATING THE GOUND BOX + Bodies are built using the following steps: + + 1) Define a body with position, damping, etc. + 2) Use the world object to create the body. + 3) Define fixtures with a shape, friction, density, etc. + 4) Create fixtures on the body. + */ + + // For step 1 we create the ground body. For this we need a body definition. + // With the body definition we specify the initial position of the ground body. + b2BodyDef groundBodyDef; + groundBodyDef.position.Set(30.0f, 50.0f); + + // For step 2 the body definition is passed to the world object to create the ground body. + // The world object does not keep a reference to the body definition. Bodies are static by default. + // Static bodies don't collide with other static bodies and are immovable. + mpGroundBody = mb2World->CreateBody(&groundBodyDef); + + // For step 3 we create a ground polygon. We use the SetAsBox shortcut to form the ground polygon + // into a box shape, with the box centered on the origin of the parent body. + // MORE INFO ABOUT SetAsBox() On the web page (see above) + mpGroundBox = new b2PolygonShape; + mpGroundBox->SetAsBox(20.0f, 5.0f); + + // We finish the ground body in step 4 by creating the shape fixture. For this step we have a shortcut. + // We do not have a need to alter the default fixture material properties, so we can pass the shape directly + // to the body without creating a fixture definition. + mpGroundBody->CreateFixture(mpGroundBox, 0.0f); + + ///////////////////////////////////////////////////////////////////////////// + // So now we have a ground body. We can use the same technique to create a dynamic body. + // The main difference, besides dimensions, is that we must establish the dynamic body's mass properties. + + // First we create the body using CreateBody. By default bodies are static, so we should set the b2BodyType + // at construction time to make the body dynamic. + b2BodyDef bodyDef; + bodyDef.type = b2_dynamicBody; // Caution: You must set the body type to b2_dynamicBody if you want the body to move in response to forces. + bodyDef.position.Set(9.0f, 4.0f); + mpDynamicBody = mb2World->CreateBody(&bodyDef); + + + // Next we create and attach a polygon shape using a fixture definition. First we create a box shape: + mpDynamicBox = new b2PolygonShape; + mpDynamicBox->SetAsBox(1.0f, 1.0f); + + // Next we create a fixture definition using the box. Notice that we set density to 1. + // The default density is zero. Also, the friction on the shape is set to 0.3. + // Caution: A dynamic body should have at least one fixture with a non-zero density. Otherwise you will get strange behavior. + b2FixtureDef fixtureDef; + fixtureDef.shape = mpDynamicBox; + fixtureDef.density = 1.0f; + fixtureDef.friction = 0.3f; + + // Using the fixture definition we can now create the fixture. This automatically updates the mass of the body. + // You can add as many fixtures as you like to a body. Each one contributes to the total mass. + mpDynamicBody->CreateFixture(&fixtureDef); + } + + void PhysicsScene::OnTick(double delta) + { + // TODO: Implement the "hello world" Box2D example and + // use a scaling factor of 10 to render it + float timeStep = 1.0f / 60.0f; + int32 velocityIterations = 6; + int32 positionIterations = 2; + + mb2World->Step(timeStep, velocityIterations, positionIterations); + } + + void PhysicsScene::OnRender(IGraphics* g) + { + // This scaling is a little backwards. + // Info about scaling factor: + // https://box2d.org/documentation/md__d_1__git_hub_box2d_docs__f_a_q.html#autotoc_md139 + float scaleFactor = 10.0f; + + b2AABB groundbox; + mpGroundBox->ComputeAABB(&groundbox, mpGroundBody->GetTransform(), 0); + // float hwidth = groundbox.GetExtents().x; + // float hheight = groundbox.GetExtents().y; + // float left = (groundbox.GetCenter().x - hwidth); + // float top = (groundbox.GetCenter().y - hheight); + // float right = left + hwidth * 2; + // float bot = top + hheight * 2; + + Rectangle groundRect(groundbox); + g->DrawFilledBox(groundRect.LeftTop() * scaleFactor, + groundRect.RightBottom() * scaleFactor, + Color(0.0f, 1.0f, 0.0f, 1.0f)); + + b2AABB dynbox; + mpDynamicBox->ComputeAABB(&dynbox, mpDynamicBody->GetTransform(), 0); + Rectangle dynRect(dynbox); + g->DrawFilledBox(dynRect.LeftTop() * scaleFactor, + dynRect.RightBottom() * scaleFactor, + Color(0.0f, 0.0f, 1.0f, 1.0f)); + + // Debug info + char str[256] = { 0 }; + sprintf(str, "GroundBox: (%f, %f) (%f, %f)", + groundRect.LeftTop().x * scaleFactor, groundRect.LeftTop().y * scaleFactor, + groundRect.RightBottom().x * scaleFactor, groundRect.RightBottom().y * scaleFactor); + g->DrawString(str, Rectangle(10.0f, 10.0f, 800.0f, 30.0f), Color(0.75f, 0.85f, 0.5f, 1.0f), 0.5f); + + sprintf(str, "GroundBox Half Size: (%f, %f)", groundRect.Width, groundRect.Height); + g->DrawString(str, Rectangle(10.0f, 35.0f, 800.0f, 30.0f), Color(0.75f, 0.85f, 0.5f, 1.0f), 0.5f); + + sprintf(str, "DynBox: (%f, %f) (%f, %f)", + dynRect.LeftTop().x * scaleFactor, dynRect.LeftTop().y * scaleFactor, + dynRect.RightBottom().x * scaleFactor, dynRect.RightBottom().y * scaleFactor); + g->DrawString(str, Rectangle(10.0f, 60.0f, 800.0f, 100.0f), Color(0.75f, 0.85f, 0.5f, 1.0f), 0.5f); + + int ww, wh; + Core::MainWindow().GetFramebufferSize(&ww, &wh); + for (int i = 0; i < wh; i += 10) + { + sprintf(str, "%d", i); + g->DrawString(str, Rectangle(ww - 35.0f, i, 4.0f, 5.0f), Color(1.0f, 0.0f, 0.0f, 1.0f), 0.25f); + g->DrawBox(Rectangle(ww - 10.0f, i, 10.0f, 2.0f), Color(1.0f, 0.0f, 0.0f, 1.0f), 1.0f); + } + + for (int i = 0; i < ww; i += 25) + { + sprintf(str, "%d", i); + g->DrawString(str, Rectangle(i, wh - 20.0f, 4.0f, 5.0f), Color(1.0f, 0.0f, 0.0f, 1.0f), 0.25f); + g->DrawBox(Rectangle(i, wh - 5.0f, 2.0f, 5.0f), Color(1.0f, 0.0f, 0.0f, 1.0f), 1.0f); + } + } + +} \ No newline at end of file diff --git a/src/run_modes/tester/scenes/physicsScene.h b/src/run_modes/tester/scenes/physicsScene.h new file mode 100644 index 0000000..0309ec8 --- /dev/null +++ b/src/run_modes/tester/scenes/physicsScene.h @@ -0,0 +1,38 @@ +/****************************************************************************** +* File - physicsScene.h +* Author - Joey Pollack +* Date - 2021/10/27 (y/m/d) +* Mod Date - 2021/10/27 (y/m/d) +* Description - Displays a scene that tests the Box2D physics system +******************************************************************************/ + +#ifndef PHYSICS_SCENE_H_ +#define PHYSICS_SCENE_H_ + +#include "baseScene.h" + +class b2World; +class b2Body; +class b2PolygonShape; + +namespace lunarium +{ + class PhysicsScene : public BaseScene + { + public: + PhysicsScene(uint32_t logCat); + ~PhysicsScene(); + virtual void OnLoad(); + virtual void OnTick(double delta); + virtual void OnRender(IGraphics* g); + + private: + b2World* mb2World; + b2Body* mpGroundBody; + b2PolygonShape* mpGroundBox; + b2Body* mpDynamicBody; + b2PolygonShape* mpDynamicBox; + }; +} + +#endif // PHYSICS_SCENE_H_ \ No newline at end of file diff --git a/src/run_modes/tester/scenes/simpleRenderScene.cpp b/src/run_modes/tester/scenes/simpleRenderScene.cpp index 3f50f2b..6893da9 100644 --- a/src/run_modes/tester/scenes/simpleRenderScene.cpp +++ b/src/run_modes/tester/scenes/simpleRenderScene.cpp @@ -23,6 +23,7 @@ namespace lunarium } void SimpleRenderScene::OnLoad() { + Logger::Log(mLogCat, LogLevel::INFO, "Running Simple Render Test Scene"); mTextBoxWidth = 500; // Currently the full default window size diff --git a/src/run_modes/tester/tester.cpp b/src/run_modes/tester/tester.cpp index 7613216..37e0db1 100644 --- a/src/run_modes/tester/tester.cpp +++ b/src/run_modes/tester/tester.cpp @@ -8,6 +8,7 @@ #include "tester.h" #include "scenes/simpleRenderScene.h" +#include "scenes/physicsScene.h" #include #include @@ -28,14 +29,14 @@ namespace lunarium mLogCat = Logger::RegisterCategory("TESTER"); #if BUILD_NO_EDITOR - Logger::Log(mLogCat, LogLevel::INFO, "BUILDING NO EDITOR!"); + Logger::Log(mLogCat, LogLevel::INFO, "BUILDING NO EDITOR!"); #else - Logger::Log(mLogCat, LogLevel::INFO, "BUILDING WITH THE EDITOR!"); + Logger::Log(mLogCat, LogLevel::INFO, "BUILDING WITH THE EDITOR!"); #endif - Logger::Log(mLogCat, LogLevel::INFO, "Running Simple Render Test Scene"); - mpScene = new SimpleRenderScene(mLogCat); - mpScene->OnLoad(); + // mpScene = new SimpleRenderScene(mLogCat); + mpScene = new PhysicsScene(mLogCat); + mpScene->OnLoad(); return OpRes::OK();