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