SplinePath.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. //
  2. // Copyright (c) 2008-2015 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. #include "../Core/Context.h"
  23. #include "../IO/Log.h"
  24. #include "../Scene/Scene.h"
  25. #include "../Scene/SplinePath.h"
  26. namespace Urho3D
  27. {
  28. extern const char* interpolationModeNames[];
  29. extern const char* LOGIC_CATEGORY;
  30. SplinePath::SplinePath(Context* context) :
  31. Component(context),
  32. spline_(BEZIER_CURVE),
  33. speed_(1.f),
  34. elapsedTime_(0.f),
  35. traveled_(0.f),
  36. length_(0.f),
  37. dirty_(false),
  38. controlledNode_(NULL),
  39. controlledIdAttr_(0)
  40. {
  41. UpdateNodeIds();
  42. }
  43. void SplinePath::RegisterObject(Context* context)
  44. {
  45. context->RegisterFactory<SplinePath>(LOGIC_CATEGORY);
  46. ENUM_ACCESSOR_ATTRIBUTE("Interpolation Mode", GetInterpolationMode, SetInterpolationMode, InterpolationMode, interpolationModeNames, BEZIER_CURVE, AM_FILE);
  47. ATTRIBUTE("Speed", float, speed_, 1.f, AM_FILE);
  48. ATTRIBUTE("Traveled", float, traveled_, 0.f, AM_FILE | AM_NOEDIT);
  49. ATTRIBUTE("Elapsed Time", float, elapsedTime_, 0.f, AM_FILE | AM_NOEDIT);
  50. ACCESSOR_ATTRIBUTE("Controlled", GetControlledIdAttr, SetControlledIdAttr, unsigned, 0, AM_FILE | AM_NODEID);
  51. ACCESSOR_ATTRIBUTE("Control Points", GetControlPointIdsAttr, SetControlPointIdsAttr, VariantVector, Variant::emptyVariantVector, AM_FILE | AM_NODEIDVECTOR);
  52. }
  53. void SplinePath::ApplyAttributes()
  54. {
  55. if (!dirty_)
  56. return;
  57. // Remove all old instance nodes before searching for new. Can not call RemoveAllInstances() as that would modify
  58. // the ID list on its own
  59. for (unsigned i = 0; i < controlPoints_.Size(); ++i)
  60. {
  61. Node* node = controlPoints_[i];
  62. if (node)
  63. node->RemoveListener(this);
  64. }
  65. controlPoints_.Clear();
  66. spline_.Clear();
  67. Scene* scene = GetScene();
  68. if (scene)
  69. {
  70. // The first index stores the number of IDs redundantly. This is for editing
  71. for (unsigned i = 1; i < controlPointIdsAttr_.Size(); ++i)
  72. {
  73. Node* node = scene->GetNode(controlPointIdsAttr_[i].GetUInt());
  74. if (node)
  75. {
  76. WeakPtr<Node> controlPoint(node);
  77. node->AddListener(this);
  78. controlPoints_.Push(controlPoint);
  79. spline_.AddKnot(node->GetWorldPosition());
  80. }
  81. }
  82. Node* node = scene->GetNode(controlledIdAttr_);
  83. if (node)
  84. {
  85. WeakPtr<Node> controlled(node);
  86. controlledNode_ = controlled;
  87. }
  88. }
  89. CalculateLength();
  90. dirty_ = false;
  91. }
  92. void SplinePath::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
  93. {
  94. if (debug && node_ && IsEnabledEffective())
  95. {
  96. if (spline_.GetKnots().Size() > 1)
  97. {
  98. Vector3 a = spline_.GetPoint(0.f).GetVector3();
  99. for (float f = 0.01f; f <= 1.0f; f = f + 0.01f)
  100. {
  101. Vector3 b = spline_.GetPoint(f).GetVector3();
  102. debug->AddLine(a, b, Color::GREEN);
  103. a = b;
  104. }
  105. }
  106. for (Vector<WeakPtr<Node> >::ConstIterator i = controlPoints_.Begin(); i != controlPoints_.End(); ++i)
  107. debug->AddNode(*i);
  108. if (controlledNode_)
  109. debug->AddNode(controlledNode_);
  110. }
  111. }
  112. void SplinePath::AddControlPoint(Node* point, unsigned index)
  113. {
  114. if (!point)
  115. return;
  116. WeakPtr<Node> controlPoint(point);
  117. point->AddListener(this);
  118. controlPoints_.Insert(index, controlPoint);
  119. spline_.AddKnot(point->GetWorldPosition(), index);
  120. UpdateNodeIds();
  121. CalculateLength();
  122. }
  123. void SplinePath::RemoveControlPoint(Node* point)
  124. {
  125. if (!point)
  126. return;
  127. WeakPtr<Node> controlPoint(point);
  128. point->RemoveListener(this);
  129. for (unsigned i = 0; i < controlPoints_.Size(); ++i)
  130. {
  131. if (controlPoints_[i] == controlPoint)
  132. {
  133. controlPoints_.Erase(i);
  134. spline_.RemoveKnot(i);
  135. break;
  136. }
  137. }
  138. UpdateNodeIds();
  139. CalculateLength();
  140. }
  141. void SplinePath::ClearControlPoints()
  142. {
  143. for (unsigned i = 0; i < controlPoints_.Size(); ++i)
  144. {
  145. Node* node = controlPoints_[i];
  146. if (node)
  147. node->RemoveListener(this);
  148. }
  149. controlPoints_.Clear();
  150. spline_.Clear();
  151. UpdateNodeIds();
  152. CalculateLength();
  153. }
  154. void SplinePath::SetControlledNode(Node* controlled)
  155. {
  156. if (controlled)
  157. controlledNode_ = WeakPtr<Node>(controlled);
  158. }
  159. void SplinePath::SetInterpolationMode(InterpolationMode interpolationMode)
  160. {
  161. spline_.SetInterpolationMode(interpolationMode);
  162. CalculateLength();
  163. }
  164. void SplinePath::SetPosition(float factor)
  165. {
  166. float t = factor;
  167. if (t < 0.f)
  168. t = 0.0f;
  169. else if (t > 1.0f)
  170. t = 1.0f;
  171. traveled_ = t;
  172. }
  173. Vector3 SplinePath::GetPoint(float factor) const
  174. {
  175. return spline_.GetPoint(factor).GetVector3();
  176. }
  177. void SplinePath::Move(float timeStep)
  178. {
  179. if (traveled_ >= 1.0f || length_ <= 0.0f || controlledNode_.Null())
  180. return;
  181. elapsedTime_ += timeStep;
  182. // 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.
  183. float distanceCovered = elapsedTime_ * speed_;
  184. traveled_ = distanceCovered / length_;
  185. controlledNode_->SetWorldPosition(GetPoint(traveled_));
  186. }
  187. void SplinePath::Reset()
  188. {
  189. traveled_ = 0.f;
  190. elapsedTime_ = 0.f;
  191. }
  192. void SplinePath::SetControlPointIdsAttr(const VariantVector& value)
  193. {
  194. // Just remember the node IDs. They need to go through the SceneResolver, and we actually find the nodes during
  195. // ApplyAttributes()
  196. if (value.Size())
  197. {
  198. controlPointIdsAttr_.Clear();
  199. unsigned index = 0;
  200. unsigned numInstances = value[index++].GetUInt();
  201. // Prevent crash on entering negative value in the editor
  202. if (numInstances > M_MAX_INT)
  203. numInstances = 0;
  204. controlPointIdsAttr_.Push(numInstances);
  205. while (numInstances--)
  206. {
  207. // If vector contains less IDs than should, fill the rest with zeros
  208. if (index < value.Size())
  209. controlPointIdsAttr_.Push(value[index++].GetUInt());
  210. else
  211. controlPointIdsAttr_.Push(0);
  212. }
  213. dirty_ = true;
  214. }
  215. else
  216. {
  217. controlPointIdsAttr_.Clear();
  218. controlPointIdsAttr_.Push(0);
  219. dirty_ = true;
  220. }
  221. }
  222. void SplinePath::SetControlledIdAttr(unsigned value)
  223. {
  224. if (value > 0 && value < M_MAX_UNSIGNED)
  225. controlledIdAttr_ = value;
  226. dirty_ = true;
  227. }
  228. void SplinePath::OnMarkedDirty(Node* point)
  229. {
  230. if (!point)
  231. return;
  232. WeakPtr<Node> controlPoint(point);
  233. for (unsigned i = 0; i < controlPoints_.Size(); ++i)
  234. {
  235. if (controlPoints_[i] == controlPoint)
  236. {
  237. spline_.SetKnot(point->GetWorldPosition(), i);
  238. break;
  239. }
  240. }
  241. CalculateLength();
  242. }
  243. void SplinePath::OnNodeSetEnabled(Node* point)
  244. {
  245. if (!point)
  246. return;
  247. WeakPtr<Node> controlPoint(point);
  248. for (unsigned i = 0; i < controlPoints_.Size(); ++i)
  249. {
  250. if (controlPoints_[i] == controlPoint)
  251. {
  252. if (point->IsEnabled())
  253. spline_.AddKnot(point->GetWorldPosition(), i);
  254. else
  255. spline_.RemoveKnot(i);
  256. break;
  257. }
  258. }
  259. CalculateLength();
  260. }
  261. void SplinePath::UpdateNodeIds()
  262. {
  263. unsigned numInstances = controlPoints_.Size();
  264. controlPointIdsAttr_.Clear();
  265. controlPointIdsAttr_.Push(numInstances);
  266. for (unsigned i = 0; i < numInstances; ++i)
  267. {
  268. Node* node = controlPoints_[i];
  269. controlPointIdsAttr_.Push(node ? node->GetID() : 0);
  270. }
  271. }
  272. void SplinePath::CalculateLength()
  273. {
  274. if (spline_.GetKnots().Size() <= 0)
  275. return;
  276. length_ = 0.f;
  277. Vector3 a = spline_.GetKnot(0).GetVector3();
  278. for (float f = 0.000f; f <= 1.000f; f += 0.001f)
  279. {
  280. Vector3 b = spline_.GetPoint(f).GetVector3();
  281. length_ += Abs((a - b).Length());
  282. a = b;
  283. }
  284. }
  285. }