| 218 | } |
| 219 | |
| 220 | void SplineModel::UpdateDeformationBuffer() |
| 221 | { |
| 222 | PROFILE_CPU(); |
| 223 | |
| 224 | // Deformation buffer contains precomputed matrices for each chunk of the spline segment (packed with transposed float3x4 matrix) |
| 225 | _deformationDirty = false; |
| 226 | if (!_deformationBuffer) |
| 227 | _deformationBuffer = GPUDevice::Instance->CreateBuffer(GetName()); |
| 228 | const auto& keyframes = _spline->Curve.GetKeyframes(); |
| 229 | const int32 segments = keyframes.Count() - 1; |
| 230 | const int32 chunksPerSegment = Math::Clamp(Math::CeilToInt(SPLINE_RESOLUTION * _quality), 2, 1024); |
| 231 | const int32 count = (chunksPerSegment * segments + 1) * 3; |
| 232 | const uint32 size = count * sizeof(Float4); |
| 233 | if (_deformationBuffer->GetSize() != size) |
| 234 | { |
| 235 | if (_deformationBufferData) |
| 236 | { |
| 237 | Allocator::Free(_deformationBufferData); |
| 238 | _deformationBufferData = nullptr; |
| 239 | } |
| 240 | if (_deformationBuffer->Init(GPUBufferDescription::Typed(count, PixelFormat::R32G32B32A32_Float, false, IsTransformStatic() ? GPUResourceUsage::Default : GPUResourceUsage::Dynamic))) |
| 241 | { |
| 242 | LOG(Error, "Failed to initialize the spline model {0} deformation buffer.", ToString()); |
| 243 | return; |
| 244 | } |
| 245 | } |
| 246 | if (!_deformationBufferData) |
| 247 | _deformationBufferData = Allocator::Allocate(size); |
| 248 | _chunksPerSegment = (float)chunksPerSegment; |
| 249 | |
| 250 | // Update pre-calculated matrices for spline chunks |
| 251 | auto ptr = (Matrix3x4*)_deformationBufferData; |
| 252 | const float chunksPerSegmentInv = 1.0f / (float)chunksPerSegment; |
| 253 | Matrix m; |
| 254 | Transform transform, leftTangent, rightTangent; |
| 255 | for (int32 segment = 0; segment < segments; segment++) |
| 256 | { |
| 257 | auto& instance = _instances[segment]; |
| 258 | const auto& start = keyframes[segment]; |
| 259 | const auto& end = keyframes[segment + 1]; |
| 260 | const float tangentScale = (end.Time - start.Time) / 3.0f; |
| 261 | AnimationUtils::GetTangent(start.Value, start.TangentOut, tangentScale, leftTangent); |
| 262 | AnimationUtils::GetTangent(end.Value, end.TangentIn, tangentScale, rightTangent); |
| 263 | for (int32 chunk = 0; chunk < chunksPerSegment; chunk++) |
| 264 | { |
| 265 | const float alpha = (chunk == chunksPerSegment - 1) ? 1.0f : ((float)chunk * chunksPerSegmentInv); |
| 266 | |
| 267 | // Evaluate transformation at the curve |
| 268 | AnimationUtils::Bezier(start.Value, leftTangent, rightTangent, end.Value, alpha, transform); |
| 269 | |
| 270 | // Apply spline direction (from position 1st derivative) |
| 271 | Vector3 direction; |
| 272 | AnimationUtils::BezierFirstDerivative(start.Value.Translation, leftTangent.Translation, rightTangent.Translation, end.Value.Translation, alpha, direction); |
| 273 | direction.Normalize(); |
| 274 | Quaternion orientation; |
| 275 | if (direction.IsZero()) |
| 276 | orientation = Quaternion::Identity; |
| 277 | else if (Vector3::Dot(direction, Vector3::Up) >= 0.999f) |
nothing calls this directly
no test coverage detected