1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #include "../Precompiled.h"
24
25 #include "../Core/Context.h"
26 #include "../IO/Log.h"
27 #include "../Scene/Scene.h"
28 #include "../Scene/SplinePath.h"
29
30 namespace Urho3D
31 {
32
33 extern const char* interpolationModeNames[];
34 extern const char* LOGIC_CATEGORY;
35
36 const char* controlPointsStructureElementNames[] =
37 {
38 "Control Point Count",
39 " NodeID",
40 0
41 };
42
SplinePath(Context * context)43 SplinePath::SplinePath(Context* context) :
44 Component(context),
45 spline_(BEZIER_CURVE),
46 speed_(1.f),
47 elapsedTime_(0.f),
48 traveled_(0.f),
49 length_(0.f),
50 dirty_(false),
51 controlledIdAttr_(0)
52 {
53 UpdateNodeIds();
54 }
55
RegisterObject(Context * context)56 void SplinePath::RegisterObject(Context* context)
57 {
58 context->RegisterFactory<SplinePath>(LOGIC_CATEGORY);
59
60 URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Interpolation Mode", GetInterpolationMode, SetInterpolationMode, InterpolationMode,
61 interpolationModeNames, BEZIER_CURVE, AM_FILE);
62 URHO3D_ATTRIBUTE("Speed", float, speed_, 1.f, AM_FILE);
63 URHO3D_ATTRIBUTE("Traveled", float, traveled_, 0.f, AM_FILE | AM_NOEDIT);
64 URHO3D_ATTRIBUTE("Elapsed Time", float, elapsedTime_, 0.f, AM_FILE | AM_NOEDIT);
65 URHO3D_ACCESSOR_ATTRIBUTE("Controlled", GetControlledIdAttr, SetControlledIdAttr, unsigned, 0, AM_FILE | AM_NODEID);
66 URHO3D_ACCESSOR_VARIANT_VECTOR_STRUCTURE_ATTRIBUTE("Control Points", GetControlPointIdsAttr, SetControlPointIdsAttr,
67 VariantVector, Variant::emptyVariantVector, controlPointsStructureElementNames,
68 AM_FILE | AM_NODEIDVECTOR);
69 }
70
ApplyAttributes()71 void SplinePath::ApplyAttributes()
72 {
73 if (!dirty_)
74 return;
75
76 // Remove all old instance nodes before searching for new. Can not call RemoveAllInstances() as that would modify
77 // the ID list on its own
78 for (unsigned i = 0; i < controlPoints_.Size(); ++i)
79 {
80 Node* node = controlPoints_[i];
81 if (node)
82 node->RemoveListener(this);
83 }
84
85 controlPoints_.Clear();
86 spline_.Clear();
87
88 Scene* scene = GetScene();
89
90 if (scene)
91 {
92 // The first index stores the number of IDs redundantly. This is for editing
93 for (unsigned i = 1; i < controlPointIdsAttr_.Size(); ++i)
94 {
95 Node* node = scene->GetNode(controlPointIdsAttr_[i].GetUInt());
96 if (node)
97 {
98 WeakPtr<Node> controlPoint(node);
99 node->AddListener(this);
100 controlPoints_.Push(controlPoint);
101 spline_.AddKnot(node->GetWorldPosition());
102 }
103 }
104
105 Node* node = scene->GetNode(controlledIdAttr_);
106 if (node)
107 {
108 WeakPtr<Node> controlled(node);
109 controlledNode_ = controlled;
110 }
111 }
112
113 CalculateLength();
114 dirty_ = false;
115 }
116
DrawDebugGeometry(DebugRenderer * debug,bool depthTest)117 void SplinePath::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
118 {
119 if (debug && node_ && IsEnabledEffective())
120 {
121 if (spline_.GetKnots().Size() > 1)
122 {
123 Vector3 a = spline_.GetPoint(0.f).GetVector3();
124 for (float f = 0.01f; f <= 1.0f; f = f + 0.01f)
125 {
126 Vector3 b = spline_.GetPoint(f).GetVector3();
127 debug->AddLine(a, b, Color::GREEN);
128 a = b;
129 }
130 }
131
132 for (Vector<WeakPtr<Node> >::ConstIterator i = controlPoints_.Begin(); i != controlPoints_.End(); ++i)
133 debug->AddNode(*i);
134
135 if (controlledNode_)
136 debug->AddNode(controlledNode_);
137 }
138 }
139
AddControlPoint(Node * point,unsigned index)140 void SplinePath::AddControlPoint(Node* point, unsigned index)
141 {
142 if (!point)
143 return;
144
145 WeakPtr<Node> controlPoint(point);
146
147 point->AddListener(this);
148 controlPoints_.Insert(index, controlPoint);
149 spline_.AddKnot(point->GetWorldPosition(), index);
150
151 UpdateNodeIds();
152 CalculateLength();
153 }
154
RemoveControlPoint(Node * point)155 void SplinePath::RemoveControlPoint(Node* point)
156 {
157 if (!point)
158 return;
159
160 WeakPtr<Node> controlPoint(point);
161
162 point->RemoveListener(this);
163
164 for (unsigned i = 0; i < controlPoints_.Size(); ++i)
165 {
166 if (controlPoints_[i] == controlPoint)
167 {
168 controlPoints_.Erase(i);
169 spline_.RemoveKnot(i);
170 break;
171 }
172 }
173
174 UpdateNodeIds();
175 CalculateLength();
176 }
177
ClearControlPoints()178 void SplinePath::ClearControlPoints()
179 {
180 for (unsigned i = 0; i < controlPoints_.Size(); ++i)
181 {
182 Node* node = controlPoints_[i];
183 if (node)
184 node->RemoveListener(this);
185 }
186
187 controlPoints_.Clear();
188 spline_.Clear();
189
190 UpdateNodeIds();
191 CalculateLength();
192 }
193
SetControlledNode(Node * controlled)194 void SplinePath::SetControlledNode(Node* controlled)
195 {
196 if (controlled)
197 controlledNode_ = WeakPtr<Node>(controlled);
198 }
199
SetInterpolationMode(InterpolationMode interpolationMode)200 void SplinePath::SetInterpolationMode(InterpolationMode interpolationMode)
201 {
202 spline_.SetInterpolationMode(interpolationMode);
203 CalculateLength();
204 }
205
SetPosition(float factor)206 void SplinePath::SetPosition(float factor)
207 {
208 float t = factor;
209
210 if (t < 0.f)
211 t = 0.0f;
212 else if (t > 1.0f)
213 t = 1.0f;
214
215 traveled_ = t;
216 }
217
GetPoint(float factor) const218 Vector3 SplinePath::GetPoint(float factor) const
219 {
220 return spline_.GetPoint(factor).GetVector3();
221 }
222
Move(float timeStep)223 void SplinePath::Move(float timeStep)
224 {
225 if (traveled_ >= 1.0f || length_ <= 0.0f || controlledNode_.Null())
226 return;
227
228 elapsedTime_ += timeStep;
229
230 // Calculate where we should be on the spline based on length, speed and time. If that is less than the set traveled_ don't move till caught up.
231 float distanceCovered = elapsedTime_ * speed_;
232 traveled_ = distanceCovered / length_;
233
234 controlledNode_->SetWorldPosition(GetPoint(traveled_));
235 }
236
Reset()237 void SplinePath::Reset()
238 {
239 traveled_ = 0.f;
240 elapsedTime_ = 0.f;
241 }
242
SetControlPointIdsAttr(const VariantVector & value)243 void SplinePath::SetControlPointIdsAttr(const VariantVector& value)
244 {
245 // Just remember the node IDs. They need to go through the SceneResolver, and we actually find the nodes during
246 // ApplyAttributes()
247 if (value.Size())
248 {
249 controlPointIdsAttr_.Clear();
250
251 unsigned index = 0;
252 unsigned numInstances = value[index++].GetUInt();
253 // Prevent crash on entering negative value in the editor
254 if (numInstances > M_MAX_INT)
255 numInstances = 0;
256
257 controlPointIdsAttr_.Push(numInstances);
258 while (numInstances--)
259 {
260 // If vector contains less IDs than should, fill the rest with zeros
261 if (index < value.Size())
262 controlPointIdsAttr_.Push(value[index++].GetUInt());
263 else
264 controlPointIdsAttr_.Push(0);
265 }
266
267 dirty_ = true;
268 }
269 else
270 {
271 controlPointIdsAttr_.Clear();
272 controlPointIdsAttr_.Push(0);
273
274 dirty_ = true;
275 }
276 }
277
SetControlledIdAttr(unsigned value)278 void SplinePath::SetControlledIdAttr(unsigned value)
279 {
280 if (value > 0 && value < M_MAX_UNSIGNED)
281 controlledIdAttr_ = value;
282
283 dirty_ = true;
284 }
285
OnMarkedDirty(Node * point)286 void SplinePath::OnMarkedDirty(Node* point)
287 {
288 if (!point)
289 return;
290
291 WeakPtr<Node> controlPoint(point);
292
293 for (unsigned i = 0; i < controlPoints_.Size(); ++i)
294 {
295 if (controlPoints_[i] == controlPoint)
296 {
297 spline_.SetKnot(point->GetWorldPosition(), i);
298 break;
299 }
300 }
301
302 CalculateLength();
303 }
304
OnNodeSetEnabled(Node * point)305 void SplinePath::OnNodeSetEnabled(Node* point)
306 {
307 if (!point)
308 return;
309
310 WeakPtr<Node> controlPoint(point);
311
312 for (unsigned i = 0; i < controlPoints_.Size(); ++i)
313 {
314 if (controlPoints_[i] == controlPoint)
315 {
316 if (point->IsEnabled())
317 spline_.AddKnot(point->GetWorldPosition(), i);
318 else
319 spline_.RemoveKnot(i);
320
321 break;
322 }
323 }
324
325 CalculateLength();
326 }
327
UpdateNodeIds()328 void SplinePath::UpdateNodeIds()
329 {
330 unsigned numInstances = controlPoints_.Size();
331
332 controlPointIdsAttr_.Clear();
333 controlPointIdsAttr_.Push(numInstances);
334
335 for (unsigned i = 0; i < numInstances; ++i)
336 {
337 Node* node = controlPoints_[i];
338 controlPointIdsAttr_.Push(node ? node->GetID() : 0);
339 }
340 }
341
CalculateLength()342 void SplinePath::CalculateLength()
343 {
344 if (spline_.GetKnots().Size() <= 0)
345 return;
346
347 length_ = 0.f;
348
349 Vector3 a = spline_.GetKnot(0).GetVector3();
350 for (float f = 0.000f; f <= 1.000f; f += 0.001f)
351 {
352 Vector3 b = spline_.GetPoint(f).GetVector3();
353 length_ += Abs((a - b).Length());
354 a = b;
355 }
356 }
357
358 }
359