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/BsCCharacterController.h"
4#include "Scene/BsSceneObject.h"
5#include "Scene/BsSceneManager.h"
6#include "Physics/BsCollider.h"
7#include "Private/RTTI/BsCCharacterControllerRTTI.h"
8#include "BsCCollider.h"
9
10using namespace std::placeholders;
11
12namespace bs
13{
14 CCharacterController::CCharacterController()
15 {
16 setName("CharacterController");
17
18 mNotifyFlags = TCF_Transform;
19 }
20
21 CCharacterController::CCharacterController(const HSceneObject& parent)
22 : Component(parent)
23 {
24 setName("CharacterController");
25
26 mNotifyFlags = TCF_Transform;
27 }
28
29 CharacterCollisionFlags CCharacterController::move(const Vector3& displacement)
30 {
31 CharacterCollisionFlags output;
32
33 if (mInternal == nullptr)
34 return output;
35
36 output = mInternal->move(displacement);
37 updatePositionFromController();
38
39 return output;
40 }
41
42 Vector3 CCharacterController::getFootPosition() const
43 {
44 if (mInternal == nullptr)
45 return Vector3::ZERO;
46
47 return mInternal->getFootPosition();
48 }
49
50 void CCharacterController::setFootPosition(const Vector3& position)
51 {
52 if (mInternal == nullptr)
53 return;
54
55 mInternal->setFootPosition(position);
56 updatePositionFromController();
57 }
58
59 void CCharacterController::setRadius(float radius)
60 {
61 mDesc.radius = radius;
62
63 if (mInternal != nullptr)
64 updateDimensions();
65 }
66
67 void CCharacterController::setHeight(float height)
68 {
69 mDesc.height = height;
70
71 if (mInternal != nullptr)
72 updateDimensions();
73 }
74
75 void CCharacterController::setUp(const Vector3& up)
76 {
77 mDesc.up = up;
78
79 if (mInternal != nullptr)
80 mInternal->setUp(up);
81 }
82
83 void CCharacterController::setClimbingMode(CharacterClimbingMode mode)
84 {
85 mDesc.climbingMode = mode;
86
87 if (mInternal != nullptr)
88 mInternal->setClimbingMode(mode);
89 }
90
91 void CCharacterController::setNonWalkableMode(CharacterNonWalkableMode mode)
92 {
93 mDesc.nonWalkableMode = mode;
94
95 if (mInternal != nullptr)
96 mInternal->setNonWalkableMode(mode);
97 }
98
99 void CCharacterController::setMinMoveDistance(float value)
100 {
101 mDesc.minMoveDistance = value;
102
103 if (mInternal != nullptr)
104 mInternal->setMinMoveDistance(value);
105 }
106
107 void CCharacterController::setContactOffset(float value)
108 {
109 mDesc.contactOffset = value;
110
111 if (mInternal != nullptr)
112 mInternal->setContactOffset(value);
113 }
114
115 void CCharacterController::setStepOffset(float value)
116 {
117 mDesc.stepOffset = value;
118
119 if (mInternal != nullptr)
120 mInternal->setStepOffset(value);
121 }
122
123 void CCharacterController::setSlopeLimit(Radian value)
124 {
125 mDesc.slopeLimit = value;
126
127 if (mInternal != nullptr)
128 mInternal->setSlopeLimit(value);
129 }
130
131 void CCharacterController::setLayer(UINT64 layer)
132 {
133 mLayer = layer;
134
135 if (mInternal != nullptr)
136 mInternal->setLayer(layer);
137 }
138
139 void CCharacterController::onInitialized()
140 {
141
142 }
143
144 void CCharacterController::onDestroyed()
145 {
146 destroyInternal();
147 }
148
149 void CCharacterController::onDisabled()
150 {
151 destroyInternal();
152 }
153
154 void CCharacterController::onEnabled()
155 {
156 const SPtr<SceneInstance>& scene = SO()->getScene();
157
158 mDesc.position = SO()->getTransform().getPosition();
159 mInternal = CharacterController::create(*scene->getPhysicsScene(), mDesc);
160 mInternal->_setOwner(PhysicsOwnerType::Component, this);
161
162 mInternal->onColliderHit.connect(std::bind(&CCharacterController::triggerOnColliderHit, this, _1));
163 mInternal->onControllerHit.connect(std::bind(&CCharacterController::triggerOnControllerHit, this, _1));
164
165 mInternal->setLayer(mLayer);
166 updateDimensions();
167 }
168
169 void CCharacterController::onTransformChanged(TransformChangedFlags flags)
170 {
171 if (!SO()->getActive() || mInternal == nullptr)
172 return;
173
174 mInternal->setPosition(SO()->getTransform().getPosition());
175 }
176
177 void CCharacterController::updatePositionFromController()
178 {
179 mNotifyFlags = (TransformChangedFlags)0;
180 SO()->setWorldPosition(mInternal->getPosition());
181 mNotifyFlags = TCF_Transform;
182 }
183
184 void CCharacterController::updateDimensions()
185 {
186 Vector3 scale = SO()->getTransform().getScale();
187 float height = mDesc.height * Math::abs(scale.y);
188 float radius = mDesc.radius * Math::abs(std::max(scale.x, scale.z));
189
190 mInternal->setHeight(height);
191 mInternal->setRadius(radius);
192 }
193
194 void CCharacterController::destroyInternal()
195 {
196 // This should release the last reference and destroy the internal controller
197 if(mInternal)
198 {
199 mInternal->_setOwner(PhysicsOwnerType::None, nullptr);
200 mInternal = nullptr;
201 }
202 }
203
204 void CCharacterController::triggerOnColliderHit(const ControllerColliderCollision& value)
205 {
206 // Const-cast and modify is okay because we're the only object receiving this event
207 auto& hit = const_cast<ControllerColliderCollision&>(value);
208
209 if(hit.colliderRaw)
210 {
211 const auto collider = (CCollider*)hit.colliderRaw->_getOwner(PhysicsOwnerType::Component);
212 hit.collider = static_object_cast<CCollider>(collider->getHandle());
213 }
214
215 onColliderHit(hit);
216 }
217
218 void CCharacterController::triggerOnControllerHit(const ControllerControllerCollision& value)
219 {
220 // Const-cast and modify is okay because we're the only object receiving this event
221 ControllerControllerCollision& hit = const_cast<ControllerControllerCollision&>(value);
222
223 if(hit.controllerRaw)
224 {
225 const auto controller = (CCharacterController*)hit.controllerRaw->_getOwner(PhysicsOwnerType::Component);
226 hit.controller = static_object_cast<CCharacterController>(controller->getHandle());
227 }
228
229 onControllerHit(hit);
230 }
231
232 RTTITypeBase* CCharacterController::getRTTIStatic()
233 {
234 return CCharacterControllerRTTI::instance();
235 }
236
237 RTTITypeBase* CCharacterController::getRTTI() const
238 {
239 return CCharacterController::getRTTIStatic();
240 }
241}