| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892 |
- // Copyright (c) 2008-2023 the Urho3D project
- // License: MIT
- #include "../Precompiled.h"
- #include "../Core/Context.h"
- #include "../Graphics/Camera.h"
- #include "../Graphics/Geometry.h"
- #include "../Graphics/Material.h"
- #include "../Graphics/OctreeQuery.h"
- #include "../Graphics/RibbonTrail.h"
- #include "../GraphicsAPI/IndexBuffer.h"
- #include "../GraphicsAPI/VertexBuffer.h"
- #include "../IO/Log.h"
- #include "../Resource/ResourceCache.h"
- #include "../Scene/Scene.h"
- #include "../Scene/SceneEvents.h"
- namespace Urho3D
- {
- extern const char* GEOMETRY_CATEGORY;
- static const unsigned MAX_TAIL_COLUMN = 16;
- const char* trailTypeNames[] =
- {
- "Face Camera",
- "Bone",
- nullptr
- };
- inline bool CompareTails(TrailPoint* lhs, TrailPoint* rhs)
- {
- return lhs->sortDistance_ > rhs->sortDistance_;
- }
- TrailPoint::TrailPoint(const Vector3& position, const Vector3& forward) :
- position_{position},
- forward_{forward}
- {
- }
- RibbonTrail::RibbonTrail(Context* context) :
- Drawable(context, DrawableTypes::Geometry),
- geometry_(new Geometry(context_)),
- animationLodBias_(1.0f),
- animationLodTimer_(0.0f),
- vertexBuffer_(new VertexBuffer(context_)),
- indexBuffer_(new IndexBuffer(context_)),
- transforms_(Matrix3x4::IDENTITY),
- bufferSizeDirty_(false),
- bufferDirty_(true),
- previousPosition_(Vector3::ZERO),
- numPoints_(0),
- lifetime_(1.0f),
- vertexDistance_(0.1f),
- width_(0.2f),
- startScale_(1.0f),
- endScale_(1.0f),
- lastTimeStep_(0.0f),
- endColor_(Color(1.0f, 1.0f, 1.0f, 0.0f)),
- startColor_(Color(1.0f, 1.0f, 1.0f, 1.0f)),
- lastUpdateFrameNumber_(NINDEX),
- needUpdate_(false),
- sorted_(false),
- previousOffset_(Vector3::ZERO),
- forceUpdate_(false),
- trailType_(TT_FACE_CAMERA),
- tailColumn_(1),
- updateInvisible_(false),
- emitting_(true),
- startEndTailTime_(0.0f)
- {
- geometry_->SetVertexBuffer(0, vertexBuffer_);
- geometry_->SetIndexBuffer(indexBuffer_);
- batches_.Resize(1);
- batches_[0].geometry_ = geometry_;
- batches_[0].geometryType_ = GEOM_TRAIL_FACE_CAMERA;
- batches_[0].worldTransform_ = &transforms_;
- batches_[0].numWorldTransforms_ = 1;
- }
- RibbonTrail::~RibbonTrail() = default;
- void RibbonTrail::RegisterObject(Context* context)
- {
- context->RegisterFactory<RibbonTrail>(GEOMETRY_CATEGORY);
- URHO3D_ACCESSOR_ATTRIBUTE("Is Enabled", IsEnabled, SetEnabled,true, AM_DEFAULT);
- URHO3D_COPY_BASE_ATTRIBUTES(Drawable);
- URHO3D_ACCESSOR_ATTRIBUTE("Material", GetMaterialAttr, SetMaterialAttr, ResourceRef(Material::GetTypeStatic()), AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Emitting", IsEmitting, SetEmitting, true, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Update Invisible", GetUpdateInvisible, SetUpdateInvisible, false, AM_DEFAULT);
- URHO3D_ENUM_ACCESSOR_ATTRIBUTE("Trail Type", GetTrailType, SetTrailType, trailTypeNames, TT_FACE_CAMERA, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Base Velocity", GetBaseVelocity, SetBaseVelocity, Vector3::ZERO, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Tail Lifetime", GetLifetime, SetLifetime, 1.0f, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Tail Column", GetTailColumn, SetTailColumn, 0, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Vertex Distance", GetVertexDistance, SetVertexDistance, 0.1f, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Width", GetWidth, SetWidth, 0.2f, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Start Scale", GetStartScale, SetStartScale, 1.0f, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("End Scale", GetEndScale, SetEndScale, 1.0f, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Start Color", GetStartColor, SetStartColor, Color::WHITE, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("End Color", GetEndColor, SetEndColor, Color(1.0f, 1.0f, 1.0f, 0.0f), AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, 1.0f, AM_DEFAULT);
- URHO3D_ACCESSOR_ATTRIBUTE("Sort By Distance", IsSorted, SetSorted, false, AM_DEFAULT);
- }
- void RibbonTrail::ProcessRayQuery(const RayOctreeQuery& query, Vector<RayQueryResult>& results)
- {
- // If no trail-level testing, use the Drawable test
- if (query.level_ < RAY_TRIANGLE)
- {
- Drawable::ProcessRayQuery(query, results);
- return;
- }
- // Check ray hit distance to AABB before proceeding with trail-level tests
- if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_)
- return;
- // Approximate the tails as spheres for raycasting
- for (i32 i = 0; i < points_.Size() - 1; ++i)
- {
- Vector3 center = (points_[i].position_ + points_[i+1].position_) * 0.5f;
- Vector3 scale = width_ * Vector3::ONE;
- // Tail should be represented in cylinder shape, but we don't have this yet on Urho,
- // so this implementation will use bounding box instead (hopefully only temporarily)
- float distance = query.ray_.HitDistance(BoundingBox(center - scale, center + scale));
- if (distance < query.maxDistance_)
- {
- // If the code reaches here then we have a hit
- RayQueryResult result;
- result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
- result.normal_ = -query.ray_.direction_;
- result.distance_ = distance;
- result.drawable_ = this;
- result.node_ = node_;
- result.subObject_ = i;
- results.Push(result);
- }
- }
- }
- void RibbonTrail::OnSetEnabled()
- {
- Drawable::OnSetEnabled();
- previousPosition_ = node_->GetWorldPosition();
- Scene* scene = GetScene();
- if (scene)
- {
- if (IsEnabledEffective())
- SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(RibbonTrail, HandleScenePostUpdate));
- else
- UnsubscribeFromEvent(scene, E_SCENEPOSTUPDATE);
- }
- }
- void RibbonTrail::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
- {
- using namespace ScenePostUpdate;
- lastTimeStep_ = eventData[P_TIMESTEP].GetFloat();
- // Update if frame has changed
- if (updateInvisible_ || viewFrameNumber_ != lastUpdateFrameNumber_)
- {
- // Reset if ribbon trail is too small and too much difference in frame
- if (points_.Size() < 3 && (viewFrameNumber_ - lastUpdateFrameNumber_) > 1)
- {
- previousPosition_ = node_->GetWorldPosition();
- points_.Erase(0, points_.Size());
- }
- lastUpdateFrameNumber_ = viewFrameNumber_;
- needUpdate_ = true;
- MarkForUpdate();
- }
- }
- void RibbonTrail::Update(const FrameInfo &frame)
- {
- Drawable::Update(frame);
- if (!needUpdate_)
- return;
- UpdateTail(frame.timeStep_);
- OnMarkedDirty(node_);
- needUpdate_ = false;
- }
- void RibbonTrail::UpdateTail(float timeStep)
- {
- // Apply base velocity to all cached positions
- if (baseVelocity_ != Vector3::ZERO)
- {
- for (TrailPoint& point : points_)
- point.position_ += baseVelocity_ * timeStep;
- previousPosition_ += baseVelocity_ * timeStep;
- }
- const Vector3 worldPosition = node_->GetWorldPosition();
- const float path = (previousPosition_ - worldPosition).Length();
- // Update tails lifetime
- int expiredIndex = -1;
- if (points_.Size() > 0)
- {
- // No need to update last point
- for (i32 i = 0; i < points_.Size() - 1; ++i)
- {
- points_[i].lifetime_ += lastTimeStep_;
- // Get point index with expired lifetime
- if (points_[i].lifetime_ > lifetime_)
- expiredIndex = i;
- }
- }
- // Delete expired points
- if (expiredIndex != -1)
- {
- points_.Erase(0, expiredIndex + 1);
- // Update endTail pointer
- if (points_.Size() > 1)
- {
- endTail_.position_ = points_[0].position_;
- startEndTailTime_ = points_[0].lifetime_;
- }
- }
- // Update previous world position if trail is still zero
- if (points_.Size() == 0)
- {
- previousPosition_ = worldPosition;
- }
- // Delete lonely point
- else if (points_.Size() == 1)
- {
- points_.Erase(0, 1);
- previousPosition_ = worldPosition;
- }
- // Update end of trail position using endTail linear interpolation
- else if (points_.Size() > 1 && points_[0].lifetime_ < lifetime_)
- {
- const float step = SmoothStep(startEndTailTime_, lifetime_, points_[0].lifetime_);
- points_[0].position_ = Lerp(endTail_.position_, points_[1].position_, step);
- bufferDirty_ = true;
- }
- // Add starting points
- if (points_.Size() == 0 && path > M_LARGE_EPSILON && emitting_)
- {
- const Vector3 forwardMotion = (previousPosition_ - worldPosition).Normalized();
- TrailPoint startPoint{previousPosition_, forwardMotion};
- TrailPoint nextPoint{worldPosition, forwardMotion};
- if (node_->GetParent() != nullptr)
- {
- startPoint.parentPos_ = node_->GetParent()->GetWorldPosition();
- nextPoint.parentPos_ = node_->GetParent()->GetWorldPosition();
- }
- points_.Push(startPoint);
- points_.Push(nextPoint);
- // Update endTail
- endTail_.position_ = startPoint.position_;
- startEndTailTime_ = 0.0f;
- }
- // Add more points
- if (points_.Size() > 1 && emitting_)
- {
- const Vector3 forwardMotion = (previousPosition_ - worldPosition).Normalized();
- // Add more points if path exceeded tail length
- if (path > vertexDistance_)
- {
- TrailPoint newPoint{worldPosition, forwardMotion};
- if (node_->GetParent() != nullptr)
- newPoint.parentPos_ = node_->GetParent()->GetWorldPosition();
- points_.Push(newPoint);
- previousPosition_ = worldPosition;
- }
- else
- {
- // Update recent tail
- points_.Back().position_ = worldPosition;
- if (forwardMotion != Vector3::ZERO)
- points_.Back().forward_ = forwardMotion;
- }
- }
- // Update buffer size if size of points different with tail number
- if (points_.Size() != numPoints_)
- bufferSizeDirty_ = true;
- }
- void RibbonTrail::SetEndScale(float endScale)
- {
- endScale_ = endScale;
- Commit();
- }
- void RibbonTrail::SetStartScale(float startScale)
- {
- startScale_ = startScale;
- Commit();
- }
- void RibbonTrail::SetEmitting(bool emitting)
- {
- if (emitting == emitting_)
- return;
- emitting_ = emitting;
- // Reset already available points
- if (emitting && points_.Size() > 0)
- {
- points_.Clear();
- bufferSizeDirty_ = true;
- }
- Drawable::OnMarkedDirty(node_);
- MarkNetworkUpdate();
- }
- void RibbonTrail::SetTailColumn(unsigned tailColumn)
- {
- if (tailColumn > MAX_TAIL_COLUMN)
- {
- URHO3D_LOGWARNING("Max ribbon trail tail column is " + String(MAX_TAIL_COLUMN));
- tailColumn_ = MAX_TAIL_COLUMN;
- }
- else if (tailColumn < 1)
- {
- tailColumn_ = 1;
- }
- else
- tailColumn_ = tailColumn;
- Drawable::OnMarkedDirty(node_);
- bufferSizeDirty_ = true;
- MarkNetworkUpdate();
- }
- void RibbonTrail::UpdateBatches(const FrameInfo& frame)
- {
- // Update information for renderer about this drawable
- distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center());
- batches_[0].distance_ = distance_;
- // Calculate scaled distance for animation LOD
- float scale = GetWorldBoundingBox().Size().DotProduct(DOT_SCALE);
- // If there are no trail, the size becomes zero, and LOD'ed updates no longer happen. Disable LOD in that case
- if (scale > M_EPSILON)
- lodDistance_ = frame.camera_->GetLodDistance(distance_, scale, lodBias_);
- else
- lodDistance_ = 0.0f;
- Vector3 worldPos = node_->GetWorldPosition();
- Vector3 offset = (worldPos - frame.camera_->GetNode()->GetWorldPosition());
- if (sorted_ && offset != previousOffset_)
- {
- bufferDirty_ = true;
- previousOffset_ = offset;
- }
- }
- void RibbonTrail::UpdateGeometry(const FrameInfo& frame)
- {
- if (bufferSizeDirty_ || indexBuffer_->IsDataLost())
- UpdateBufferSize();
- if (bufferDirty_ || vertexBuffer_->IsDataLost())
- UpdateVertexBuffer(frame);
- }
- UpdateGeometryType RibbonTrail::GetUpdateGeometryType()
- {
- if (bufferDirty_ || bufferSizeDirty_ || vertexBuffer_->IsDataLost() || indexBuffer_->IsDataLost())
- return UPDATE_MAIN_THREAD;
- else
- return UPDATE_NONE;
- }
- void RibbonTrail::SetMaterial(Material* material)
- {
- batches_[0].material_ = material;
- MarkNetworkUpdate();
- }
- void RibbonTrail::OnSceneSet(Scene* scene)
- {
- Drawable::OnSceneSet(scene);
- if (scene && IsEnabledEffective())
- SubscribeToEvent(scene, E_SCENEPOSTUPDATE, URHO3D_HANDLER(RibbonTrail, HandleScenePostUpdate));
- else if (!scene)
- UnsubscribeFromEvent(E_SCENEPOSTUPDATE);
- }
- void RibbonTrail::OnWorldBoundingBoxUpdate()
- {
- BoundingBox worldBox;
- for (i32 i = 0; i < points_.Size(); ++i)
- {
- Vector3 &p = points_[i].position_;
- Vector3 scale = width_ * Vector3::ONE;
- worldBox.Merge(BoundingBox(p - scale, p + scale));
- }
- worldBoundingBox_ = worldBox;
- }
- void RibbonTrail::UpdateBufferSize()
- {
- numPoints_ = points_.Size();
- unsigned indexPerSegment = 6 + (tailColumn_ - 1) * 6;
- unsigned vertexPerSegment = 4 + (tailColumn_ - 1) * 2;
- VertexElements mask = VertexElements::None;
- if (trailType_ == TT_FACE_CAMERA)
- {
- batches_[0].geometryType_ = GEOM_TRAIL_FACE_CAMERA;
- mask = VertexElements::Position | VertexElements::Color | VertexElements::TexCoord1 | VertexElements::Tangent;
- }
- else if (trailType_ == TT_BONE)
- {
- batches_[0].geometryType_ = GEOM_TRAIL_BONE;
- mask = VertexElements::Position | VertexElements::Normal | VertexElements::Color | VertexElements::TexCoord1 | VertexElements::Tangent;
- }
- bufferSizeDirty_ = false;
- bufferDirty_ = true;
- forceUpdate_ = true;
- if (numPoints_ < 2)
- {
- indexBuffer_->SetSize(0, false);
- vertexBuffer_->SetSize(0, mask, true);
- return;
- }
- else
- {
- indexBuffer_->SetSize(((numPoints_ - 1) * indexPerSegment), false);
- vertexBuffer_->SetSize(numPoints_ * vertexPerSegment, mask, true);
- }
- // Indices do not change for a given tail generator capacity
- auto* dest = (unsigned short*)indexBuffer_->Lock(0, ((numPoints_ - 1) * indexPerSegment), true);
- if (!dest)
- return;
- unsigned vertexIndex = 0;
- unsigned stripsLen = numPoints_ - 1;
- while (stripsLen--)
- {
- dest[0] = (unsigned short)vertexIndex;
- dest[1] = (unsigned short)(vertexIndex + 2);
- dest[2] = (unsigned short)(vertexIndex + 1);
- dest[3] = (unsigned short)(vertexIndex + 1);
- dest[4] = (unsigned short)(vertexIndex + 2);
- dest[5] = (unsigned short)(vertexIndex + 3);
- dest += 6;
- vertexIndex += 2;
- for (unsigned i = 0; i < (tailColumn_ - 1); ++i)
- {
- dest[0] = (unsigned short)vertexIndex;
- dest[1] = (unsigned short)(vertexIndex + 2);
- dest[2] = (unsigned short)(vertexIndex + 1);
- dest[3] = (unsigned short)(vertexIndex + 1);
- dest[4] = (unsigned short)(vertexIndex + 2);
- dest[5] = (unsigned short)(vertexIndex + 3);
- dest += 6;
- vertexIndex += 2;
- }
- vertexIndex += 2;
- }
- indexBuffer_->Unlock();
- indexBuffer_->ClearDataLost();
- }
- void RibbonTrail::UpdateVertexBuffer(const FrameInfo& frame)
- {
- // If using animation LOD, accumulate time and see if it is time to update
- if (animationLodBias_ > 0.0f && lodDistance_ > 0.0f)
- {
- animationLodTimer_ += animationLodBias_ * frame.timeStep_ * ANIMATION_LOD_BASESCALE;
- if (animationLodTimer_ >= lodDistance_)
- animationLodTimer_ = fmodf(animationLodTimer_, lodDistance_);
- else
- {
- // No LOD if immediate update forced
- if (!forceUpdate_)
- return;
- }
- }
- // if tail path is short and nothing to draw, exit
- if (numPoints_ < 2)
- {
- batches_[0].geometry_->SetDrawRange(TRIANGLE_LIST, 0, 0, false);
- return;
- }
- unsigned indexPerSegment = 6 + (tailColumn_ - 1) * 6;
- unsigned vertexPerSegment = 4 + (tailColumn_ - 1) * 2;
- // Fill sorted points vector
- sortedPoints_.Resize(numPoints_);
- for (unsigned i = 0; i < numPoints_; ++i)
- {
- TrailPoint& point = points_[i];
- sortedPoints_[i] = &point;
- if (sorted_)
- point.sortDistance_ = frame.camera_->GetDistanceSquared(point.position_);
- }
- // Sort points
- if (sorted_)
- Sort(sortedPoints_.Begin(), sortedPoints_.End(), CompareTails);
- // Update individual trail elapsed length
- float trailLength = 0.0f;
- for(unsigned i = 0; i < numPoints_; ++i)
- {
- float length = i == 0 ? 0.0f : (points_[i].position_ - points_[i-1].position_).Length();
- trailLength += length;
- points_[i].elapsedLength_ = trailLength;
- if (i < numPoints_ - 1)
- points_[i].next_ = &points_[i+1];
- }
- batches_[0].geometry_->SetDrawRange(TRIANGLE_LIST, 0, (numPoints_ - 1) * indexPerSegment, false);
- bufferDirty_ = false;
- forceUpdate_ = false;
- auto* dest = (float*)vertexBuffer_->Lock(0, (numPoints_ - 1) * vertexPerSegment, true);
- if (!dest)
- return;
- // Generate trail mesh
- if (trailType_ == TT_FACE_CAMERA)
- {
- for (unsigned i = 0; i < numPoints_; ++i)
- {
- TrailPoint& point = *sortedPoints_[i];
- if (sortedPoints_[i] == &points_.Back()) continue;
- // This point
- float factor = SmoothStep(0.0f, trailLength, point.elapsedLength_);
- color32 c = endColor_.Lerp(startColor_, factor).ToU32();
- float width = Lerp(width_ * endScale_, width_ * startScale_, factor);
- // Next point
- float nextFactor = SmoothStep(0.0f, trailLength, point.next_->elapsedLength_);
- color32 nextC = endColor_.Lerp(startColor_, nextFactor).ToU32();
- float nextWidth = Lerp(width_ * endScale_, width_ * startScale_, nextFactor);
- // First row
- dest[0] = point.position_.x_;
- dest[1] = point.position_.y_;
- dest[2] = point.position_.z_;
- ((color32&)dest[3]) = c;
- dest[4] = factor;
- dest[5] = 0.0f;
- dest[6] = point.forward_.x_;
- dest[7] = point.forward_.y_;
- dest[8] = point.forward_.z_;
- dest[9] = width;
- dest[10] = point.next_->position_.x_;
- dest[11] = point.next_->position_.y_;
- dest[12] = point.next_->position_.z_;
- ((color32&)dest[13]) = nextC;
- dest[14] = nextFactor;
- dest[15] = 0.0f;
- dest[16] = point.next_->forward_.x_;
- dest[17] = point.next_->forward_.y_;
- dest[18] = point.next_->forward_.z_;
- dest[19] = nextWidth;
- dest += 20;
- // Middle rows
- for (unsigned j = 0; j < (tailColumn_ - 1); ++j)
- {
- float elapsed = 1.0f / tailColumn_ * (j + 1);
- float midWidth = width - elapsed * 2.0f * width;
- float nextMidWidth = nextWidth - elapsed * 2.0f * nextWidth;
- dest[0] = point.position_.x_;
- dest[1] = point.position_.y_;
- dest[2] = point.position_.z_;
- ((color32&)dest[3]) = c;
- dest[4] = factor;
- dest[5] = elapsed;
- dest[6] = point.forward_.x_;
- dest[7] = point.forward_.y_;
- dest[8] = point.forward_.z_;
- dest[9] = midWidth;
- dest[10] = point.next_->position_.x_;
- dest[11] = point.next_->position_.y_;
- dest[12] = point.next_->position_.z_;
- ((color32&)dest[13]) = nextC;
- dest[14] = nextFactor;
- dest[15] = elapsed;
- dest[16] = point.next_->forward_.x_;
- dest[17] = point.next_->forward_.y_;
- dest[18] = point.next_->forward_.z_;
- dest[19] = nextMidWidth;
- dest += 20;
- }
- // Last row
- dest[0] = point.position_.x_;
- dest[1] = point.position_.y_;
- dest[2] = point.position_.z_;
- ((unsigned&)dest[3]) = c;
- dest[4] = factor;
- dest[5] = 1.0f;
- dest[6] = point.forward_.x_;
- dest[7] = point.forward_.y_;
- dest[8] = point.forward_.z_;
- dest[9] = -width;
- dest[10] = point.next_->position_.x_;
- dest[11] = point.next_->position_.y_;
- dest[12] = point.next_->position_.z_;
- ((unsigned&)dest[13]) = nextC;
- dest[14] = nextFactor;
- dest[15] = 1.0f;
- dest[16] = point.next_->forward_.x_;
- dest[17] = point.next_->forward_.y_;
- dest[18] = point.next_->forward_.z_;
- dest[19] = -nextWidth;
- dest += 20;
- }
- }
- else if (trailType_ == TT_BONE)
- {
- for (unsigned i = 0; i < numPoints_; ++i)
- {
- TrailPoint& point = *sortedPoints_[i];
- if (sortedPoints_[i] == &points_.Back()) continue;
- // This point
- float factor = SmoothStep(0.0f, trailLength, point.elapsedLength_);
- color32 c = endColor_.Lerp(startColor_, factor).ToU32();
- float rightScale = Lerp(endScale_, startScale_, factor);
- float shift = (rightScale - 1.0f) / 2.0f;
- float leftScale = 0.0f - shift;
- // Next point
- float nextFactor = SmoothStep(0.0f, trailLength, point.next_->elapsedLength_);
- color32 nextC = endColor_.Lerp(startColor_, nextFactor).ToU32();
- float nextRightScale = Lerp(endScale_, startScale_, nextFactor);
- float nextShift = (nextRightScale - 1.0f) / 2.0f;
- float nextLeftScale = 0.0f - nextShift;
- // First row
- dest[0] = point.position_.x_;
- dest[1] = point.position_.y_;
- dest[2] = point.position_.z_;
- dest[3] = point.forward_.x_;
- dest[4] = point.forward_.y_;
- dest[5] = point.forward_.z_;
- ((color32&)dest[6]) = c;
- dest[7] = factor;
- dest[8] = 0.0f;
- dest[9] = point.parentPos_.x_;
- dest[10] = point.parentPos_.y_;
- dest[11] = point.parentPos_.z_;
- dest[12] = leftScale;
- dest[13] = point.next_->position_.x_;
- dest[14] = point.next_->position_.y_;
- dest[15] = point.next_->position_.z_;
- dest[16] = point.next_->forward_.x_;
- dest[17] = point.next_->forward_.y_;
- dest[18] = point.next_->forward_.z_;
- ((color32&)dest[19]) = nextC;
- dest[20] = nextFactor;
- dest[21] = 0.0f;
- dest[22] = point.next_->parentPos_.x_;
- dest[23] = point.next_->parentPos_.y_;
- dest[24] = point.next_->parentPos_.z_;
- dest[25] = nextLeftScale;
- dest += 26;
- // Middle row
- for (unsigned j = 0; j < (tailColumn_ - 1); ++j)
- {
- float elapsed = 1.0f / tailColumn_ * (j + 1);
- dest[0] = point.position_.x_;
- dest[1] = point.position_.y_;
- dest[2] = point.position_.z_;
- dest[3] = point.forward_.x_;
- dest[4] = point.forward_.y_;
- dest[5] = point.forward_.z_;
- ((color32&)dest[6]) = c;
- dest[7] = factor;
- dest[8] = elapsed;
- dest[9] = point.parentPos_.x_;
- dest[10] = point.parentPos_.y_;
- dest[11] = point.parentPos_.z_;
- dest[12] = Lerp(leftScale, rightScale, elapsed);
- dest[13] = point.next_->position_.x_;
- dest[14] = point.next_->position_.y_;
- dest[15] = point.next_->position_.z_;
- dest[16] = point.next_->forward_.x_;
- dest[17] = point.next_->forward_.y_;
- dest[18] = point.next_->forward_.z_;
- ((color32&)dest[19]) = nextC;
- dest[20] = nextFactor;
- dest[21] = elapsed;
- dest[22] = point.next_->parentPos_.x_;
- dest[23] = point.next_->parentPos_.y_;
- dest[24] = point.next_->parentPos_.z_;
- dest[25] = Lerp(nextLeftScale, nextRightScale, elapsed);
- dest += 26;
- }
- // Last row
- dest[0] = point.position_.x_;
- dest[1] = point.position_.y_;
- dest[2] = point.position_.z_;
- dest[3] = point.forward_.x_;
- dest[4] = point.forward_.y_;
- dest[5] = point.forward_.z_;
- ((color32&)dest[6]) = c;
- dest[7] = factor;
- dest[8] = 1.0f;
- dest[9] = point.parentPos_.x_;
- dest[10] = point.parentPos_.y_;
- dest[11] = point.parentPos_.z_;
- dest[12] = rightScale;
- dest[13] = point.next_->position_.x_;
- dest[14] = point.next_->position_.y_;
- dest[15] = point.next_->position_.z_;
- dest[16] = point.next_->forward_.x_;
- dest[17] = point.next_->forward_.y_;
- dest[18] = point.next_->forward_.z_;
- ((color32&)dest[19]) = nextC;
- dest[20] = nextFactor;
- dest[21] = 1.0f;
- dest[22] = point.next_->parentPos_.x_;
- dest[23] = point.next_->parentPos_.y_;
- dest[24] = point.next_->parentPos_.z_;
- dest[25] = nextRightScale;
- dest += 26;
- }
- }
- vertexBuffer_->Unlock();
- vertexBuffer_->ClearDataLost();
- }
- void RibbonTrail::SetLifetime(float time)
- {
- lifetime_ = time;
- Commit();
- }
- void RibbonTrail::SetVertexDistance(float length)
- {
- vertexDistance_ = length;
- Commit();
- }
- void RibbonTrail::SetEndColor(const Color& color)
- {
- endColor_ = color;
- Commit();
- }
- void RibbonTrail::SetStartColor(const Color& color)
- {
- startColor_ = color;
- Commit();
- }
- void RibbonTrail::SetSorted(bool enable)
- {
- sorted_ = enable;
- Commit();
- }
- void RibbonTrail::SetTrailType(TrailType type)
- {
- if (trailType_ == type)
- return;
- if (type == TT_BONE && (node_->GetParent() == nullptr || node_->GetParent() == node_->GetScene()))
- {
- URHO3D_LOGWARNING("No parent node found, revert back to Face Camera type");
- return;
- }
- trailType_ = type;
- Drawable::OnMarkedDirty(node_);
- bufferSizeDirty_ = true;
- MarkNetworkUpdate();
- }
- void RibbonTrail::SetBaseVelocity(const Vector3& baseVelocity)
- {
- baseVelocity_ = baseVelocity;
- }
- void RibbonTrail::SetMaterialAttr(const ResourceRef& value)
- {
- auto* cache = GetSubsystem<ResourceCache>();
- SetMaterial(cache->GetResource<Material>(value.name_));
- Commit();
- }
- void RibbonTrail::SetWidth(float width)
- {
- width_ = width;
- Commit();
- }
- void RibbonTrail::SetAnimationLodBias(float bias)
- {
- animationLodBias_ = Max(bias, 0.0f);
- MarkNetworkUpdate();
- }
- void RibbonTrail::SetUpdateInvisible(bool enable)
- {
- updateInvisible_ = enable;
- MarkNetworkUpdate();
- }
- void RibbonTrail::Commit()
- {
- MarkPositionsDirty();
- MarkNetworkUpdate();
- }
- void RibbonTrail::MarkPositionsDirty()
- {
- Drawable::OnMarkedDirty(node_);
- bufferDirty_ = true;
- }
- Material* RibbonTrail::GetMaterial() const
- {
- return batches_[0].material_;
- }
- ResourceRef RibbonTrail::GetMaterialAttr() const
- {
- return GetResourceRef(batches_[0].material_, Material::GetTypeStatic());
- }
- }
|