/****************************************************************************** * File - physics_scene.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 "physics_scene.h" #include #include #include namespace lunarium { PhysicsScene::PhysicsScene(uint32_t logCat) : BaseScene(logCat), mb2World(nullptr) { } PhysicsScene::~PhysicsScene() { delete mb2World; } void PhysicsScene::OnLoad() { Logger::Info(mLogCat, "Running Physics 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(60.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); // Create a second ground body groundBodyDef.position.Set(20.0f, 70.0f); mpGroundBody2 = mb2World->CreateBody(&groundBodyDef); mpGroundBox2 = new b2PolygonShape; mpGroundBox2->SetAsBox(30.0f, 5.0f); mpGroundBody2->CreateFixture(mpGroundBox2, 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(39.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); RenderScene(); } void PhysicsScene::RenderScene() { Renderer2D& g = Core::Graphics(); // 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); Rectangle groundRect(groundbox); groundRect.Scale(scaleFactor, scaleFactor); groundRect.X *= scaleFactor; groundRect.Y *= scaleFactor; g.DrawQuad(groundRect, Color(0.0f, 1.0f, 0.0f, 1.0f)); b2AABB groundbox2; mpGroundBox2->ComputeAABB(&groundbox2, mpGroundBody2->GetTransform(), 0); Rectangle groundRect2(groundbox2); groundRect2.Scale(scaleFactor, scaleFactor); groundRect2.X *= scaleFactor; groundRect2.Y *= scaleFactor; g.DrawQuad(groundRect2, Color(0.0f, 1.0f, 0.0f, 1.0f)); b2AABB dynbox; b2Transform transform; transform.p = mpDynamicBody->GetTransform().p; transform.q = b2Rot(0.0f); mpDynamicBox->ComputeAABB(&dynbox, transform, 0); Rectangle dynRect(dynbox); float angle = mpDynamicBody->GetAngle(); dynRect.Scale(scaleFactor, scaleFactor); dynRect.X *= scaleFactor; dynRect.Y *= scaleFactor; g.DrawQuad(dynRect, Color(0.0f, 0.0f, 1.0f, 1.0f), nullptr, angle); // Debug info // char str[256] = { 0 }; // sprintf(str, "GroundBox: pos: (%f, %f) size: (%f, %f)", groundRect.X, groundRect.Y, groundRect.HalfWidth, groundRect.HalfHeight); // g.DrawString(str, Rectangle::MakeFromTopLeft(10.0f, 10.0f, 800.0f, 30.0f), Color(0.75f, 0.85f, 0.5f, 1.0f), 0.4f); // 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.4f); // sprintf(str, "DynBox: (%f, %f) (%f, %f), Angle: %f", // dynRect.LeftTop().x * scaleFactor, dynRect.LeftTop().y * scaleFactor, // dynRect.RightBottom().x * scaleFactor, dynRect.RightBottom().y * scaleFactor, angle); // g->DrawString(str, Rectangle(10.0f, 60.0f, 1000.0f, 100.0f), Color(0.75f, 0.85f, 0.5f, 1.0f), 0.4); // int ww, wh; // Core::MainWindow().GetFramebufferSize(&ww, &wh); // for (int i = 0; i < wh; i += 10) // { // sprintf(str, "%d", i); // g.DrawString(str, Rectangle::MakeFromTopLeft(ww - 35.0f, i, 4.0f, 5.0f), Color(1.0f, 0.0f, 0.0f, 1.0f), 0.25f); // g.DrawBox(Rectangle::MakeFromTopLeft(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::MakeFromTopLeft(i, wh - 20.0f, 4.0f, 5.0f), Color(1.0f, 0.0f, 0.0f, 1.0f), 0.25f); // g.DrawBox(Rectangle::MakeFromTopLeft(i, wh - 5.0f, 2.0f, 5.0f), Color(1.0f, 0.0f, 0.0f, 1.0f), 1.0f); // } } }