1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Components/BsCCollider.h"
4#include "Scene/BsSceneObject.h"
5#include "Components/BsCRigidbody.h"
6#include "Physics/BsPhysics.h"
7#include "Private/RTTI/BsCColliderRTTI.h"
8
9using namespace std::placeholders;
10
11namespace bs
12{
13 CCollider::CCollider()
14 {
15 setName("Collider");
16
17 mNotifyFlags = (TransformChangedFlags)(TCF_Parent | TCF_Transform);
18 }
19
20 CCollider::CCollider(const HSceneObject& parent)
21 : Component(parent)
22 {
23 setName("Collider");
24
25 mNotifyFlags = (TransformChangedFlags)(TCF_Parent | TCF_Transform);
26 }
27
28 void CCollider::setIsTrigger(bool value)
29 {
30 if (mIsTrigger == value)
31 return;
32
33 mIsTrigger = value;
34
35 if (mInternal != nullptr)
36 {
37 mInternal->setIsTrigger(value);
38
39 updateParentRigidbody();
40 updateTransform();
41 }
42 }
43
44 void CCollider::setMass(float mass)
45 {
46 if (mMass == mass)
47 return;
48
49 mMass = mass;
50
51 if (mInternal != nullptr)
52 {
53 mInternal->setMass(mass);
54
55 if (mParent != nullptr)
56 mParent->_updateMassDistribution();
57 }
58 }
59
60 void CCollider::setMaterial(const HPhysicsMaterial& material)
61 {
62 mMaterial = material;
63
64 if (mInternal != nullptr)
65 mInternal->setMaterial(material);
66 }
67
68 void CCollider::setContactOffset(float value)
69 {
70 value = std::max(0.0f, std::max(value, getRestOffset()));
71
72 mContactOffset = value;
73
74 if (mInternal != nullptr)
75 mInternal->setContactOffset(value);
76 }
77
78 void CCollider::setRestOffset(float value)
79 {
80 value = std::min(value, getContactOffset());
81
82 mRestOffset = value;
83
84 if (mInternal != nullptr)
85 mInternal->setRestOffset(value);
86 }
87
88 void CCollider::setLayer(UINT64 layer)
89 {
90 mLayer = layer;
91
92 if (mInternal != nullptr)
93 mInternal->setLayer(layer);
94 }
95
96 void CCollider::setCollisionReportMode(CollisionReportMode mode)
97 {
98 mCollisionReportMode = mode;
99
100 if (mInternal != nullptr)
101 updateCollisionReportMode();
102 }
103
104 void CCollider::onInitialized()
105 {
106
107 }
108
109 void CCollider::onDestroyed()
110 {
111 destroyInternal();
112 }
113
114 void CCollider::onDisabled()
115 {
116 destroyInternal();
117 }
118
119 void CCollider::onEnabled()
120 {
121 restoreInternal();
122 }
123
124 void CCollider::onTransformChanged(TransformChangedFlags flags)
125 {
126 if (!SO()->getActive())
127 return;
128
129 if ((flags & TCF_Parent) != 0)
130 updateParentRigidbody();
131
132 // Don't update the transform if it's due to Physics update since then we can guarantee it will remain at the same
133 // relative transform to its parent
134 if (gPhysics()._isUpdateInProgress())
135 return;
136
137 if ((flags & (TCF_Parent | TCF_Transform)) != 0)
138 updateTransform();
139 }
140
141 void CCollider::setRigidbody(const HRigidbody& rigidbody, bool internal)
142 {
143 if (rigidbody == mParent)
144 return;
145
146 if (mInternal != nullptr && !internal)
147 {
148 if (mParent != nullptr)
149 mParent->removeCollider(static_object_cast<CCollider>(mThisHandle));
150
151 Rigidbody* rigidBodyPtr = nullptr;
152
153 if (rigidbody != nullptr)
154 rigidBodyPtr = rigidbody->_getInternal();
155
156 mInternal->setRigidbody(rigidBodyPtr);
157
158 if (rigidbody != nullptr)
159 rigidbody->addCollider(static_object_cast<CCollider>(mThisHandle));
160 }
161
162 mParent = rigidbody;
163 updateCollisionReportMode();
164 updateTransform();
165 }
166
167 bool CCollider::rayCast(const Ray& ray, PhysicsQueryHit& hit, float maxDist) const
168 {
169 if (mInternal == nullptr)
170 return false;
171
172 return mInternal->rayCast(ray, hit, maxDist);
173 }
174
175 bool CCollider::rayCast(const Vector3& origin, const Vector3& unitDir, PhysicsQueryHit& hit,
176 float maxDist) const
177 {
178 if (mInternal == nullptr)
179 return false;
180
181 return mInternal->rayCast(origin, unitDir, hit, maxDist);
182 }
183
184 void CCollider::restoreInternal()
185 {
186 if (mInternal == nullptr)
187 {
188 mInternal = createInternal();
189
190 mInternal->onCollisionBegin.connect(std::bind(&CCollider::triggerOnCollisionBegin, this, _1));
191 mInternal->onCollisionStay.connect(std::bind(&CCollider::triggerOnCollisionStay, this, _1));
192 mInternal->onCollisionEnd.connect(std::bind(&CCollider::triggerOnCollisionEnd, this, _1));
193 }
194
195 // Note: Merge into one call to avoid many virtual function calls
196 mInternal->setIsTrigger(mIsTrigger);
197 mInternal->setMass(mMass);
198 mInternal->setMaterial(mMaterial);
199 mInternal->setContactOffset(mContactOffset);
200 mInternal->setRestOffset(mRestOffset);
201 mInternal->setLayer(mLayer);
202
203 updateParentRigidbody();
204 updateTransform();
205 updateCollisionReportMode();
206 }
207
208 void CCollider::destroyInternal()
209 {
210 if (mParent != nullptr)
211 mParent->removeCollider(static_object_cast<CCollider>(mThisHandle));
212
213 mParent = nullptr;
214
215 // This should release the last reference and destroy the internal collider
216 if(mInternal)
217 {
218 mInternal->_setOwner(PhysicsOwnerType::None, nullptr);
219 mInternal = nullptr;
220 }
221 }
222
223 void CCollider::updateParentRigidbody()
224 {
225 if (mIsTrigger)
226 {
227 setRigidbody(HRigidbody());
228 return;
229 }
230
231 HSceneObject currentSO = SO();
232 while (currentSO != nullptr)
233 {
234 HRigidbody parent = currentSO->getComponent<CRigidbody>();
235 if (parent != nullptr)
236 {
237 if(currentSO->getActive() && isValidParent(parent))
238 setRigidbody(parent);
239 else
240 setRigidbody(HRigidbody());
241
242 return;
243 }
244
245 currentSO = currentSO->getParent();
246 }
247
248 // Not found
249 setRigidbody(HRigidbody());
250 }
251
252 void CCollider::updateTransform()
253 {
254 const Transform& tfrm = SO()->getTransform();
255 Vector3 myScale = tfrm.getScale();
256
257 if (mParent != nullptr)
258 {
259 const Transform& parentTfrm = mParent->SO()->getTransform();
260 Vector3 parentPos = parentTfrm.getPosition();
261 Quaternion parentRot = parentTfrm.getRotation();
262
263 Vector3 myPos = tfrm.getPosition();
264 Quaternion myRot = tfrm.getRotation();
265
266 Vector3 scale = parentTfrm.getScale();
267 Vector3 invScale = scale;
268 if (invScale.x != 0) invScale.x = 1.0f / invScale.x;
269 if (invScale.y != 0) invScale.y = 1.0f / invScale.y;
270 if (invScale.z != 0) invScale.z = 1.0f / invScale.z;
271
272 Quaternion invRotation = parentRot.inverse();
273
274 Vector3 relativePos = invRotation.rotate(myPos - parentPos) * invScale;
275 Quaternion relativeRot = invRotation * myRot;
276
277 relativePos = relativePos + relativeRot.rotate(mLocalPosition * scale);
278 relativeRot = relativeRot * mLocalRotation;
279
280 if(mInternal)
281 mInternal->setTransform(relativePos, relativeRot);
282
283 mParent->_updateMassDistribution();
284 }
285 else
286 {
287 Quaternion myRot = tfrm.getRotation();
288 Vector3 myPos = tfrm.getPosition() + myRot.rotate(mLocalPosition * myScale);
289 myRot = myRot * mLocalRotation;
290
291 if(mInternal)
292 mInternal->setTransform(myPos, myRot);
293 }
294
295 if (mInternal)
296 mInternal->setScale(myScale);
297 }
298
299 void CCollider::updateCollisionReportMode()
300 {
301 CollisionReportMode mode = mCollisionReportMode;
302
303 if (mParent != nullptr)
304 mode = mParent->getCollisionReportMode();
305
306 if(mInternal != nullptr)
307 mInternal->setCollisionReportMode(mode);
308 }
309
310 void CCollider::triggerOnCollisionBegin(const CollisionDataRaw& data)
311 {
312 CollisionData hit;
313 hit.contactPoints = data.contactPoints;
314 hit.collider[0] = static_object_cast<CCollider>(mThisHandle);
315
316 if(data.colliders[1] != nullptr)
317 {
318 CCollider* other = (CCollider*)data.colliders[1]->_getOwner(PhysicsOwnerType::Component);
319 hit.collider[1] = static_object_cast<CCollider>(other->getHandle());
320 }
321
322 onCollisionBegin(hit);
323 }
324
325 void CCollider::triggerOnCollisionStay(const CollisionDataRaw& data)
326 {
327 CollisionData hit;
328 hit.contactPoints = data.contactPoints;
329 hit.collider[0] = static_object_cast<CCollider>(mThisHandle);
330
331 if (data.colliders[1] != nullptr)
332 {
333 CCollider* other = (CCollider*)data.colliders[1]->_getOwner(PhysicsOwnerType::Component);
334 hit.collider[1] = static_object_cast<CCollider>(other->getHandle());
335 }
336
337 onCollisionStay(hit);
338 }
339
340 void CCollider::triggerOnCollisionEnd(const CollisionDataRaw& data)
341 {
342 CollisionData hit;
343 hit.contactPoints = data.contactPoints;
344 hit.collider[0] = static_object_cast<CCollider>(mThisHandle);
345
346 if (data.colliders[1] != nullptr)
347 {
348 CCollider* other = (CCollider*)data.colliders[1]->_getOwner(PhysicsOwnerType::Component);
349 hit.collider[1] = static_object_cast<CCollider>(other->getHandle());
350 }
351
352 onCollisionEnd(hit);
353 }
354
355 RTTITypeBase* CCollider::getRTTIStatic()
356 {
357 return CColliderRTTI::instance();
358 }
359
360 RTTITypeBase* CCollider::getRTTI() const
361 {
362 return CCollider::getRTTIStatic();
363 }
364}