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 | |
9 | using namespace std::placeholders; |
10 | |
11 | namespace 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 | } |