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/BsCJoint.h" |
4 | #include "Components/BsCRigidbody.h" |
5 | #include "Scene/BsSceneObject.h" |
6 | #include "Physics/BsPhysics.h" |
7 | #include "Private/RTTI/BsCJointRTTI.h" |
8 | |
9 | using namespace std::placeholders; |
10 | |
11 | namespace bs |
12 | { |
13 | CJoint::CJoint(JOINT_DESC& desc) |
14 | :mDesc(desc) |
15 | { |
16 | mPositions[0] = Vector3::ZERO; |
17 | mPositions[1] = Vector3::ZERO; |
18 | |
19 | mRotations[0] = Quaternion::IDENTITY; |
20 | mRotations[1] = Quaternion::IDENTITY; |
21 | } |
22 | |
23 | CJoint::CJoint(const HSceneObject& parent, JOINT_DESC& desc) |
24 | : Component(parent), mDesc(desc) |
25 | { |
26 | setName("Joint" ); |
27 | |
28 | mPositions[0] = Vector3::ZERO; |
29 | mPositions[1] = Vector3::ZERO; |
30 | |
31 | mRotations[0] = Quaternion::IDENTITY; |
32 | mRotations[1] = Quaternion::IDENTITY; |
33 | |
34 | mNotifyFlags = (TransformChangedFlags)(TCF_Parent | TCF_Transform); |
35 | } |
36 | |
37 | HRigidbody CJoint::getBody(JointBody body) const |
38 | { |
39 | return mBodies[(int)body]; |
40 | } |
41 | |
42 | void CJoint::setBody(JointBody body, const HRigidbody& value) |
43 | { |
44 | if (mBodies[(int)body] == value) |
45 | return; |
46 | |
47 | if (mBodies[(int)body] != nullptr) |
48 | mBodies[(int)body]->_setJoint(HJoint()); |
49 | |
50 | mBodies[(int)body] = value; |
51 | |
52 | if (value != nullptr) |
53 | mBodies[(int)body]->_setJoint(static_object_cast<CJoint>(mThisHandle)); |
54 | |
55 | // If joint already exists, destroy it if we removed all bodies, otherwise update its transform |
56 | if(mInternal != nullptr) |
57 | { |
58 | if (!isBodyValid(mBodies[0]) && !isBodyValid(mBodies[1])) |
59 | destroyInternal(); |
60 | else |
61 | { |
62 | Rigidbody* rigidbody = nullptr; |
63 | if (value != nullptr) |
64 | rigidbody = value->_getInternal(); |
65 | |
66 | mInternal->setBody(body, rigidbody); |
67 | updateTransform(body); |
68 | } |
69 | } |
70 | else // If joint doesn't exist, check if we can create it |
71 | { |
72 | // Must be an active component and at least one of the bodies must be non-null |
73 | if (SO()->getActive() && (isBodyValid(mBodies[0]) || isBodyValid(mBodies[1]))) |
74 | { |
75 | restoreInternal(); |
76 | } |
77 | } |
78 | } |
79 | |
80 | Vector3 CJoint::getPosition(JointBody body) const |
81 | { |
82 | return mPositions[(int)body]; |
83 | } |
84 | |
85 | Quaternion CJoint::getRotation(JointBody body) const |
86 | { |
87 | return mRotations[(int)body]; |
88 | } |
89 | |
90 | void CJoint::setTransform(JointBody body, const Vector3& position, const Quaternion& rotation) |
91 | { |
92 | if (mPositions[(int)body] == position && mRotations[(int)body] == rotation) |
93 | return; |
94 | |
95 | mPositions[(int)body] = position; |
96 | mRotations[(int)body] = rotation; |
97 | |
98 | if (mInternal != nullptr) |
99 | updateTransform(body); |
100 | } |
101 | |
102 | float CJoint::getBreakForce() const |
103 | { |
104 | return mDesc.breakForce; |
105 | } |
106 | |
107 | void CJoint::setBreakForce(float force) |
108 | { |
109 | if (mDesc.breakForce == force) |
110 | return; |
111 | |
112 | mDesc.breakForce = force; |
113 | |
114 | if (mInternal != nullptr) |
115 | mInternal->setBreakForce(force); |
116 | } |
117 | |
118 | float CJoint::getBreakTorque() const |
119 | { |
120 | return mDesc.breakTorque; |
121 | } |
122 | |
123 | void CJoint::setBreakTorque(float torque) |
124 | { |
125 | if (mDesc.breakTorque == torque) |
126 | return; |
127 | |
128 | mDesc.breakTorque = torque; |
129 | |
130 | if (mInternal != nullptr) |
131 | mInternal->setBreakTorque(torque); |
132 | } |
133 | |
134 | bool CJoint::getEnableCollision() const |
135 | { |
136 | return mDesc.enableCollision; |
137 | } |
138 | |
139 | void CJoint::setEnableCollision(bool value) |
140 | { |
141 | if (mDesc.enableCollision == value) |
142 | return; |
143 | |
144 | mDesc.enableCollision = value; |
145 | |
146 | if (mInternal != nullptr) |
147 | mInternal->setEnableCollision(value); |
148 | } |
149 | |
150 | void CJoint::onInitialized() |
151 | { |
152 | |
153 | } |
154 | |
155 | void CJoint::onDestroyed() |
156 | { |
157 | if (mBodies[0] != nullptr) |
158 | mBodies[0]->_setJoint(HJoint()); |
159 | |
160 | if (mBodies[1] != nullptr) |
161 | mBodies[1]->_setJoint(HJoint()); |
162 | |
163 | if(mInternal != nullptr) |
164 | destroyInternal(); |
165 | } |
166 | |
167 | void CJoint::onDisabled() |
168 | { |
169 | if (mInternal != nullptr) |
170 | destroyInternal(); |
171 | } |
172 | |
173 | void CJoint::onEnabled() |
174 | { |
175 | if(isBodyValid(mBodies[0]) || isBodyValid(mBodies[1])) |
176 | restoreInternal(); |
177 | } |
178 | |
179 | void CJoint::onTransformChanged(TransformChangedFlags flags) |
180 | { |
181 | if (mInternal == nullptr) |
182 | return; |
183 | |
184 | // We're ignoring this during physics update because it would cause problems if the joint itself was moved by physics |
185 | // Note: This isn't particularily correct because if the joint is being moved by physics but the rigidbodies |
186 | // themselves are not parented to the joint, the transform will need updating. However I'm leaving it up to the |
187 | // user to ensure rigidbodies are always parented to the joint in such a case (It's an unlikely situation that |
188 | // I can't think of an use for - joint transform will almost always be set as an initialization step and not a |
189 | // physics response). |
190 | if (gPhysics()._isUpdateInProgress()) |
191 | return; |
192 | |
193 | updateTransform(JointBody::Target); |
194 | updateTransform(JointBody::Anchor); |
195 | } |
196 | |
197 | void CJoint::restoreInternal() |
198 | { |
199 | if (mBodies[0] != nullptr) |
200 | mDesc.bodies[0].body = mBodies[0]->_getInternal(); |
201 | else |
202 | mDesc.bodies[0].body = nullptr; |
203 | |
204 | if (mBodies[1] != nullptr) |
205 | mDesc.bodies[1].body = mBodies[1]->_getInternal(); |
206 | else |
207 | mDesc.bodies[1].body = nullptr; |
208 | |
209 | getLocalTransform(JointBody::Target, mDesc.bodies[0].position, mDesc.bodies[0].rotation); |
210 | getLocalTransform(JointBody::Anchor, mDesc.bodies[1].position, mDesc.bodies[1].rotation); |
211 | |
212 | mInternal = createInternal(); |
213 | |
214 | mInternal->onJointBreak.connect(std::bind(&CJoint::triggerOnJointBroken, this)); |
215 | } |
216 | |
217 | void CJoint::destroyInternal() |
218 | { |
219 | // This should release the last reference and destroy the internal joint |
220 | if(mInternal) |
221 | { |
222 | mInternal->_setOwner(PhysicsOwnerType::None, nullptr); |
223 | mInternal = nullptr; |
224 | } |
225 | } |
226 | |
227 | void CJoint::notifyRigidbodyMoved(const HRigidbody& body) |
228 | { |
229 | if (mInternal == nullptr) |
230 | return; |
231 | |
232 | // If physics update is in progress do nothing, as its the joint itself that's probably moving the body |
233 | if (gPhysics()._isUpdateInProgress()) |
234 | return; |
235 | |
236 | if (mBodies[0] == body) |
237 | updateTransform(JointBody::Target); |
238 | else if (mBodies[1] == body) |
239 | updateTransform(JointBody::Anchor); |
240 | else |
241 | assert(false); // Not allowed to happen |
242 | } |
243 | |
244 | bool CJoint::isBodyValid(const HRigidbody& body) |
245 | { |
246 | if (body == nullptr) |
247 | return false; |
248 | |
249 | if (body->_getInternal() == nullptr) |
250 | return false; |
251 | |
252 | return true; |
253 | } |
254 | |
255 | void CJoint::updateTransform(JointBody body) |
256 | { |
257 | Vector3 localPos; |
258 | Quaternion localRot; |
259 | getLocalTransform(body, localPos, localRot); |
260 | |
261 | mInternal->setTransform(body, localPos, localRot); |
262 | } |
263 | |
264 | void CJoint::getLocalTransform(JointBody body, Vector3& position, Quaternion& rotation) |
265 | { |
266 | position = mPositions[(UINT32)body]; |
267 | rotation = mRotations[(UINT32)body]; |
268 | |
269 | HRigidbody rigidbody = mBodies[(UINT32)body]; |
270 | if (rigidbody == nullptr) // Get world space transform if no relative to any body |
271 | { |
272 | const Transform& tfrm = SO()->getTransform(); |
273 | Quaternion worldRot = tfrm.getRotation(); |
274 | |
275 | rotation = worldRot*rotation; |
276 | position = worldRot.rotate(position) + tfrm.getPosition(); |
277 | } |
278 | else |
279 | { |
280 | position = rotation.rotate(position); |
281 | } |
282 | } |
283 | |
284 | void CJoint::triggerOnJointBroken() |
285 | { |
286 | onJointBreak(); |
287 | } |
288 | |
289 | RTTITypeBase* CJoint::getRTTIStatic() |
290 | { |
291 | return CJointRTTI::instance(); |
292 | } |
293 | |
294 | RTTITypeBase* CJoint::getRTTI() const |
295 | { |
296 | return CJoint::getRTTIStatic(); |
297 | } |
298 | } |