Cogs.Core
RigidBodySystem.cpp
1#include "RigidBodySystem.h"
2
3#include "PhysicsManager.h"
4
5#include "Systems/Core/TransformSystem.h"
6
7#include "Context.h"
8
9#include "Services/Services.h"
10#include "Services/Time.h"
11
12#include "EntityStore.h"
13#include "Components/Core/MotionComponent.h"
14
15#include "CollisionComponent.h"
16
17#include "Platform/Instrumentation.h"
18
19#include "Foundation/Logging/Logger.h"
20
21#include <glm/gtc/type_ptr.hpp>
22#include <glm/gtx/matrix_decompose.hpp>
23
24namespace
25{
26 Cogs::Logging::Log logger = Cogs::Logging::getLogger("RigidBodySystem");
27}
28
29namespace Cogs
30{
31 namespace Core
32 {
33 void updateMassProperties(RigidBodyData & data, float mass, const glm::vec3 & scale)
34 {
35 const float margin = scale.x < scale.y && scale.x < scale.z ? scale.x : (scale.y < scale.z ? scale.y : scale.z);
36 const float marginMultiplier = 0.1f;
37
38 data.collisionShape->setLocalScaling(toBullet(scale));
39
40 auto diff = scale - glm::vec3(1, 1, 1);
41
42 if (glm::any(glm::greaterThan(glm::abs(diff), glm::vec3(0.01f, 0.01f, 0.01f)))) {
43 data.collisionShape->setMargin(margin * marginMultiplier);
44 }
45
46 btVector3 inertia;
47 if (mass != 0) {
48 if (data.collisionShape->getShapeType() != EMPTY_SHAPE_PROXYTYPE) data.collisionShape->calculateLocalInertia(mass, inertia);
49 }
50
51 if (data.rigidBody) {
52 data.rigidBody->setMassProps(mass, inertia);
53 data.rigidBody->updateInertiaTensor();
54 }
55
56 data.scale = scale;
57 }
58 }
59}
60
62{
64 auto manager = context->services->getService<PhysicsManager>();
65 manager->preUpdate(context);
66}
67
69{
70 if (pool.size() && glm::any(glm::notEqual(context->transformSystem->getOrigin(), glm::dvec3(0, 0, 0)))) {
71 LOG_ERROR(logger, "Physics not supported using global origin.");
72
73 return;
74 }
75
76 auto manager = context->services->getService<PhysicsManager>();
77 auto elapsed = context->time->getAnimationTimeDelta();
78
79 for (auto & c : pool) {
80 if (!c.isActive()) continue;
81
82 auto & data = getData(&c);
83
84 auto transformComponent = c.getComponent<TransformComponent>();
85
86 if (!data.rigidBody) {
87 auto collisionComponent = c.getComponent<CollisionComponent>();
88
89 if (!collisionComponent || !collisionComponent->collisionShape) {
90 return;
91 }
92 data.collisionShape = collisionComponent->collisionShape;
93
94 context->transformSystem->updateLocalToWorldTransform(*transformComponent, true);
95 auto worldTransform = context->transformSystem->getLocalToWorld(transformComponent);
96
97 auto scale = glm::vec3(glm::length(worldTransform[0]), glm::length(worldTransform[1]), glm::length(worldTransform[2]));
98 auto rotation = glm::normalize(glm::quat_cast(glm::mat3(worldTransform * glm::scale(glm::vec3(1.0f / scale.x, 1.0f / scale.y, 1.0f / scale.z)))));
99 auto position = glm::vec3(worldTransform[3]);
100
101 data.scale = scale;
102 updateMassProperties(data, c.mass, scale);
103
104 btVector3 localInertia(0, 0, 0);
105
106 if (c.mass != 0) {
107 if (data.collisionShape->getShapeType() != EMPTY_SHAPE_PROXYTYPE) data.collisionShape->calculateLocalInertia(c.mass, localInertia);
108 }
109
110 const btTransform origin(toBullet(rotation), toBullet(position));
111
112 data.lastPosition = position;
113
114 data.motionState = std::make_unique<btDefaultMotionState>(origin);
115
116 btRigidBody::btRigidBodyConstructionInfo ci(c.mass, data.motionState.get(), data.collisionShape, localInertia);
117
118 ci.m_friction = c.friction;
119 ci.m_rollingFriction = c.rollingFriction;
120 ci.m_spinningFriction = c.spinningFriction;
121 ci.m_linearDamping = c.linearDamping;
122 ci.m_angularDamping = c.angularDamping;
123 ci.m_restitution = c.restitution;
124
125 data.rigidBody = std::make_unique<btRigidBody>(ci);
126 data.rigidBody->setCcdMotionThreshold(c.ccdMotionThreshold);
127 data.rigidBody->setCcdSweptSphereRadius(c.ccdSweptSphereRadius);
128 data.rigidBody->setLinearFactor(toBullet(c.linearFactor));
129 data.rigidBody->setAngularFactor(toBullet(c.angularFactor));
130 data.shared = std::make_unique<SharedRigidBodyData>();
131 data.shared->entity = context->store->getEntity(c.getContainer()->getId());
132 data.rigidBody->setUserPointer(data.shared.get());
133 data.kinematic = c.kinematic;
134
135 auto flags = data.rigidBody->getCollisionFlags();
136 flags |= c.kinematic ? btCollisionObject::CF_KINEMATIC_OBJECT : 0;
137
138 if (c.kinematic) {
139 data.rigidBody->setActivationState(DISABLE_DEACTIVATION);
140 }
141
142 data.rigidBody->setCollisionFlags(flags);
143
144 manager->addRigidBody(data.rigidBody.get());
145 }
146
147 if (!data.rigidBody) continue;
148
149 if (c.kinematic != data.kinematic) {
150 manager->removeRigidBody(data.rigidBody.get());
151
152 auto flags = data.rigidBody->getCollisionFlags();
153
154 if (c.kinematic) {
155 flags |= btCollisionObject::CF_KINEMATIC_OBJECT;
156 data.rigidBody->setActivationState(DISABLE_DEACTIVATION);
157 } else {
158 flags &= ~btCollisionObject::CF_KINEMATIC_OBJECT;
159 data.rigidBody->activate(true);
160
161 context->transformSystem->updateLocalToWorldTransform(*transformComponent, true);
162 auto worldTransform = context->transformSystem->getLocalToWorld(transformComponent);
163
164 auto scale = glm::vec3(glm::length(worldTransform[0]), glm::length(worldTransform[1]), glm::length(worldTransform[2]));
165
166 if (glm::any(glm::greaterThan(glm::abs(scale - data.scale), glm::vec3(0.001f)))) {
167 updateMassProperties(data, c.mass, scale);
168 }
169 }
170
171 data.rigidBody->setCollisionFlags(flags);
172 data.kinematic = c.kinematic;
173
174 manager->addRigidBody(data.rigidBody.get());
175 }
176
177 if (c.kinematic) {
178 auto motionState = static_cast<btDefaultMotionState *>(data.motionState.get());
179
180 context->transformSystem->updateLocalToWorldTransform(*transformComponent, true);
181 const auto & worldTransform = context->transformSystem->getLocalToWorld(transformComponent);
182
183 const auto scale = glm::vec3(glm::length(worldTransform[0]), glm::length(worldTransform[1]), glm::length(worldTransform[2]));
184 const auto rotation = glm::normalize(glm::quat_cast(glm::mat3(worldTransform * glm::scale(glm::vec3(1.0f / scale.x, 1.0f / scale.y, 1.0f / scale.z)))));
185 const auto position = glm::vec3(worldTransform[3]);
186
187 if (glm::any(glm::greaterThan(glm::abs(scale - data.scale), glm::vec3(0.001f)))) {
188 manager->removeRigidBody(data.rigidBody.get());
189 updateMassProperties(data, c.mass, scale);
190 manager->addRigidBody(data.rigidBody.get());
191 }
192
193 const btTransform origin(toBullet(rotation), toBullet(position));
194
195 motionState->setWorldTransform(origin);
196
197
198 auto motionComponent = c.getComponent<MotionComponent>();
199 if(motionComponent != nullptr) {
200 data.rigidBody->setLinearVelocity(toBullet(motionComponent->velocity));
201 data.rigidBody->setAngularVelocity(toBullet(motionComponent->angularVelocity));
202 }
203 else {
204 glm::vec3 deltaPosition = position - data.lastPosition;
205 glm::vec3 velocity = deltaPosition / static_cast<float>(elapsed);
206 data.rigidBody->setLinearVelocity(toBullet(velocity));
207 }
208
209 data.lastPosition = position;
210 } else {
211 btTransform transform;
212 data.motionState->getWorldTransform(transform);
213
214 auto btOrigin = transform.getOrigin();
215 auto btRotation = transform.getRotation();
216
217 transformComponent->position = *reinterpret_cast<const glm::vec3 *>(&btOrigin);
218 transformComponent->rotation = *reinterpret_cast<const glm::quat *>(&btRotation);
219 transformComponent->setChanged();
220
221 auto motionComponent = c.getComponent<MotionComponent>();
222 if (motionComponent != nullptr) {
223 motionComponent->angularVelocity = *reinterpret_cast<const glm::vec3 *>(&data.rigidBody->getAngularVelocity());
224 motionComponent->velocity = *reinterpret_cast<const glm::vec3 *>(&data.rigidBody->getLinearVelocity());
225 motionComponent->setChanged();
226 }
227 }
228 }
229}
230
232{
233 auto * manager = context->services->getService<PhysicsManager>();
234 manager->postUpdate(context);
235 base::postUpdate(context);
236}
237
239{
240 auto * manager = context->services->getService<PhysicsManager>();
241 manager->waitForASync(context);
242
243 auto c = component.resolveComponent<RigidBodyComponent>();
244 auto & data = getData(c);
245
246 if (data.rigidBody) {
247 manager->removeRigidBody(data.rigidBody.get());
248 }
249
250 base::destroyComponent(component);
251}
ComponentType * getComponent() const
Definition: Component.h:159
Context * context
Pointer to the Context instance the system lives in.
void postUpdate()
Perform post update logic in the system.
void update()
Updates the system state to that of the current frame.
void preUpdate()
Run the pre-update method of the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class Services > services
Services.
Definition: Context.h:174
class EntityStore * store
Entity store.
Definition: Context.h:231
std::unique_ptr< class Time > time
Time service instance.
Definition: Context.h:198
EntityPtr getEntity(const StringView &name, bool logIfNotFound=true) const
Retrieve a reference to the shared entity pointer to the Entity with the given name.
void destroyComponent(ComponentHandle component) final
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
glm::dvec3 getOrigin() const
Gets the Origin offset of the scene.
Log implementation class.
Definition: LogManager.h:139
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
Handle to a Component instance.
Definition: Component.h:67
ComponentType * resolveComponent() const
Definition: Component.h:90
Contains motion data.
glm::vec3 angularVelocity
Angular velocity.