RibbonTrail.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. //
  2. // Copyright (c) 2008-2020 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 "../Precompiled.h"
  23. #include "../Core/Context.h"
  24. #include "../Graphics/RibbonTrail.h"
  25. #include "../Graphics/VertexBuffer.h"
  26. #include "../Graphics/IndexBuffer.h"
  27. #include "../Graphics/Camera.h"
  28. #include "../Graphics/Material.h"
  29. #include "../Graphics/OctreeQuery.h"
  30. #include "../Graphics/Geometry.h"
  31. #include "../Scene/Scene.h"
  32. #include "../Scene/SceneEvents.h"
  33. #include "../Resource/ResourceCache.h"
  34. #include "../IO/Log.h"
  35. namespace Urho3D
  36. {
  37. extern const char* GEOMETRY_CATEGORY;
  38. static const unsigned MAX_TAIL_COLUMN = 16;
  39. const char* trailTypeNames[] =
  40. {
  41. "Face Camera",
  42. "Bone",
  43. nullptr
  44. };
  45. inline bool CompareTails(TrailPoint* lhs, TrailPoint* rhs)
  46. {
  47. return lhs->sortDistance_ > rhs->sortDistance_;
  48. }
  49. TrailPoint::TrailPoint(const Vector3& position, const Vector3& forward) :
  50. position_{position},
  51. forward_{forward}
  52. {
  53. }
  54. RibbonTrail::RibbonTrail(Context* context) :
  55. Drawable(context, DRAWABLE_GEOMETRY),
  56. geometry_(new Geometry(context_)),
  57. animationLodBias_(1.0f),
  58. animationLodTimer_(0.0f),
  59. vertexBuffer_(new VertexBuffer(context_)),
  60. indexBuffer_(new IndexBuffer(context_)),
  61. transforms_(Matrix3x4::IDENTITY),
  62. bufferSizeDirty_(false),
  63. bufferDirty_(true),
  64. previousPosition_(Vector3::ZERO),
  65. numPoints_(0),
  66. lifetime_(1.0f),
  67. vertexDistance_(0.1f),
  68. width_(0.2f),
  69. startScale_(1.0f),
  70. endScale_(1.0f),
  71. lastTimeStep_(0.0f),
  72. endColor_(Color(1.0f, 1.0f, 1.0f, 0.0f)),
  73. startColor_(Color(1.0f, 1.0f, 1.0f, 1.0f)),
  74. lastUpdateFrameNumber_(M_MAX_UNSIGNED),
  75. needUpdate_(false),
  76. sorted_(false),
  77. previousOffset_(Vector3::ZERO),
  78. forceUpdate_(false),
  79. trailType_(TT_FACE_CAMERA),
  80. tailColumn_(1),
  81. updateInvisible_(false),
  82. emitting_(true),
  83. startEndTailTime_(0.0f)
  84. {
  85. geometry_->SetVertexBuffer(0, vertexBuffer_);
  86. geometry_->SetIndexBuffer(indexBuffer_);
  87. batches_.Resize(1);
  88. batches_[0].geometry_ = geometry_;
  89. batches_[0].geometryType_ = GEOM_TRAIL_FACE_CAMERA;
  90. batches_[0].worldTransform_ = &transforms_;
  91. batches_[0].numWorldTransforms_ = 1;
  92. }
  93. RibbonTrail::~RibbonTrail() = default;
  94. void RibbonTrail::RegisterObject(Context* context)
  95. {
  96. context->RegisterFactory<RibbonTrail>(GEOMETRY_CATEGORY);
  97. URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled, bool, true, AM_DEFAULT);
  98. URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
  99. URHO3D_MIXED_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
  100. URHO3D_ACCESSOR_ATTRIBUTE("Emitting", IsEmitting, SetEmitting, bool, true, AM_DEFAULT);
  101. URHO3D_ACCESSOR_ATTRIBUTE("Update Invisible", GetUpdateInvisible, SetUpdateInvisible, bool, false, AM_DEFAULT);
  102. URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Trail Type", GetTrailType, SetTrailType, TrailType, trailTypeNames, TT_FACE_CAMERA, AM_DEFAULT);
  103. URHO3D_ACCESSOR_ATTRIBUTE("Base Velocity", GetBaseVelocity, SetBaseVelocity, Vector3, Vector3::ZERO, AM_DEFAULT);
  104. URHO3D_ACCESSOR_ATTRIBUTE("Tail Lifetime", GetLifetime, SetLifetime, float, 1.0f, AM_DEFAULT);
  105. URHO3D_ACCESSOR_ATTRIBUTE("Tail Column", GetTailColumn, SetTailColumn, unsigned, 0, AM_DEFAULT);
  106. URHO3D_ACCESSOR_ATTRIBUTE("Vertex Distance", GetVertexDistance, SetVertexDistance, float, 0.1f, AM_DEFAULT);
  107. URHO3D_ACCESSOR_ATTRIBUTE("Width", GetWidth, SetWidth, float, 0.2f, AM_DEFAULT);
  108. URHO3D_ACCESSOR_ATTRIBUTE("Start Scale", GetStartScale, SetStartScale, float, 1.0f, AM_DEFAULT);
  109. URHO3D_ACCESSOR_ATTRIBUTE("End Scale", GetEndScale, SetEndScale, float, 1.0f, AM_DEFAULT);
  110. URHO3D_ACCESSOR_ATTRIBUTE("Start Color", GetStartColor, SetStartColor, Color, Color::WHITE, AM_DEFAULT);
  111. URHO3D_ACCESSOR_ATTRIBUTE("End Color", GetEndColor, SetEndColor, Color, Color(1.0f, 1.0f, 1.0f, 0.0f), AM_DEFAULT);
  112. URHO3D_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
  113. URHO3D_ACCESSOR_ATTRIBUTE("Sort By Distance", IsSorted, SetSorted, bool, false, AM_DEFAULT);
  114. }
  115. void RibbonTrail::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
  116. {
  117. // If no trail-level testing, use the Drawable test
  118. if (query.level_ < RAY_TRIANGLE)
  119. {
  120. Drawable::ProcessRayQuery(query, results);
  121. return;
  122. }
  123. // Check ray hit distance to AABB before proceeding with trail-level tests
  124. if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_)
  125. return;
  126. // Approximate the tails as spheres for raycasting
  127. for (unsigned i = 0; i < points_.Size() - 1; ++i)
  128. {
  129. Vector3 center = (points_[i].position_ + points_[i+1].position_) * 0.5f;
  130. Vector3 scale = width_ * Vector3::ONE;
  131. // Tail should be represented in cylinder shape, but we don't have this yet on Urho,
  132. // so this implementation will use bounding box instead (hopefully only temporarily)
  133. float distance = query.ray_.HitDistance(BoundingBox(center - scale, center + scale));
  134. if (distance < query.maxDistance_)
  135. {
  136. // If the code reaches here then we have a hit
  137. RayQueryResult result;
  138. result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
  139. result.normal_ = -query.ray_.direction_;
  140. result.distance_ = distance;
  141. result.drawable_ = this;
  142. result.node_ = node_;
  143. result.subObject_ = i;
  144. results.Push(result);
  145. }
  146. }
  147. }
  148. void RibbonTrail::OnSetEnabled()
  149. {
  150. Drawable::OnSetEnabled();
  151. previousPosition_ = node_->GetWorldPosition();
  152. Scene* scene = GetScene();
  153. if (scene)
  154. {
  155. if (IsEnabledEffective())
  156. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(RibbonTrail, HandleScenePostUpdate));
  157. else
  158. UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
  159. }
  160. }
  161. void RibbonTrail::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
  162. {
  163. using namespace ScenePostUpdate;
  164. lastTimeStep_ = eventData[P_TIMESTEP].GetFloat();
  165. // Update if frame has changed
  166. if (updateInvisible_ || viewFrameNumber_ != lastUpdateFrameNumber_)
  167. {
  168. // Reset if ribbon trail is too small and too much difference in frame
  169. if (points_.Size() < 3 && viewFrameNumber_ - lastUpdateFrameNumber_ > 1)
  170. {
  171. previousPosition_ = node_->GetWorldPosition();
  172. points_.Erase(0, points_.Size());
  173. }
  174. lastUpdateFrameNumber_ = viewFrameNumber_;
  175. needUpdate_ = true;
  176. MarkForUpdate();
  177. }
  178. }
  179. void RibbonTrail::Update(const FrameInfo &frame)
  180. {
  181. Drawable::Update(frame);
  182. if (!needUpdate_)
  183. return;
  184. UpdateTail(frame.timeStep_);
  185. OnMarkedDirty(node_);
  186. needUpdate_ = false;
  187. }
  188. void RibbonTrail::UpdateTail(float timeStep)
  189. {
  190. // Apply base velocity to all cached positions
  191. if (baseVelocity_ != Vector3::ZERO)
  192. {
  193. for (TrailPoint& point : points_)
  194. point.position_ += baseVelocity_ * timeStep;
  195. previousPosition_ += baseVelocity_ * timeStep;
  196. }
  197. const Vector3 worldPosition = node_->GetWorldPosition();
  198. const float path = (previousPosition_ - worldPosition).Length();
  199. // Update tails lifetime
  200. int expiredIndex = -1;
  201. if (points_.Size() > 0)
  202. {
  203. // No need to update last point
  204. for (unsigned i = 0; i < points_.Size() - 1; ++i)
  205. {
  206. points_[i].lifetime_ += lastTimeStep_;
  207. // Get point index with expired lifetime
  208. if (points_[i].lifetime_ > lifetime_)
  209. expiredIndex = i;
  210. }
  211. }
  212. // Delete expired points
  213. if (expiredIndex != -1)
  214. {
  215. points_.Erase(0, (unsigned)(expiredIndex + 1));
  216. // Update endTail pointer
  217. if (points_.Size() > 1)
  218. {
  219. endTail_.position_ = points_[0].position_;
  220. startEndTailTime_ = points_[0].lifetime_;
  221. }
  222. }
  223. // Update previous world position if trail is still zero
  224. if (points_.Size() == 0)
  225. {
  226. previousPosition_ = worldPosition;
  227. }
  228. // Delete lonely point
  229. else if (points_.Size() == 1)
  230. {
  231. points_.Erase(0, 1);
  232. previousPosition_ = worldPosition;
  233. }
  234. // Update end of trail position using endTail linear interpolation
  235. else if (points_.Size() > 1 && points_[0].lifetime_ < lifetime_)
  236. {
  237. const float step = SmoothStep(startEndTailTime_, lifetime_, points_[0].lifetime_);
  238. points_[0].position_ = Lerp(endTail_.position_, points_[1].position_, step);
  239. bufferDirty_ = true;
  240. }
  241. // Add starting points
  242. if (points_.Size() == 0 && path > M_LARGE_EPSILON && emitting_)
  243. {
  244. const Vector3 forwardMotion = (previousPosition_ - worldPosition).Normalized();
  245. TrailPoint startPoint{previousPosition_, forwardMotion};
  246. TrailPoint nextPoint{worldPosition, forwardMotion};
  247. if (node_->GetParent() != nullptr)
  248. {
  249. startPoint.parentPos_ = node_->GetParent()->GetWorldPosition();
  250. nextPoint.parentPos_ = node_->GetParent()->GetWorldPosition();
  251. }
  252. points_.Push(startPoint);
  253. points_.Push(nextPoint);
  254. // Update endTail
  255. endTail_.position_ = startPoint.position_;
  256. startEndTailTime_ = 0.0f;
  257. }
  258. // Add more points
  259. if (points_.Size() > 1 && emitting_)
  260. {
  261. const Vector3 forwardMotion = (previousPosition_ - worldPosition).Normalized();
  262. // Add more points if path exceeded tail length
  263. if (path > vertexDistance_)
  264. {
  265. TrailPoint newPoint{worldPosition, forwardMotion};
  266. if (node_->GetParent() != nullptr)
  267. newPoint.parentPos_ = node_->GetParent()->GetWorldPosition();
  268. points_.Push(newPoint);
  269. previousPosition_ = worldPosition;
  270. }
  271. else
  272. {
  273. // Update recent tail
  274. points_.Back().position_ = worldPosition;
  275. if (forwardMotion != Vector3::ZERO)
  276. points_.Back().forward_ = forwardMotion;
  277. }
  278. }
  279. // Update buffer size if size of points different with tail number
  280. if (points_.Size() != numPoints_)
  281. bufferSizeDirty_ = true;
  282. }
  283. void RibbonTrail::SetEndScale(float endScale)
  284. {
  285. endScale_ = endScale;
  286. Commit();
  287. }
  288. void RibbonTrail::SetStartScale(float startScale)
  289. {
  290. startScale_ = startScale;
  291. Commit();
  292. }
  293. void RibbonTrail::SetEmitting(bool emitting)
  294. {
  295. if (emitting == emitting_)
  296. return;
  297. emitting_ = emitting;
  298. // Reset already available points
  299. if (emitting && points_.Size() > 0)
  300. {
  301. points_.Clear();
  302. bufferSizeDirty_ = true;
  303. }
  304. Drawable::OnMarkedDirty(node_);
  305. MarkNetworkUpdate();
  306. }
  307. void RibbonTrail::SetTailColumn(unsigned tailColumn)
  308. {
  309. if (tailColumn > MAX_TAIL_COLUMN)
  310. {
  311. URHO3D_LOGWARNING("Max ribbon trail tail column is " + String(MAX_TAIL_COLUMN));
  312. tailColumn_ = MAX_TAIL_COLUMN;
  313. }
  314. else if (tailColumn < 1)
  315. {
  316. tailColumn_ = 1;
  317. }
  318. else
  319. tailColumn_ = tailColumn;
  320. Drawable::OnMarkedDirty(node_);
  321. bufferSizeDirty_ = true;
  322. MarkNetworkUpdate();
  323. }
  324. void RibbonTrail::UpdateBatches(const FrameInfo& frame)
  325. {
  326. // Update information for renderer about this drawable
  327. distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center());
  328. batches_[0].distance_ = distance_;
  329. // Calculate scaled distance for animation LOD
  330. float scale = GetWorldBoundingBox().Size().DotProduct(DOT_SCALE);
  331. // If there are no trail, the size becomes zero, and LOD'ed updates no longer happen. Disable LOD in that case
  332. if (scale > M_EPSILON)
  333. lodDistance_ = frame.camera_->GetLodDistance(distance_, scale, lodBias_);
  334. else
  335. lodDistance_ = 0.0f;
  336. Vector3 worldPos = node_->GetWorldPosition();
  337. Vector3 offset = (worldPos - frame.camera_->GetNode()->GetWorldPosition());
  338. if (sorted_ && offset != previousOffset_)
  339. {
  340. bufferDirty_ = true;
  341. previousOffset_ = offset;
  342. }
  343. }
  344. void RibbonTrail::UpdateGeometry(const FrameInfo& frame)
  345. {
  346. if (bufferSizeDirty_ || indexBuffer_->IsDataLost())
  347. UpdateBufferSize();
  348. if (bufferDirty_ || vertexBuffer_->IsDataLost())
  349. UpdateVertexBuffer(frame);
  350. }
  351. UpdateGeometryType RibbonTrail::GetUpdateGeometryType()
  352. {
  353. if (bufferDirty_ || bufferSizeDirty_ || vertexBuffer_->IsDataLost() || indexBuffer_->IsDataLost())
  354. return UPDATE_MAIN_THREAD;
  355. else
  356. return UPDATE_NONE;
  357. }
  358. void RibbonTrail::SetMaterial(Material* material)
  359. {
  360. batches_[0].material_ = material;
  361. MarkNetworkUpdate();
  362. }
  363. void RibbonTrail::OnSceneSet(Scene* scene)
  364. {
  365. Drawable::OnSceneSet(scene);
  366. if (scene && IsEnabledEffective())
  367. SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(RibbonTrail, HandleScenePostUpdate));
  368. else if (!scene)
  369. UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
  370. }
  371. void RibbonTrail::OnWorldBoundingBoxUpdate()
  372. {
  373. BoundingBox worldBox;
  374. for (unsigned i = 0; i < points_.Size(); ++i)
  375. {
  376. Vector3 &p = points_[i].position_;
  377. Vector3 scale = width_ * Vector3::ONE;
  378. worldBox.Merge(BoundingBox(p - scale, p + scale));
  379. }
  380. worldBoundingBox_ = worldBox;
  381. }
  382. void RibbonTrail::UpdateBufferSize()
  383. {
  384. numPoints_ = points_.Size();
  385. unsigned indexPerSegment = 6 + (tailColumn_ - 1) * 6;
  386. unsigned vertexPerSegment = 4 + (tailColumn_ - 1) * 2;
  387. unsigned mask = 0;
  388. if (trailType_ == TT_FACE_CAMERA)
  389. {
  390. batches_[0].geometryType_ = GEOM_TRAIL_FACE_CAMERA;
  391. mask = MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1 | MASK_TANGENT;
  392. }
  393. else if (trailType_ == TT_BONE)
  394. {
  395. batches_[0].geometryType_ = GEOM_TRAIL_BONE;
  396. mask = MASK_POSITION | MASK_NORMAL | MASK_COLOR | MASK_TEXCOORD1 | MASK_TANGENT;
  397. }
  398. bufferSizeDirty_ = false;
  399. bufferDirty_ = true;
  400. forceUpdate_ = true;
  401. if (numPoints_ < 2)
  402. {
  403. indexBuffer_->SetSize(0, false);
  404. vertexBuffer_->SetSize(0, mask, true);
  405. return;
  406. }
  407. else
  408. {
  409. indexBuffer_->SetSize(((numPoints_ - 1) * indexPerSegment), false);
  410. vertexBuffer_->SetSize(numPoints_ * vertexPerSegment, mask, true);
  411. }
  412. // Indices do not change for a given tail generator capacity
  413. auto* dest = (unsigned short*)indexBuffer_->Lock(0, ((numPoints_ - 1) * indexPerSegment), true);
  414. if (!dest)
  415. return;
  416. unsigned vertexIndex = 0;
  417. unsigned stripsLen = numPoints_ - 1;
  418. while (stripsLen--)
  419. {
  420. dest[0] = (unsigned short)vertexIndex;
  421. dest[1] = (unsigned short)(vertexIndex + 2);
  422. dest[2] = (unsigned short)(vertexIndex + 1);
  423. dest[3] = (unsigned short)(vertexIndex + 1);
  424. dest[4] = (unsigned short)(vertexIndex + 2);
  425. dest[5] = (unsigned short)(vertexIndex + 3);
  426. dest += 6;
  427. vertexIndex += 2;
  428. for (unsigned i = 0; i < (tailColumn_ - 1); ++i)
  429. {
  430. dest[0] = (unsigned short)vertexIndex;
  431. dest[1] = (unsigned short)(vertexIndex + 2);
  432. dest[2] = (unsigned short)(vertexIndex + 1);
  433. dest[3] = (unsigned short)(vertexIndex + 1);
  434. dest[4] = (unsigned short)(vertexIndex + 2);
  435. dest[5] = (unsigned short)(vertexIndex + 3);
  436. dest += 6;
  437. vertexIndex += 2;
  438. }
  439. vertexIndex += 2;
  440. }
  441. indexBuffer_->Unlock();
  442. indexBuffer_->ClearDataLost();
  443. }
  444. void RibbonTrail::UpdateVertexBuffer(const FrameInfo& frame)
  445. {
  446. // If using animation LOD, accumulate time and see if it is time to update
  447. if (animationLodBias_ > 0.0f && lodDistance_ > 0.0f)
  448. {
  449. animationLodTimer_ += animationLodBias_ * frame.timeStep_ * ANIMATION_LOD_BASESCALE;
  450. if (animationLodTimer_ >= lodDistance_)
  451. animationLodTimer_ = fmodf(animationLodTimer_, lodDistance_);
  452. else
  453. {
  454. // No LOD if immediate update forced
  455. if (!forceUpdate_)
  456. return;
  457. }
  458. }
  459. // if tail path is short and nothing to draw, exit
  460. if (numPoints_ < 2)
  461. {
  462. batches_[0].geometry_->SetDrawRange(TRIANGLE_LIST, 0, 0, false);
  463. return;
  464. }
  465. unsigned indexPerSegment = 6 + (tailColumn_ - 1) * 6;
  466. unsigned vertexPerSegment = 4 + (tailColumn_ - 1) * 2;
  467. // Fill sorted points vector
  468. sortedPoints_.Resize(numPoints_);
  469. for (unsigned i = 0; i < numPoints_; ++i)
  470. {
  471. TrailPoint& point = points_[i];
  472. sortedPoints_[i] = &point;
  473. if (sorted_)
  474. point.sortDistance_ = frame.camera_->GetDistanceSquared(point.position_);
  475. }
  476. // Sort points
  477. if (sorted_)
  478. Sort(sortedPoints_.Begin(), sortedPoints_.End(), CompareTails);
  479. // Update individual trail elapsed length
  480. float trailLength = 0.0f;
  481. for(unsigned i = 0; i < numPoints_; ++i)
  482. {
  483. float length = i == 0 ? 0.0f : (points_[i].position_ - points_[i-1].position_).Length();
  484. trailLength += length;
  485. points_[i].elapsedLength_ = trailLength;
  486. if (i < numPoints_ - 1)
  487. points_[i].next_ = &points_[i+1];
  488. }
  489. batches_[0].geometry_->SetDrawRange(TRIANGLE_LIST, 0, (numPoints_ - 1) * indexPerSegment, false);
  490. bufferDirty_ = false;
  491. forceUpdate_ = false;
  492. auto* dest = (float*)vertexBuffer_->Lock(0, (numPoints_ - 1) * vertexPerSegment, true);
  493. if (!dest)
  494. return;
  495. // Generate trail mesh
  496. if (trailType_ == TT_FACE_CAMERA)
  497. {
  498. for (unsigned i = 0; i < numPoints_; ++i)
  499. {
  500. TrailPoint& point = *sortedPoints_[i];
  501. if (sortedPoints_[i] == &points_.Back()) continue;
  502. // This point
  503. float factor = SmoothStep(0.0f, trailLength, point.elapsedLength_);
  504. unsigned c = endColor_.Lerp(startColor_, factor).ToUInt();
  505. float width = Lerp(width_ * endScale_, width_ * startScale_, factor);
  506. // Next point
  507. float nextFactor = SmoothStep(0.0f, trailLength, point.next_->elapsedLength_);
  508. unsigned nextC = endColor_.Lerp(startColor_, nextFactor).ToUInt();
  509. float nextWidth = Lerp(width_ * endScale_, width_ * startScale_, nextFactor);
  510. // First row
  511. dest[0] = point.position_.x_;
  512. dest[1] = point.position_.y_;
  513. dest[2] = point.position_.z_;
  514. ((unsigned&)dest[3]) = c;
  515. dest[4] = factor;
  516. dest[5] = 0.0f;
  517. dest[6] = point.forward_.x_;
  518. dest[7] = point.forward_.y_;
  519. dest[8] = point.forward_.z_;
  520. dest[9] = width;
  521. dest[10] = point.next_->position_.x_;
  522. dest[11] = point.next_->position_.y_;
  523. dest[12] = point.next_->position_.z_;
  524. ((unsigned&)dest[13]) = nextC;
  525. dest[14] = nextFactor;
  526. dest[15] = 0.0f;
  527. dest[16] = point.next_->forward_.x_;
  528. dest[17] = point.next_->forward_.y_;
  529. dest[18] = point.next_->forward_.z_;
  530. dest[19] = nextWidth;
  531. dest += 20;
  532. // Middle rows
  533. for (unsigned j = 0; j < (tailColumn_ - 1); ++j)
  534. {
  535. float elapsed = 1.0f / tailColumn_ * (j + 1);
  536. float midWidth = width - elapsed * 2.0f * width;
  537. float nextMidWidth = nextWidth - elapsed * 2.0f * nextWidth;
  538. dest[0] = point.position_.x_;
  539. dest[1] = point.position_.y_;
  540. dest[2] = point.position_.z_;
  541. ((unsigned&)dest[3]) = c;
  542. dest[4] = factor;
  543. dest[5] = elapsed;
  544. dest[6] = point.forward_.x_;
  545. dest[7] = point.forward_.y_;
  546. dest[8] = point.forward_.z_;
  547. dest[9] = midWidth;
  548. dest[10] = point.next_->position_.x_;
  549. dest[11] = point.next_->position_.y_;
  550. dest[12] = point.next_->position_.z_;
  551. ((unsigned&)dest[13]) = nextC;
  552. dest[14] = nextFactor;
  553. dest[15] = elapsed;
  554. dest[16] = point.next_->forward_.x_;
  555. dest[17] = point.next_->forward_.y_;
  556. dest[18] = point.next_->forward_.z_;
  557. dest[19] = nextMidWidth;
  558. dest += 20;
  559. }
  560. // Last row
  561. dest[0] = point.position_.x_;
  562. dest[1] = point.position_.y_;
  563. dest[2] = point.position_.z_;
  564. ((unsigned&)dest[3]) = c;
  565. dest[4] = factor;
  566. dest[5] = 1.0f;
  567. dest[6] = point.forward_.x_;
  568. dest[7] = point.forward_.y_;
  569. dest[8] = point.forward_.z_;
  570. dest[9] = -width;
  571. dest[10] = point.next_->position_.x_;
  572. dest[11] = point.next_->position_.y_;
  573. dest[12] = point.next_->position_.z_;
  574. ((unsigned&)dest[13]) = nextC;
  575. dest[14] = nextFactor;
  576. dest[15] = 1.0f;
  577. dest[16] = point.next_->forward_.x_;
  578. dest[17] = point.next_->forward_.y_;
  579. dest[18] = point.next_->forward_.z_;
  580. dest[19] = -nextWidth;
  581. dest += 20;
  582. }
  583. }
  584. else if (trailType_ == TT_BONE)
  585. {
  586. for (unsigned i = 0; i < numPoints_; ++i)
  587. {
  588. TrailPoint& point = *sortedPoints_[i];
  589. if (sortedPoints_[i] == &points_.Back()) continue;
  590. // This point
  591. float factor = SmoothStep(0.0f, trailLength, point.elapsedLength_);
  592. unsigned c = endColor_.Lerp(startColor_, factor).ToUInt();
  593. float rightScale = Lerp(endScale_, startScale_, factor);
  594. float shift = (rightScale - 1.0f) / 2.0f;
  595. float leftScale = 0.0f - shift;
  596. // Next point
  597. float nextFactor = SmoothStep(0.0f, trailLength, point.next_->elapsedLength_);
  598. unsigned nextC = endColor_.Lerp(startColor_, nextFactor).ToUInt();
  599. float nextRightScale = Lerp(endScale_, startScale_, nextFactor);
  600. float nextShift = (nextRightScale - 1.0f) / 2.0f;
  601. float nextLeftScale = 0.0f - nextShift;
  602. // First row
  603. dest[0] = point.position_.x_;
  604. dest[1] = point.position_.y_;
  605. dest[2] = point.position_.z_;
  606. dest[3] = point.forward_.x_;
  607. dest[4] = point.forward_.y_;
  608. dest[5] = point.forward_.z_;
  609. ((unsigned&)dest[6]) = c;
  610. dest[7] = factor;
  611. dest[8] = 0.0f;
  612. dest[9] = point.parentPos_.x_;
  613. dest[10] = point.parentPos_.y_;
  614. dest[11] = point.parentPos_.z_;
  615. dest[12] = leftScale;
  616. dest[13] = point.next_->position_.x_;
  617. dest[14] = point.next_->position_.y_;
  618. dest[15] = point.next_->position_.z_;
  619. dest[16] = point.next_->forward_.x_;
  620. dest[17] = point.next_->forward_.y_;
  621. dest[18] = point.next_->forward_.z_;
  622. ((unsigned&)dest[19]) = nextC;
  623. dest[20] = nextFactor;
  624. dest[21] = 0.0f;
  625. dest[22] = point.next_->parentPos_.x_;
  626. dest[23] = point.next_->parentPos_.y_;
  627. dest[24] = point.next_->parentPos_.z_;
  628. dest[25] = nextLeftScale;
  629. dest += 26;
  630. // Middle row
  631. for (unsigned j = 0; j < (tailColumn_ - 1); ++j)
  632. {
  633. float elapsed = 1.0f / tailColumn_ * (j + 1);
  634. dest[0] = point.position_.x_;
  635. dest[1] = point.position_.y_;
  636. dest[2] = point.position_.z_;
  637. dest[3] = point.forward_.x_;
  638. dest[4] = point.forward_.y_;
  639. dest[5] = point.forward_.z_;
  640. ((unsigned&)dest[6]) = c;
  641. dest[7] = factor;
  642. dest[8] = elapsed;
  643. dest[9] = point.parentPos_.x_;
  644. dest[10] = point.parentPos_.y_;
  645. dest[11] = point.parentPos_.z_;
  646. dest[12] = Lerp(leftScale, rightScale, elapsed);
  647. dest[13] = point.next_->position_.x_;
  648. dest[14] = point.next_->position_.y_;
  649. dest[15] = point.next_->position_.z_;
  650. dest[16] = point.next_->forward_.x_;
  651. dest[17] = point.next_->forward_.y_;
  652. dest[18] = point.next_->forward_.z_;
  653. ((unsigned&)dest[19]) = nextC;
  654. dest[20] = nextFactor;
  655. dest[21] = elapsed;
  656. dest[22] = point.next_->parentPos_.x_;
  657. dest[23] = point.next_->parentPos_.y_;
  658. dest[24] = point.next_->parentPos_.z_;
  659. dest[25] = Lerp(nextLeftScale, nextRightScale, elapsed);
  660. dest += 26;
  661. }
  662. // Last row
  663. dest[0] = point.position_.x_;
  664. dest[1] = point.position_.y_;
  665. dest[2] = point.position_.z_;
  666. dest[3] = point.forward_.x_;
  667. dest[4] = point.forward_.y_;
  668. dest[5] = point.forward_.z_;
  669. ((unsigned&)dest[6]) = c;
  670. dest[7] = factor;
  671. dest[8] = 1.0f;
  672. dest[9] = point.parentPos_.x_;
  673. dest[10] = point.parentPos_.y_;
  674. dest[11] = point.parentPos_.z_;
  675. dest[12] = rightScale;
  676. dest[13] = point.next_->position_.x_;
  677. dest[14] = point.next_->position_.y_;
  678. dest[15] = point.next_->position_.z_;
  679. dest[16] = point.next_->forward_.x_;
  680. dest[17] = point.next_->forward_.y_;
  681. dest[18] = point.next_->forward_.z_;
  682. ((unsigned&)dest[19]) = nextC;
  683. dest[20] = nextFactor;
  684. dest[21] = 1.0f;
  685. dest[22] = point.next_->parentPos_.x_;
  686. dest[23] = point.next_->parentPos_.y_;
  687. dest[24] = point.next_->parentPos_.z_;
  688. dest[25] = nextRightScale;
  689. dest += 26;
  690. }
  691. }
  692. vertexBuffer_->Unlock();
  693. vertexBuffer_->ClearDataLost();
  694. }
  695. void RibbonTrail::SetLifetime(float time)
  696. {
  697. lifetime_ = time;
  698. Commit();
  699. }
  700. void RibbonTrail::SetVertexDistance(float length)
  701. {
  702. vertexDistance_ = length;
  703. Commit();
  704. }
  705. void RibbonTrail::SetEndColor(const Color& color)
  706. {
  707. endColor_ = color;
  708. Commit();
  709. }
  710. void RibbonTrail::SetStartColor(const Color& color)
  711. {
  712. startColor_ = color;
  713. Commit();
  714. }
  715. void RibbonTrail::SetSorted(bool enable)
  716. {
  717. sorted_ = enable;
  718. Commit();
  719. }
  720. void RibbonTrail::SetTrailType(TrailType type)
  721. {
  722. if (trailType_ == type)
  723. return;
  724. if (type == TT_BONE && (node_->GetParent() == nullptr || node_->GetParent() == node_->GetScene()))
  725. {
  726. URHO3D_LOGWARNING("No parent node found, revert back to Face Camera type");
  727. return;
  728. }
  729. trailType_ = type;
  730. Drawable::OnMarkedDirty(node_);
  731. bufferSizeDirty_ = true;
  732. MarkNetworkUpdate();
  733. }
  734. void RibbonTrail::SetBaseVelocity(const Vector3& baseVelocity)
  735. {
  736. baseVelocity_ = baseVelocity;
  737. }
  738. void RibbonTrail::SetMaterialAttr(const ResourceRef& value)
  739. {
  740. auto* cache = GetSubsystem<ResourceCache>();
  741. SetMaterial(cache->GetResource<Material>(value.name_));
  742. Commit();
  743. }
  744. void RibbonTrail::SetWidth(float width)
  745. {
  746. width_ = width;
  747. Commit();
  748. }
  749. void RibbonTrail::SetAnimationLodBias(float bias)
  750. {
  751. animationLodBias_ = Max(bias, 0.0f);
  752. MarkNetworkUpdate();
  753. }
  754. void RibbonTrail::SetUpdateInvisible(bool enable)
  755. {
  756. updateInvisible_ = enable;
  757. MarkNetworkUpdate();
  758. }
  759. void RibbonTrail::Commit()
  760. {
  761. MarkPositionsDirty();
  762. MarkNetworkUpdate();
  763. }
  764. void RibbonTrail::MarkPositionsDirty()
  765. {
  766. Drawable::OnMarkedDirty(node_);
  767. bufferDirty_ = true;
  768. }
  769. Material* RibbonTrail::GetMaterial() const
  770. {
  771. return batches_[0].material_;
  772. }
  773. ResourceRef RibbonTrail::GetMaterialAttr() const
  774. {
  775. return GetResourceRef(batches_[0].material_, Material::GetTypeStatic());
  776. }
  777. }