RibbonTrail.cpp 27 KB

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