1 // This file belongs to the "MiniCore" game engine.
2 // Copyright (C) 2010 Jussi Lind <jussi.lind@iki.fi>
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (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, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 // MA 02110-1301, USA.
18 //
19
20 #include "mcworld.hh"
21
22 #include "mcbbox.hh"
23 #include "mccamera.hh"
24 #include "mccollisiondetector.hh"
25 #include "mcforcegenerator.hh"
26 #include "mcforceregistry.hh"
27 #include "mcfrictiongenerator.hh"
28 #include "mcimpulsegenerator.hh"
29 #include "mcmathutil.hh"
30 #include "mcobject.hh"
31 #include "mcobjectgrid.hh"
32 #include "mcparticle.hh"
33 #include "mcphysicscomponent.hh"
34 #include "mcrectshape.hh"
35 #include "mcshape.hh"
36 #include "mcshapeview.hh"
37 #include "mctrigonom.hh"
38 #include "mcworldrenderer.hh"
39
40 #include <cassert>
41 #include <iostream>
42
43 MCWorld * MCWorld::m_instance = nullptr;
44
45 float MCWorld::m_metersPerUnit = 1.0;
46
47 float MCWorld::m_metersPerUnitSquared = 1.0;
48
49 namespace {
50 const int REMOVED_INDEX = -1;
51 }
52
MCWorld()53 MCWorld::MCWorld()
54 : m_renderer(new MCWorldRenderer)
55 , m_forceRegistry(new MCForceRegistry)
56 , m_collisionDetector(new MCCollisionDetector)
57 , m_impulseGenerator(new MCImpulseGenerator)
58 , m_minX(0)
59 , m_maxX(0)
60 , m_minY(0)
61 , m_maxY(0)
62 , m_minZ(0)
63 , m_maxZ(0)
64 , m_numCollisions(0)
65 , m_resolverLoopCount(5)
66 , m_resolverStep(1.0f / m_resolverLoopCount)
67 , m_gravity(MCVector3dF(0, 0, -9.81f))
68 {
69 if (!MCWorld::m_instance)
70 {
71 MCWorld::m_instance = this;
72 }
73 else
74 {
75 std::cerr << "ERROR!!: Only one MCWorld can exist!" << std::endl;
76 exit(EXIT_FAILURE);
77 }
78
79 // Default dimensions. Creates also MCObjectGrid.
80 setDimensions(0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 1.0);
81 }
82
~MCWorld()83 MCWorld::~MCWorld()
84 {
85 clear();
86
87 delete m_renderer;
88 delete m_forceRegistry;
89 delete m_collisionDetector;
90 delete m_impulseGenerator;
91
92 MCWorld::m_instance = nullptr;
93 }
94
integrate(int step)95 void MCWorld::integrate(int step)
96 {
97 // Integrate and update all registered objects
98 m_forceRegistry->update();
99 for (auto && object : m_objects)
100 {
101 if (object->isPhysicsObject() && !object->physicsComponent().isStationary())
102 {
103 object->physicsComponent().stepTime(step);
104 }
105
106 object->onStepTime(step);
107 }
108 }
109
generateImpulses()110 void MCWorld::generateImpulses()
111 {
112 m_impulseGenerator->generateImpulsesFromDeepestContacts(m_objects);
113 }
114
resolvePositions(float accuracy)115 void MCWorld::resolvePositions(float accuracy)
116 {
117 m_impulseGenerator->resolvePositions(m_objects, accuracy);
118 }
119
prepareRendering(MCCamera * camera)120 void MCWorld::prepareRendering(MCCamera * camera)
121 {
122 m_renderer->buildBatches(camera);
123 }
124
render(MCCamera * camera,MCRenderGroup renderGroup)125 void MCWorld::render(MCCamera * camera, MCRenderGroup renderGroup)
126 {
127 m_renderer->render(camera, renderGroup);
128 }
129
hasInstance()130 bool MCWorld::hasInstance()
131 {
132 return MCWorld::m_instance;
133 }
134
instance()135 MCWorld & MCWorld::instance()
136 {
137 if (!MCWorld::m_instance)
138 {
139 std::cerr << "ERROR!!: MCWorld instance not created!" << std::endl;
140 exit(EXIT_FAILURE);
141 }
142
143 return *MCWorld::m_instance;
144 }
145
clear()146 void MCWorld::clear()
147 {
148 // This does the same as removeObject(), but the removal
149 // process here is simpler as all data structures will be
150 // cleared and all objects will be removed at once.
151 for (auto && object : m_objects)
152 {
153 object->deleteContacts();
154 object->physicsComponent().reset();
155 object->setIndex(REMOVED_INDEX);
156
157 if (object->isParticle())
158 {
159 static_cast<MCParticle *>(object)->die();
160 }
161 }
162
163 m_renderer->clear();
164 m_objectGrid->removeAll();
165 m_objects.clear();
166 m_removeObjs.clear();
167 m_collisionDetector->clear();
168 }
169
setDimensions(float minX,float maxX,float minY,float maxY,float minZ,float maxZ,float metersPerUnit,bool addAreaWalls,size_t gridSize)170 void MCWorld::setDimensions(
171 float minX, float maxX, float minY, float maxY, float minZ, float maxZ,
172 float metersPerUnit, bool addAreaWalls, size_t gridSize)
173 {
174 assert(maxX - minX > 0);
175 assert(maxY - minY > 0);
176 assert(maxZ - minZ > 0);
177
178 MCWorld::setMetersPerUnit(metersPerUnit);
179
180 // Set dimensions
181 m_minX = minX;
182 m_maxX = maxX;
183 m_minY = minY;
184 m_maxY = maxY;
185 m_minZ = minZ;
186 m_maxZ = maxZ;
187
188 // Init objectGrid
189 const float leafWidth = (maxX - minX) / gridSize;
190 const float leafHeight = (maxY - minY) / gridSize;
191
192 m_objectGrid = std::make_unique<MCObjectGrid>(m_minX, m_minY, m_maxX, m_maxY, leafWidth, leafHeight);
193
194 if (addAreaWalls)
195 {
196 // Create "wall" objects
197 const float w = m_maxX - m_minX;
198 const float h = m_maxY - m_minY;
199
200 if (m_leftWallObject)
201 {
202 removeObjectNow(*m_leftWallObject);
203 }
204
205 const float wallRestitution = 0.25f;
206
207 m_leftWallObject = std::make_unique<MCObject>("__WORLD_LEFT_WALL");
208 m_leftWallObject->setShape(std::make_shared<MCRectShape>(nullptr, w, h));
209 m_leftWallObject->physicsComponent().setMass(0, true);
210 m_leftWallObject->physicsComponent().setRestitution(wallRestitution);
211 m_leftWallObject->addToWorld();
212 m_leftWallObject->translate(MCVector3dF(-w / 2, h / 2, 0));
213
214 if (m_rightWallObject)
215 {
216 removeObjectNow(*m_rightWallObject);
217 }
218
219 m_rightWallObject = std::make_unique<MCObject>("__WORLD_RIGHT_WALL");
220 m_rightWallObject->setShape(std::make_shared<MCRectShape>(nullptr, w, h));
221 m_rightWallObject->physicsComponent().setMass(0, true);
222 m_rightWallObject->physicsComponent().setRestitution(wallRestitution);
223 m_rightWallObject->addToWorld();
224 m_rightWallObject->translate(MCVector3dF(w + w / 2, h / 2, 0));
225
226 if (m_topWallObject)
227 {
228 removeObjectNow(*m_topWallObject);
229 }
230
231 m_topWallObject = std::make_unique<MCObject>("__WORLD_TOP_WALL");
232 m_topWallObject->setShape(std::make_shared<MCRectShape>(nullptr, w, h));
233 m_topWallObject->physicsComponent().setMass(0, true);
234 m_topWallObject->physicsComponent().setRestitution(wallRestitution);
235 m_topWallObject->addToWorld();
236 m_topWallObject->translate(MCVector3dF(w / 2, h + h / 2, 0));
237
238 if (m_bottomWallObject)
239 {
240 removeObjectNow(*m_bottomWallObject);
241 }
242
243 m_bottomWallObject = std::make_unique<MCObject>("__WORLD_BOTTOM_WALL");
244 m_bottomWallObject->setShape(std::make_shared<MCRectShape>(nullptr, w, h));
245 m_bottomWallObject->physicsComponent().setMass(0, true);
246 m_bottomWallObject->physicsComponent().setRestitution(wallRestitution);
247 m_bottomWallObject->addToWorld();
248 m_bottomWallObject->translate(MCVector3dF(w / 2, -h / 2, 0));
249 }
250 else
251 {
252 if (m_leftWallObject)
253 {
254 removeObjectNow(*m_leftWallObject);
255 m_leftWallObject.reset();
256 }
257
258 if (m_rightWallObject)
259 {
260 removeObjectNow(*m_rightWallObject);
261 m_rightWallObject.reset();
262 }
263
264 if (m_topWallObject)
265 {
266 removeObjectNow(*m_topWallObject);
267 m_topWallObject.reset();
268 }
269
270 if (m_bottomWallObject)
271 {
272 removeObjectNow(*m_bottomWallObject);
273 m_bottomWallObject.reset();
274 }
275 }
276 }
277
minX() const278 float MCWorld::minX() const
279 {
280 return m_minX;
281 }
282
maxX() const283 float MCWorld::maxX() const
284 {
285 return m_maxX;
286 }
287
minY() const288 float MCWorld::minY() const
289 {
290 return m_minY;
291 }
292
maxY() const293 float MCWorld::maxY() const
294 {
295 return m_maxY;
296 }
297
minZ() const298 float MCWorld::minZ() const
299 {
300 return m_minZ;
301 }
302
maxZ() const303 float MCWorld::maxZ() const
304 {
305 return m_maxZ;
306 }
307
objectCount() const308 size_t MCWorld::objectCount() const
309 {
310 return m_objects.size();
311 }
312
addObject(MCObject & object)313 void MCWorld::addObject(MCObject & object)
314 {
315 if (!object.removing())
316 {
317 if (object.index() == REMOVED_INDEX)
318 {
319 m_renderer->addObject(object);
320
321 // Add to object vector (O(1))
322 m_objects.push_back(&object);
323 object.setIndex(static_cast<int>(m_objects.size()) - 1);
324
325 m_objectGrid->insert(object);
326
327 // Add xy friction
328 const float FrictionThreshold = 0.001f;
329 if (object.physicsComponent().xyFriction() > FrictionThreshold)
330 {
331 m_forceRegistry->addForceGenerator(
332 std::make_shared<MCFrictionGenerator>(
333 object.physicsComponent().xyFriction(), object.physicsComponent().xyFriction()),
334 object);
335 }
336 }
337 }
338 else
339 {
340 object.setRemoving(false);
341 }
342 }
343
removeObject(MCObject & object)344 void MCWorld::removeObject(MCObject & object)
345 {
346 if (object.index() > REMOVED_INDEX || object.physicsComponent().isSleeping())
347 {
348 object.setRemoving(true);
349 m_removeObjs.push_back(&object);
350 }
351 }
352
removeObjectNow(MCObject & object)353 void MCWorld::removeObjectNow(MCObject & object)
354 {
355 if (object.index() > REMOVED_INDEX || object.physicsComponent().isSleeping())
356 {
357 object.setRemoving(true);
358 for (auto && obj : m_objects)
359 {
360 if (obj != &object && obj->isPhysicsObject())
361 {
362 obj->deleteContacts(object);
363 }
364 }
365
366 doRemoveObject(object);
367 }
368 }
369
doRemoveObject(MCObject & object)370 void MCWorld::doRemoveObject(MCObject & object)
371 {
372 // Reset motion
373 object.physicsComponent().reset();
374
375 m_renderer->removeObject(object);
376
377 // Remove from object vector (O(1))
378 removeObjectFromIntegration(object);
379
380 // Remove from ObjectTree
381 if (object.isPhysicsObject() && !object.bypassCollisions())
382 {
383 m_objectGrid->remove(object);
384 m_collisionDetector->remove(object);
385 }
386
387 object.setRemoving(false);
388 }
389
removeObjectFromIntegration(MCObject & object)390 void MCWorld::removeObjectFromIntegration(MCObject & object)
391 {
392 // Remove from object vector (O(1))
393 if (object.index() > REMOVED_INDEX && object.index() < static_cast<int>(m_objects.size()))
394 {
395 m_objects[static_cast<size_t>(object.index())] = m_objects.back();
396 m_objects[static_cast<size_t>(object.index())]->setIndex(object.index());
397 m_objects.pop_back();
398 object.setIndex(REMOVED_INDEX);
399 }
400 }
401
restoreObjectToIntegration(MCObject & object)402 void MCWorld::restoreObjectToIntegration(MCObject & object)
403 {
404 if (object.index() == REMOVED_INDEX)
405 {
406 // Add to object vector (O(1))
407 m_objects.push_back(&object);
408 object.setIndex(static_cast<int>(m_objects.size()) - 1);
409 }
410 }
411
processRemovedObjects()412 void MCWorld::processRemovedObjects()
413 {
414 for (auto && obj : m_removeObjs)
415 {
416 if (obj->removing())
417 {
418 doRemoveObject(*obj);
419 }
420 }
421
422 m_removeObjs.clear();
423 }
424
processCollisions()425 void MCWorld::processCollisions()
426 {
427 // Check collisions for all registered objects
428 m_numCollisions = m_collisionDetector->detectCollisions(*m_objectGrid);
429 if (m_numCollisions)
430 {
431 generateImpulses();
432
433 // Process contacts and generate impulses
434 for (size_t i = 0; i < m_resolverLoopCount && m_numCollisions > 0; i++)
435 {
436 m_numCollisions = m_collisionDetector->iterateCurrentCollisions();
437 resolvePositions(m_resolverStep);
438 }
439 }
440 }
441
forceRegistry() const442 MCForceRegistry & MCWorld::forceRegistry() const
443 {
444 assert(m_forceRegistry);
445 return *m_forceRegistry;
446 }
447
stepTime(int step)448 void MCWorld::stepTime(int step)
449 {
450 // Integrate physics
451 integrate(step);
452
453 // Process collisions and generate impulses
454 processCollisions();
455
456 // Remove objects that are marked to be removed
457 processRemovedObjects();
458 }
459
objects() const460 const MCWorld::ObjectVector & MCWorld::objects() const
461 {
462 return m_objects;
463 }
464
objectGrid() const465 MCObjectGrid & MCWorld::objectGrid() const
466 {
467 assert(m_objectGrid);
468 return *m_objectGrid;
469 }
470
renderer() const471 MCWorldRenderer & MCWorld::renderer() const
472 {
473 assert(m_renderer);
474 return *m_renderer;
475 }
476
setGravity(const MCVector3dF & gravity)477 void MCWorld::setGravity(const MCVector3dF & gravity)
478 {
479 m_gravity = gravity;
480 }
481
gravity() const482 const MCVector3dF & MCWorld::gravity() const
483 {
484 return m_gravity;
485 }
486
setMetersPerUnit(float value)487 void MCWorld::setMetersPerUnit(float value)
488 {
489 MCWorld::m_metersPerUnit = value;
490 MCWorld::m_metersPerUnitSquared = value * value;
491 }
492
metersPerUnit()493 float MCWorld::metersPerUnit()
494 {
495 assert(MCWorld::m_instance);
496 return MCWorld::m_metersPerUnit;
497 }
498
toMeters(float & units)499 void MCWorld::toMeters(float & units)
500 {
501 units *= MCWorld::m_metersPerUnit;
502 }
503
toMeters(MCVector2dF & units)504 void MCWorld::toMeters(MCVector2dF & units)
505 {
506 units *= MCWorld::m_metersPerUnit;
507 }
508
toMeters(MCVector3dF & units)509 void MCWorld::toMeters(MCVector3dF & units)
510 {
511 units *= MCWorld::m_metersPerUnit;
512 }
513
setResolverLoopCount(size_t resolverLoopCount)514 void MCWorld::setResolverLoopCount(size_t resolverLoopCount)
515 {
516 m_resolverLoopCount = resolverLoopCount;
517 m_resolverStep = 1.0f / resolverLoopCount;
518 }
519