hx3d  1
2D/3D Simple Game Framework
world.cpp
1 #include "hx3d/physics/2d/world.hpp"
2 
3 #include "hx3d/graphics/sprite.hpp"
4 
5 #include "hx3d/math/vector_utils.hpp"
6 #include "hx3d/utils/algorithm.hpp"
7 
8 using namespace hx3d::graphics;
9 
10 namespace hx3d {
11 namespace physics2d {
12 
13 World::World(const glm::vec2 globalGravity, const unsigned int iterations, const float physRatio):
14  _iterations(iterations), _physRatio(physRatio)
15  {
16  _attractors.emplace_back(Make<GlobalAttractor>(globalGravity));
17  }
18 
19 void World::addAttractor(const Ptr<Attractor>& attractor) {
20  _attractors.push_back(attractor);
21 }
22 
23 void World::addCollider(const Ptr<Collider>& collider) {
24  _colliders.push_back(collider);
25 }
26 
28  _listeners.push_back(listener);
29 }
30 
31 void World::removeCollider(const Ptr<Collider>& collider) {
32  _colliders.erase(std::remove(_colliders.begin(), _colliders.end(), collider), _colliders.end());
33 }
34 
35 void World::step(float dt) {
36 
37  _contacts.clear();
38  _inContact.clear();
39 
40  for (unsigned int i = 0; i < _colliders.size(); ++i) {
41  const Ptr<Collider>& a = _colliders[i];
42 
43  for (unsigned int j = i+1; j < _colliders.size(); ++j) {
44  const Ptr<Collider>& b = _colliders[j];
45  if (a->massData.invMass == 0 && b->massData.invMass == 0)
46  continue;
47 
48  Manifold m(a, b);
49  m.solve();
50 
51  if (m.contacts.size() > 0) {
52 
53  // Ajout dans les contacts
54  _inContact.insert(m);
55 
56  if ((m.a->mask & m.b->category) || (m.b->mask & m.a->category)) {
57  // Contact
58  } else {
59  if (m.a->category != 0 && m.b->category != 0)
60  m.disabled = true;
61  }
62 
63  if (prevContactExists(m)) {
64 
65  // IN
66  algo::apply(_listeners, [&m](Ptr<CollisionListener>& listener) {
67  listener->duringCollision(m);
68  });
69 
70  } else {
71 
72  // BEGIN
73  algo::apply(_listeners, [&m](Ptr<CollisionListener>& listener) {
74  listener->beginCollision(m);
75  });
76 
77  }
78 
79  _contacts.push_back(m);
80  }
81  }
82  }
83 
84  // DiffĂ©rence entre les deux sets => END
85  checkOldContacts();
86 
87  // Sauvegarde des contacts
88  _inPrevContact.clear();
89  algo::clone(_inContact, _inPrevContact);
90 
91  // Integrate
92  for (unsigned int i = 0; i < _colliders.size(); ++i) {
93  integrateForces(_colliders[i], dt);
94  }
95 
96  for (unsigned int i = 0; i < _contacts.size(); ++i) {
97  _contacts[i].initialize();
98  }
99 
100  for (unsigned int j = 0; j < _iterations; ++j) {
101  for (unsigned int i = 0; i < _contacts.size(); ++i) {
102  _contacts[i].applyImpulse();
103  }
104  }
105 
106  for (unsigned int i = 0; i < _colliders.size(); ++i) {
107  integrateVelocity(_colliders[i], dt);
108  }
109 
110  for (unsigned int i = 0; i < _contacts.size(); ++i) {
111  _contacts[i].positionalCorrection();
112  }
113 
114  for (unsigned int i = 0; i < _colliders.size(); ++i) {
115  const Ptr<Collider>& c = _colliders[i];
116  c->force = {0, 0};
117  c->gravityForce = {0.f, 0.f};
118  c->torque = 0;
119  }
120 }
121 
123 
124  for (unsigned int i = 0; i < _attractors.size(); ++i) {
125  const Ptr<Attractor>& attractor = _attractors[i];
126 
127  if (attractor->type == Attractor::Type::Zone) {
128  const Ptr<ZoneAttractor>& zone = std::dynamic_pointer_cast<ZoneAttractor>(attractor);
129  Sprite sprite;
130  sprite.setTexture(Texture::Blank);
131  sprite.transform.position.x = zone->position.x * _physRatio;
132  sprite.transform.position.y = zone->position.y * _physRatio;
133  sprite.transform.position.z = -0.25f;
134  sprite.transform.size.x = zone->width * _physRatio;
135  sprite.transform.size.y = zone->height * _physRatio;
136 
137  sprite.setTint(Color(240, 20, 201));
138  batch.draw(sprite);
139  }
140 
141  else if (attractor->type == Attractor::Type::Point) {
142  const Ptr<PointAttractor>& point = std::dynamic_pointer_cast<PointAttractor>(attractor);
143  Sprite sprite;
144  sprite.setTexture(Texture::Blank);
145  sprite.transform.position.x = point->position.x * _physRatio;
146  sprite.transform.position.y = point->position.y * _physRatio;
147  sprite.transform.position.z = -0.25f;
148  sprite.transform.size.x = point->radius * 2 * _physRatio;
149  sprite.transform.size.y = point->radius * 2 * _physRatio;
150 
151  sprite.setTint(Color(20, 240, 201));
152  batch.draw(sprite);
153  }
154  }
155 
156  for (unsigned int i = 0; i < _colliders.size(); ++i) {
157  const Ptr<Collider>& c = _colliders[i];
158 
159  Sprite sprite;
160  sprite.setTexture(Texture::Blank);
161  sprite.transform.position.x = c->position.x * _physRatio;
162  sprite.transform.position.y = c->position.y * _physRatio;
163 
164  if (c->shape == Collider::Shape::Polygon) {
165  const Ptr<colliders::Polygon>& b = std::dynamic_pointer_cast<colliders::Polygon>(c);
166  if (b->box) {
167  float w = b->vertices[1].x * 2;
168  float h = b->vertices[2].y * 2;
169  sprite.transform.size.x = w * _physRatio;
170  sprite.transform.size.y = h * _physRatio;
171  }
172  }
173 
174  else if (c->shape == Collider::Shape::Circle) {
175  const Ptr<colliders::Circle>& b = std::dynamic_pointer_cast<colliders::Circle>(c);
176  sprite.transform.size.x = (b->radius / 2) * _physRatio;
177  sprite.transform.size.y = (b->radius / 2) * _physRatio;
178  }
179 
180  sprite.transform.rotation.z = c->orientation;
181 
182  if (c->type == Collider::Type::Static)
183  sprite.setTint(Color::Blue);
184  else
185  sprite.setTint(Color::Red);
186 
187  batch.draw(sprite);
188  }
189 
190  for (unsigned int i = 0; i < _contacts.size(); ++i) {
191  const Manifold& m = _contacts[i];
192 
193  for (unsigned int j = 0; j < m.contacts.size(); ++j) {
194  const glm::vec2& contact = m.contacts[j];
195 
196  Sprite sprite;
197  sprite.setTexture(Texture::Blank);
198  sprite.setTint(Color::Green);
199  sprite.transform.position.x = contact.x * _physRatio;
200  sprite.transform.position.y = contact.y * _physRatio;
201  sprite.transform.position.z = 0.5f;
202  sprite.transform.size.x = 5;
203  sprite.transform.size.y = 5;
204 
205  batch.draw(sprite);
206  }
207  }
208 
209  for (unsigned int i = 0; i < _contacts.size(); ++i) {
210  const Manifold& m = _contacts[i];
211 
212  for (unsigned int j = 0; j < m.contacts.size(); ++j) {
213  const glm::vec2& contact = m.contacts[j];
214 
215  Sprite sprite;
216  sprite.setTexture(Texture::Blank);
217  sprite.setTint(Color(255, 0, 255));
218  sprite.transform.size.x = 3;
219  sprite.transform.size.y = 20;
220  sprite.transform.position.x = contact.x * _physRatio;
221  sprite.transform.position.y = contact.y * _physRatio;
222  sprite.transform.position.z = 0.75f;
223  sprite.transform.rotation.z = math::angleBetweenVecs(glm::vec2(0, 1), m.normal);
224 
225  batch.draw(sprite);
226  }
227  }
228 }
229 
230 float World::getPhysRatio() const {
231  return _physRatio;
232 }
233 
235  return std::dynamic_pointer_cast<GlobalAttractor>(_attractors[0]);
236 }
237 
239  return _collisionMatrix;
240 }
241 
243 
244 void World::integrateForces(const Ptr<Collider>& c, float dt) {
245  if (c->massData.invMass == 0.f)
246  return;
247 
248  for (auto attractor: _attractors) {
249  Attractor::applyForce(c, attractor, dt);
250  }
251 
252  // c->velocity += c->gravityForce;
253  c->velocity += ((c->force * c->massData.invMass) + (c->gravityForce * 2.f)) * (dt / 2.f);
254  if (!c->fixedRotation) {
255  c->angularVelocity += c->torque * c->massData.invInertia * (dt / 2.f);
256  }
257 }
258 
259 void World::integrateVelocity(const Ptr<Collider>& c, float dt) {
260  if (c->type != Collider::Type::Kinematic && c->massData.invMass == 0.f)
261  return;
262 
263  c->position += c->velocity * dt;
264 
265  // Calcul de l'angle
266  //
267  // glm::vec2 nP = glm::vec2(0, -1);
268  // glm::vec2 nG = glm::normalize(c->gravityForce);
269  // float angle = 0.f;
270  // float a = glm::dot(nP, nG);
271  //
272  // if (nG.x < 0) {
273  // angle = -std::acos(a);
274  // } else {
275  // angle = std::acos(a);
276  // }
277  //
278  // c->setOrientation(angle);
279 
280  if (!c->fixedRotation) {
281  c->orientation += c->angularVelocity * dt;
282  c->setOrientation(c->orientation);
283  }
284 
285  integrateForces(c, dt);
286 }
287 
288 bool World::prevContactExists(Manifold& m) {
289  bool result = false;
290  for (auto& ma: _inPrevContact) {
291  if (ma.a == m.a && ma.b == m.b)
292  {
293  result = true;
294  break;
295  }
296  }
297 
298  return result;
299 }
300 
301 void World::checkOldContacts() {
302  std::set<Manifold> diffSet;
303  for (auto& manif: _inPrevContact) {
304  bool inside = false;
305  for (auto& curr: _contacts) {
306  if (curr.a == manif.a && curr.b == manif.b) {
307  inside = true;
308  }
309  }
310 
311  if (!inside) {
312  diffSet.insert(manif);
313  }
314  }
315 
316  algo::apply(diffSet, [this](Manifold manif) {
317  algo::apply(_listeners, [&manif](Ptr<CollisionListener> listener) {
318  listener->endCollision(manif);
319  });
320  });
321 }
322 
323 } /* physics2d */
324 } /* hx3d */
void clone(Source &src, Dest &dst)
Clone a container.
Static: no forces or velocity.
void setTint(Color tint)
Set the mesh tint.
Definition: mesh.cpp:43
void addListener(const Ptr< CollisionListener > &listener)
Add a collision listener.
Definition: world.cpp:27
virtual void draw(Mesh &mesh)=0
Draw the mesh.
Four [0..255] components defined color.
Definition: color.hpp:32
glm::vec3 position
Position.
Definition: transform.hpp:80
void setTexture(const Ptr< Texture > &texture)
Set the sprite texture.
Definition: sprite.cpp:36
static void applyForce(const Ptr< Collider > &collider, const Ptr< Attractor > &attractor, const float dt)
Apply an attractor force on a collider.
Definition: attractor.cpp:18
Kinematic: no forces but velocity.
void addAttractor(const Ptr< Attractor > &attractor)
Add an attractor.
Definition: world.cpp:19
std::vector< glm::vec2 > contacts
Contact points.
Definition: manifold.hpp:49
2D and 3D graphics components.
Definition: animation.hpp:28
hx3d framework namespace
Definition: audio.hpp:26
CollisionMatrix & getCollisionMatrix()
Get the collision matrix.
Definition: world.cpp:238
Ptr< Collider > b
Second collider.
Definition: manifold.hpp:38
void render(graphics::BaseBatch &batch)
Render a debug view of the physical simulation.
Definition: world.cpp:122
std::vector< glm::vec2 > vertices
Vertices.
Definition: polygon.hpp:74
glm::vec3 rotation
Rotation.
Definition: transform.hpp:86
Collision matrix with masks and category.
Circle shaped collider.
Definition: circle.hpp:37
bool disabled
Is the contact disabled ?
Definition: manifold.hpp:44
bool solve()
Solve the contact.
Definition: manifold.cpp:19
Contact manifold definition.
Definition: manifold.hpp:33
glm::vec3 size
Size.
Definition: transform.hpp:84
2D texture manipulation.
Definition: sprite.hpp:62
Transform transform
Mesh transformation.
Definition: mesh.hpp:90
Ptr< Collider > a
First collider.
Definition: manifold.hpp:36
float angleBetweenVecs(const glm::vec2 vec1, const glm::vec2 vec2)
Compute the angle between two 2D vectors.
void apply(Container &container, Function func)
Function application helper on a container.
const Ptr< GlobalAttractor > getGlobalGravity()
Get the global attractor.
Definition: world.cpp:234
float getPhysRatio() const
Get the physical ratio.
Definition: world.cpp:230
Draw meshes and texts on screen.
Definition: base_batch.hpp:41
Polygon or box shaped collider.
Definition: polygon.hpp:35
void removeCollider(const Ptr< Collider > &collider)
Remove a collider.
Definition: world.cpp:31
void step(float dt=1.f/60.f)
Step the physical simulation.
Definition: world.cpp:35
std::shared_ptr< T > Ptr
Quick-typing shared ptr.
Definition: ptr.hpp:34
glm::vec2 normal
Normal vector.
Definition: manifold.hpp:47
void addCollider(const Ptr< Collider > &collider)
Add a collider.
Definition: world.cpp:23