LCOV - code coverage report
Current view: top level - src/simlab - WreckingBallScenario.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.1 % 77 74
Test Date: 2026-04-10 19:03:25 Functions: 100.0 % 5 5

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2025 aeml
       3              :  *
       4              :  * This program is free software: you can redistribute it and/or modify
       5              :  * it under the terms of the GNU General Public License as published by
       6              :  * the Free Software Foundation, either version 3 of the License, or
       7              :  * (at your option) any later version.
       8              :  *
       9              :  * This program is distributed in the hope that it will be useful,
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12              :  * GNU General Public License for more details.
      13              :  *
      14              :  * You should have received a copy of the GNU General Public License
      15              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      16              :  */
      17              : 
      18              : #include "simlab/Scenario.hpp"
      19              : #include "ecs/World.hpp"
      20              : #include "physics/Components.hpp"
      21              : #include "physics/Systems.hpp"
      22              : #include "jobs/JobSystem.hpp"
      23              : #include "ascii/TextRenderer.hpp"
      24              : #include <vector>
      25              : #include <cmath>
      26              : #include <iostream>
      27              : 
      28              : namespace simlab
      29              : {
      30              :     class WreckingBallScenario : public IScenario
      31              :     {
      32              :     public:
      33            6 :         void Setup(ecs::World& world) override
      34              :         {
      35            6 :             m_renderer = std::make_unique<ascii::TextRenderer>(80, 30);
      36              :             
      37            6 :             physics::EnvironmentForces env;
      38            6 :             env.gravityY = -15.0f;
      39            6 :             env.drag = 0.01f;
      40              :             
      41            6 :             auto physicsSystem = std::make_unique<physics::PhysicsSystem>();
      42            6 :             physics::PhysicsSettings settings;
      43            6 :             settings.substeps = 16;
      44            6 :             settings.constraintIterations = 16; // High for stable chains
      45            6 :             physicsSystem->SetSettings(settings);
      46            6 :             physicsSystem->SetEnvironment(env);
      47            6 :             physicsSystem->SetJobSystem(&m_jobSystem);
      48            6 :             world.AddSystem(std::move(physicsSystem));
      49              : 
      50              :             // Floor
      51            6 :             auto floor = world.CreateEntity();
      52            6 :             world.AddComponent<physics::TransformComponent>(floor, 0.0f, -10.0f, 0.0f);
      53            6 :             auto& fb = world.AddComponent<physics::RigidBodyComponent>(floor);
      54            6 :             fb.mass = 0.0f; fb.invMass = 0.0f;
      55            6 :             world.AddComponent<physics::AABBComponent>(floor, -40.0f, -11.0f, 40.0f, -9.0f);
      56              : 
      57              :             // Wall of boxes
      58            6 :             float boxSize = 1.5f;
      59           54 :             for (int y = 0; y < 8; ++y)
      60              :             {
      61          336 :                 for (int x = 0; x < 6; ++x)
      62              :                 {
      63          288 :                     auto box = world.CreateEntity();
      64          288 :                     const float xPos = 5.0f + x * boxSize;
      65          288 :                     const float yPos = -9.0f + y * boxSize + boxSize / 2;
      66          288 :                     world.AddComponent<physics::TransformComponent>(box, xPos, yPos, 0.0f);
      67          288 :                     auto& b = world.AddComponent<physics::RigidBodyComponent>(box);
      68          288 :                     b.mass = 1.0f; b.invMass = 1.0f; b.friction = 0.6f;
      69          288 :                     world.AddComponent<physics::AABBComponent>(
      70              :                         box,
      71            0 :                         xPos - boxSize / 2,
      72            0 :                         yPos - boxSize / 2,
      73            0 :                         xPos + boxSize / 2,
      74          288 :                         yPos + boxSize / 2);
      75          288 :                     physics::ConfigureBoxInertia(b, boxSize, boxSize);
      76              :                 }
      77              :             }
      78              : 
      79              :             // Wrecking Ball Chain
      80            6 :             ecs::EntityId prevLink = world.CreateEntity(); // Anchor
      81            6 :             world.AddComponent<physics::TransformComponent>(prevLink, -10.0f, 10.0f, 0.0f);
      82            6 :             auto& ab = world.AddComponent<physics::RigidBodyComponent>(prevLink);
      83            6 :             ab.mass = 0.0f; ab.invMass = 0.0f;
      84              : 
      85            6 :             int chainLen = 10;
      86            6 :             float linkLen = 1.5f;
      87           66 :             for (int i = 0; i < chainLen; ++i)
      88              :             {
      89           60 :                 auto link = world.CreateEntity();
      90           60 :                 world.AddComponent<physics::TransformComponent>(link, -10.0f + (i+1)*1.0f, 10.0f - (i+1)*1.0f, 0.0f); // Initial swing pos
      91           60 :                 auto& lb = world.AddComponent<physics::RigidBodyComponent>(link);
      92              :                 
      93           60 :                 if (i == chainLen - 1) {
      94              :                     // Heavy Ball
      95            6 :                     lb.mass = 50.0f; lb.invMass = 1.0f/50.0f;
      96            6 :                     world.AddComponent<physics::CircleColliderComponent>(link, 2.0f);
      97            6 :                     physics::ConfigureCircleInertia(lb, 2.0f);
      98              :                 } else {
      99              :                     // Chain link
     100           54 :                     lb.mass = 0.5f; lb.invMass = 2.0f;
     101           54 :                     world.AddComponent<physics::CircleColliderComponent>(link, 0.2f);
     102              :                 }
     103              : 
     104           60 :                 auto& joint = world.AddComponent<physics::DistanceJointComponent>(link);
     105           60 :                 joint.entityA = prevLink;
     106           60 :                 joint.entityB = link;
     107           60 :                 joint.targetDistance = linkLen;
     108           60 :                 joint.compliance = 0.0f; // Rigid chain
     109              : 
     110           60 :                 prevLink = link;
     111              :             }
     112            6 :         }
     113              : 
     114          544 :         void Update(ecs::World& world, float dt) override
     115              :         {
     116              :             (void)world;
     117              :             (void)dt;
     118          544 :         }
     119              : 
     120          183 :         void Render(ecs::World& world, std::ostream& out) override
     121              :         {
     122          183 :             m_renderer->Clear();
     123              :             
     124              :             // Draw Floor
     125        14823 :             for(int x=0; x<80; ++x) m_renderer->Put(x, 29, '#');
     126              : 
     127              :             // Draw Entities
     128          183 :             world.ForEach<physics::TransformComponent>([&](ecs::EntityId id, physics::TransformComponent& t) {
     129              :                 // Map -20..20 to 0..80
     130        11081 :                 int sx = static_cast<int>((t.x + 20.0f) * 2.0f);
     131        11081 :                 int sy = static_cast<int>(30.0f - (t.y + 10.0f));
     132              : 
     133        11081 :                 if (sx >= 0 && sx < 80 && sy >= 0 && sy < 30)
     134              :                 {
     135        10688 :                     char c = '*';
     136        10688 :                     if (world.GetComponent<physics::AABBComponent>(id)) c = '#'; // Box
     137        10688 :                     if (world.GetComponent<physics::DistanceJointComponent>(id)) c = '.'; // Chain
     138        10688 :                     if (auto* rb = world.GetComponent<physics::RigidBodyComponent>(id)) {
     139        10688 :                         if (rb->mass > 10.0f) c = 'O'; // Wrecking ball
     140              :                     }
     141        10688 :                     m_renderer->Put(sx, sy, c);
     142              :                 }
     143        11081 :             });
     144              : 
     145          183 :             m_renderer->SetHeadless(simlab::IsHeadlessRendering());
     146          183 :             m_renderer->PresentDiff(out);
     147          183 :         }
     148              : 
     149              :     private:
     150              :         std::unique_ptr<ascii::TextRenderer> m_renderer;
     151              :         jobs::JobSystem m_jobSystem;
     152              :     };
     153              : 
     154            8 :     std::unique_ptr<IScenario> CreateWreckingBallScenario()
     155              :     {
     156            8 :         return std::make_unique<WreckingBallScenario>();
     157              :     }
     158              : }
        

Generated by: LCOV version 2.0-1