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 <random>
26 : #include <iostream>
27 :
28 : namespace simlab
29 : {
30 : class ParticleFluidScenario : public IScenario
31 : {
32 : public:
33 5 : void Setup(ecs::World& world) override
34 : {
35 5 : m_renderer = std::make_unique<ascii::TextRenderer>(80, 40);
36 :
37 5 : physics::EnvironmentForces env;
38 5 : env.gravityY = -9.81f;
39 :
40 5 : auto physicsSystem = std::make_unique<physics::PhysicsSystem>();
41 5 : physics::PhysicsSettings settings;
42 5 : settings.substeps = 8; // Increased substeps for stability
43 5 : physicsSystem->SetSettings(settings);
44 5 : physicsSystem->SetEnvironment(env);
45 5 : physicsSystem->SetJobSystem(&m_jobSystem);
46 5 : world.AddSystem(std::move(physicsSystem));
47 :
48 : // Container (Closed box)
49 : // Visible range: X[-20, 20], Y[-15, 25]
50 : // Walls made very thick (100.0f) to prevent tunneling
51 : // Inner faces at: Left -18.5, Right 18.5, Bottom -13.5, Top 23.5
52 5 : CreateWall(world, -68.5f, 5.0f, 100.0f, 40.0f); // Left
53 5 : CreateWall(world, 68.5f, 5.0f, 100.0f, 40.0f); // Right
54 5 : CreateWall(world, 0.0f, -63.5f, 40.0f, 100.0f); // Bottom
55 5 : CreateWall(world, 0.0f, 73.5f, 40.0f, 100.0f); // Top
56 :
57 : // Particles
58 5 : std::mt19937 rng(123);
59 5 : std::uniform_real_distribution<float> distX(-15.0f, 15.0f);
60 5 : std::uniform_real_distribution<float> distY(-5.0f, 15.0f);
61 :
62 505 : for (int i = 0; i < 100; ++i)
63 : {
64 500 : float x = distX(rng);
65 500 : float y = distY(rng);
66 500 : auto p = world.CreateEntity();
67 500 : world.AddComponent<physics::TransformComponent>(p, x, y, 0.0f);
68 500 : auto& b = world.AddComponent<physics::RigidBodyComponent>(p);
69 500 : b.mass = 0.1f; b.invMass = 10.0f; b.restitution = 0.9f; b.friction = 0.0f; // High restitution for bouncing
70 500 : b.lastX = x;
71 500 : b.lastY = y;
72 500 : world.AddComponent<physics::CircleColliderComponent>(p, 0.3f);
73 : }
74 5 : }
75 :
76 20 : void CreateWall(ecs::World& world, float x, float y, float w, float h)
77 : {
78 20 : auto wall = world.CreateEntity();
79 20 : world.AddComponent<physics::TransformComponent>(wall, x, y, 0.0f);
80 20 : auto& b = world.AddComponent<physics::RigidBodyComponent>(wall);
81 20 : b.mass = 0.0f; b.invMass = 0.0f;
82 : // AABB must be in world space. Since static bodies don't update AABB, we must set it correctly here.
83 20 : world.AddComponent<physics::AABBComponent>(wall, x - w/2, y - h/2, x + w/2, y + h/2);
84 20 : }
85 :
86 542 : void Update(ecs::World& world, float dt) override
87 : {
88 : (void)world;
89 : (void)dt;
90 542 : }
91 :
92 181 : void Render(ecs::World& world, std::ostream& out) override
93 : {
94 181 : m_renderer->Clear();
95 :
96 181 : world.ForEach<physics::TransformComponent>([&](ecs::EntityId id, physics::TransformComponent& t) {
97 : // Map -20..20 to 0..80
98 18985 : int sx = static_cast<int>((t.x + 20.0f) * 2.0f);
99 18985 : int sy = static_cast<int>(40.0f - (t.y + 15.0f));
100 :
101 18985 : if (sx >= 0 && sx < 80 && sy >= 0 && sy < 40)
102 : {
103 18226 : char c = '.';
104 18226 : if (world.GetComponent<physics::AABBComponent>(id)) c = '#';
105 18226 : m_renderer->Put(sx, sy, c);
106 : }
107 18985 : });
108 :
109 181 : m_renderer->SetHeadless(simlab::IsHeadlessRendering());
110 181 : m_renderer->PresentDiff(out);
111 181 : }
112 :
113 : private:
114 : std::unique_ptr<ascii::TextRenderer> m_renderer;
115 : jobs::JobSystem m_jobSystem;
116 : };
117 :
118 8 : std::unique_ptr<IScenario> CreateParticleFluidScenario()
119 : {
120 8 : return std::make_unique<ParticleFluidScenario>();
121 : }
122 : }
|