Cogs.Core
PhysicsManager.cpp
1#ifdef _WIN32
2#pragma warning(push)
3#pragma warning(disable: 4127) // conditional expression is constant
4#pragma warning(disable: 5033) // 'register' is no longer a supported storage class
5#endif
6#include <BulletCollision/CollisionDispatch/btGhostObject.h>
7#ifdef _WIN32
8#pragma warning(pop)
9#endif
10
11#include "PhysicsManager.h"
12
13#include "RigidBodySystem.h"
14
15#include "Context.h"
16
17#include "Services/Time.h"
18#include "ExtensionRegistry.h"
19
20#include "Components/Appearance/MaterialComponent.h"
21
22#include "GhostSystem.h"
23#include "Types.h"
24
25#include "Foundation/HashSequence.h"
26#include "Foundation/Collections/Pool.h"
27#include "Foundation/ComponentModel/Entity.h"
28
29using namespace Cogs::Reflection;
30
31namespace
32{
34 constexpr Cogs::StringView timeScale("physics.timeScale");
35}
36
37Cogs::Core::PhysicsManager::PhysicsManager(Context * context) :
38 context(context),
39 broadphase(std::make_unique<btDbvtBroadphase>()),
40 collisionConfiguration(std::make_unique<btDefaultCollisionConfiguration>()),
41 dispatcher(std::make_unique<btCollisionDispatcher>(collisionConfiguration.get())),
42 solver(std::make_unique<btSequentialImpulseConstraintSolver>()),
43 dynamicsWorld(std::make_unique<btDiscreteDynamicsWorld>(dispatcher.get(), broadphase.get(), solver.get(), collisionConfiguration.get()))
44{
45 dynamicsWorld->setGravity(btVector3(0, 0, -9.81f));
46}
47
48Cogs::Core::PhysicsManager::~PhysicsManager() = default;
49
50void Cogs::Core::PhysicsManager::addRigidBody(btRigidBody * rigidBody)
51{
52 dynamicsWorld->addRigidBody(rigidBody);
53}
54
55void Cogs::Core::PhysicsManager::removeRigidBody(btRigidBody * rigidBody)
56{
57 dynamicsWorld->removeRigidBody(rigidBody);
58}
59
60void Cogs::Core::PhysicsManager::addCollisionObject(btCollisionObject * btCollisionObject)
61{
62 if (!ghostCallbackSet) {
63 dynamicsWorld->getPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
64 ghostCallbackSet = true;
65 }
66
67 dynamicsWorld->addCollisionObject(btCollisionObject, btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
68}
69
70void Cogs::Core::PhysicsManager::removeCollisionObject(btCollisionObject * btCollisionObject)
71{
72 dynamicsWorld->removeCollisionObject(btCollisionObject);
73}
74
75void Cogs::Core::PhysicsManager::addConstraint(btTypedConstraint * constraint)
76{
77 dynamicsWorld->addConstraint(constraint);
78}
79
80void Cogs::Core::PhysicsManager::removeConstraint(btTypedConstraint * constraint)
81{
82 dynamicsWorld->removeConstraint(constraint);
83}
84
85void Cogs::Core::PhysicsManager::stepSimulation(float deltaTime)
86{
87 const float tick = 1.0f / 90.0f;
88
89 float scale = context->variables->get(timeScale, 1.0f);
90 dynamicsWorld->stepSimulation(deltaTime * scale, 10, tick);
91}
92
93void Cogs::Core::CollisionDetail::registerType()
94{
95 Field fields[] = {
96 Field("idA", &CollisionDetail::idA),
97 Field("idB", &CollisionDetail::idB),
98 Field("positionA", &CollisionDetail::positionA),
99 Field("positionB", &CollisionDetail::positionB),
100 Field("impulse", &CollisionDetail::impulse),
101 };
102
103 TypeDatabase::createType<CollisionDetail>().setFields(fields);
104}
105
106
107void Cogs::Core::PhysicsManager::preUpdate(Context* context)
108{
109 if (task.isValid()) {
110 context->taskManager->wait(task);
111 pullGhostState();
112 }
113}
114
115void Cogs::Core::PhysicsManager::postUpdate(Context* context)
116{
117 pushGhostState();
118 auto elapsed = context->time->getAnimationTimeDelta();
119 task = context->taskManager->enqueue(TaskManager::GlobalQueue, [elapsed, this]()
120 {
121 CpuInstrumentationScope(SCOPE_PHYSICS, "stepSimulation");
122 stepSimulation(float(elapsed));
123 });
124}
125
126void Cogs::Core::PhysicsManager::waitForASync(Context* context)
127{
128 if (task.isValid()) {
129 context->taskManager->wait(task);
130 }
131}
132
133void Cogs::Core::PhysicsManager::pullGhostState()
134{
135 auto * ghostSystem = ExtensionRegistry::getExtensionSystem<GhostSystem>(context);
136 for (auto & ghostComp : ghostSystem->pool) {
137 ghostComp.contacts.clear();
138
139 if (!ghostComp.isActive()) continue;
140
141 auto & ghostData = ghostSystem->getData(&ghostComp);
142 if(!ghostData.collisionObject) continue;
143
144 auto & pairArray = ghostData.collisionObject->getOverlappingPairCache()->getOverlappingPairArray();
145
146
147 ghostComp.contacts.clear();
148 btManifoldArray manifoldArray;
149 for (int i = 0; i < pairArray.size(); i++) {
150 manifoldArray.clear();
151
152 const auto & pair = pairArray[i];
153 auto * collisionPair = dynamicsWorld->getPairCache()->findPair(pair.m_pProxy0, pair.m_pProxy1);
154 if (!collisionPair || !collisionPair->m_algorithm) continue;
155
156 collisionPair->m_algorithm->getAllContactManifolds(manifoldArray);
157 for (int j = 0; j < manifoldArray.size(); j++) {
158 auto * manifold = manifoldArray[j];
159
160 bool noFlip = manifold->getBody0() == ghostData.collisionObject.get();
161 auto * objB = noFlip ? manifold->getBody1() : manifold->getBody0();
162 auto * dataB = static_cast<SharedRigidBodyData *>(objB->getUserPointer());
163 auto entityB = dataB->entity;
164 if(!entityB.lock()) continue;
165
166 for (int k = 0; k < manifold->getNumContacts(); k++) {
167 const auto & pt = manifold->getContactPoint(k);
168 if (pt.getDistance() < 0.f && ghostComp.contacts.size() < 1024) {
169
170 ghostComp.contacts.push_back(GhostContact{
171 entityB,
172 (noFlip ? -1.f : 1.f)*(*reinterpret_cast<const glm::vec3 *>(&pt.m_normalWorldOnB)),
173 *reinterpret_cast<const glm::vec3 *>(noFlip ? &pt.m_positionWorldOnA : &pt.m_positionWorldOnB),
174 pt.getDistance()
175 });
176 }
177 }
178 }
179 }
180
181 }
182
183}
184
185void Cogs::Core::PhysicsManager::pushGhostState()
186{
187 auto * ghostSystem = ExtensionRegistry::getExtensionSystem<GhostSystem>(context);
188 auto * rigidBodySystem = ExtensionRegistry::getExtensionSystem<RigidBodySystem>(context);
189
190 for (auto & ghostComp : ghostSystem->pool) {
191
192 for (auto & action : ghostComp.actions) {
193 if (auto e = action.target.lock(); e) {
194 if (auto * c = e->getComponent<RigidBodyComponent>(); c) {
195 if (auto & d = rigidBodySystem->getData(c); d.rigidBody) {
196
197 switch (action.type)
198 {
199 case GhostAction::ApplyImpulse:
200 d.rigidBody->applyImpulse(toBullet(action.value), toBullet(action.position));
201 d.rigidBody->activate();
202 break;
203
204 default:
205 break;
206 }
207 }
208 }
209 }
210 }
211
212 ghostComp.actions.clear();
213 }
214}
215
static constexpr TaskQueueId GlobalQueue
Global task queue.
Definition: TaskManager.h:224
Field definition describing a single data member of a data structure.
Definition: Field.h:68
Provides a weakly referenced view over the contents of a string.
Definition: StringView.h:24
Contains reflection support.
Definition: Component.h:11
STL namespace.