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