UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
MultiSpline.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "SplineInterfaces.h"
7#include "BoxTypes.h"
9#include "Spline.h"
10
11namespace UE
12{
13namespace Geometry
14{
15namespace Spline
16{
17
18template <typename SPLINETYPE> class UE_EXPERIMENTAL(5.7, "") TMultiSpline;
19
24template <typename SPLINETYPE>
26 public TSplineWrapper<SPLINETYPE>,
27 private TSelfRegisteringSpline<TMultiSpline<SPLINETYPE>, typename SPLINETYPE::ValueType>
28{
29public:
31 {
33 Parent
34 };
35
36private:
37 /* Internal structure for child splines */
38 struct FChildSpline
39 {
40 // Default constructor
41 FChildSpline() = default;
42
43 // Copy constructor - creates a deep copy of the spline
44 FChildSpline(const FChildSpline& Other)
45 : MappingRangeSpace(Other.MappingRangeSpace)
46 , MappingRange(Other.MappingRange)
47 {
48 // Clone the spline if it exists
49 if (Other.Spline)
50 {
51 Spline = Other.Spline->Clone();
52 }
53 }
54
55 // Move constructor
56 FChildSpline(FChildSpline&& Other) = default;
57
58 // Copy assignment - creates a deep copy
59 FChildSpline& operator=(const FChildSpline& Other)
60 {
61 if (this != &Other)
62 {
63 MappingRangeSpace = Other.MappingRangeSpace;
64 MappingRange = Other.MappingRange;
65
66 // Clone the spline if it exists
67 if (Other.Spline)
68 {
69 Spline = Other.Spline->Clone();
70 }
71 else
72 {
73 Spline.Reset();
74 }
75 }
76 return *this;
77 }
78
79 // Move assignment
80 FChildSpline& operator=(FChildSpline&& Other) = default;
81
82 friend FArchive& operator<<( FArchive& Ar, FChildSpline& Child )
83 {
84 Child.Serialize(Ar);
85 return Ar;
86 }
87
88 bool Serialize(FArchive& Ar)
89 {
90 Ar << MappingRangeSpace;
91 Ar << MappingRange.Min;
92 Ar << MappingRange.Max;
93
94 // We must create the proper spline before we can serialize it.
95 if (Ar.IsLoading())
96 {
98
99 // We are about to read data from the archive that the spline itself will attempt to read,
100 // so we have to restore the archive position after reading it.
101 const int64 Pos = Ar.Tell();
102 {
104 Ar << Version;
105 Ar << TypeId;
106 }
107 Ar.Seek(Pos);
108
110
111 ensureAlways(Spline); // if this failed, most likely the spline type has not been registered.
112 }
113
114 Ar << *Spline;
115
116 return true;
117 }
118
119 float MapParameterToChildSpace(float ParentParameter, const FInterval1f& ParentSpace) const
120 {
121 if (!Spline)
122 {
123 return 0.f;
124 }
125
126 if (FInterval1f ChildSpace = Spline->GetParameterSpace(); !ChildSpace.IsEmpty())
127 {
128 FInterval1f MappedParentRange = GetParentSpaceRange(ParentSpace);
129
130 float T = MappedParentRange.GetUnclampedT(ParentParameter);
131 return ChildSpace.Interpolate(T);
132 }
133 else
134 {
135 return 0.f;
136 }
137 }
138
139 float MapParameterFromChildSpace(float ChildParameter, const FInterval1f& ParentSpace) const
140 {
141 if (!Spline)
142 {
143 return 0.f;
144 }
145
146 if (FInterval1f ChildSpace = Spline->GetParameterSpace(); !ChildSpace.IsEmpty())
147 {
148 FInterval1f MappedParentRange = GetParentSpaceRange(ParentSpace);
149
150 float T = ChildSpace.GetUnclampedT(ChildParameter);
151 return MappedParentRange.Interpolate(T);
152 }
153 else
154 {
155 return 0.f;
156 }
157 }
158
159 FInterval1f GetParentSpaceRange(const FInterval1f& ParentSpace) const
160 {
161 switch(MappingRangeSpace)
162 {
164 return FInterval1f(ParentSpace.Interpolate(MappingRange.Min), ParentSpace.Interpolate(MappingRange.Max));
165
167 return MappingRange;
168
169 default:
170 return FInterval1f::Empty();
171 }
172 }
173
174 EMappingRangeSpace GetMappingRangeSpace() const
175 {
176 return MappingRangeSpace;
177 }
178
179 void SetMappingRangeSpace(EMappingRangeSpace InMappingRangeSpace, const FInterval1f& ParentSpace)
180 {
181 if (MappingRangeSpace == InMappingRangeSpace)
182 {
183 return;
184 }
185
186 switch(InMappingRangeSpace)
187 {
189 MappingRange = FInterval1f(ParentSpace.GetT(MappingRange.Min), ParentSpace.GetT(MappingRange.Max));
190 break;
191
193 MappingRange = FInterval1f(ParentSpace.Interpolate(MappingRange.Min), ParentSpace.Interpolate(MappingRange.Max));
194 break;
195
196 default:
197 break;
198 }
199
200 MappingRangeSpace = InMappingRangeSpace;
201 }
202
204 FInterval1f MappingRange = FInterval1f(0.f, 1.f);
206 };
207
208public:
209
212
220
221 virtual FSplineTypeId::IdType GetTypeId() const override
222 {
223 return GetStaticTypeId();
224 }
225
226 static FString GetSplineTypeName()
227 {
228 return FString::Printf(TEXT("MultiSpline.%s"), *SplineType::GetSplineTypeName());
229 }
230
231 virtual FString GetImplementationName() const override
232 {
233 return GetSplineTypeName();
234 }
235
238
239 virtual bool operator==(const TMultiSpline<SplineType>& Other) const
240 {
241 if (GetSpline() != Other.GetSpline())
242 {
243 return false;
244 }
245
247 TArray<FName> OtherAttributeNames = Other.GetAllAttributeChannelNames();
248
249 if (AttributeNames.Num() != OtherAttributeNames.Num())
250 {
251 return false;
252 }
253
254 for (FName& Name : AttributeNames)
255 {
256 if (!OtherAttributeNames.Contains(Name))
257 {
258 return false;
259 }
260
261 const ISplineInterface* Child = Children[Name].Spline.Get();
262 const ISplineInterface* OtherChild = Other.Children[Name].Spline.Get();
263
264 if (!Child || !OtherChild || !Child->IsEqual(OtherChild))
265 {
266 return false;
267 }
268 }
269
270 return true;
271 }
272
273 virtual void Clear() override
274 {
276
277 for (const FName& Name : GetAllAttributeChannelNames())
278 {
280 }
281 }
282
283 virtual bool IsEqual(const ISplineInterface* OtherSpline) const override
284 {
285 if (OtherSpline->GetTypeId() == GetTypeId())
286 {
287 const TMultiSpline* Other = static_cast<const TMultiSpline*>(OtherSpline);
288 return operator==(*Other);
289 }
290
291 return false;
292 }
293
294 virtual bool Serialize(FArchive& Ar) override
295 {
297 {
298 return false;
299 }
300
301 Ar << Children;
302
303 return true;
304 }
305
313 template <typename ImplType>
314 ImplType* GetTypedAttributeChannel(FName Name) const
315 {
316 if (const FChildSpline* ExistingChannel = Children.Find(Name))
317 {
318 if (ExistingChannel->Spline.IsValid())
319 {
320 // Get implementation names for comparison
321 const FString RequestedImplName = ImplType::GetSplineTypeName();
322 const FString ExistingImplName = ExistingChannel->Spline->GetImplementationName();
323
324 // Check implementation type compatibility
326 {
327 // Safe to cast if implementation types match
328 return static_cast<ImplType*>(ExistingChannel->Spline.Get());
329 }
330
331 // Log warning about implementation type mismatch
332 UE_LOG(LogSpline, Warning, TEXT("Failed to fetch attribute channel '%s': Channel exists with implementation type '%s', but requested type is '%s'."),
333 *Name.ToString(), *ExistingImplName, *RequestedImplName);
334 }
335 }
336 return nullptr;
337 }
338
344 template <typename AttrType>
346 {
347 if (const FChildSpline* ExistingChannel = Children.Find(Name))
348 {
349 if (ExistingChannel->Spline.IsValid())
350 {
351 const FString TypeName = TSplineValueTypeTraits<AttrType>::Name;
352 const FString ExistingTypeName = ExistingChannel->Spline->GetValueTypeName();
353
354 if (TypeName == ExistingTypeName)
355 {
356 return static_cast<TSplineInterface<AttrType>*>(ExistingChannel->Spline.Get());
357 }
358
359 // Channel exists but with a different type
360 UE_LOG(LogSpline, Warning, TEXT("Failed to fetch attribute channel '%s': Channel already exists with type '%s', but requested type is '%s'."),
361 *Name.ToString(), *ExistingTypeName, *TypeName);
362 }
363 }
364
365 return nullptr;
366 }
367
373 {
374 TArray<FName> ChannelNames;
375 Children.GetKeys(ChannelNames);
376 return ChannelNames;
377 }
378
385 template <typename AttrType>
387 {
388 TArray<FName> ChannelNames;
390
391 for (auto& Pair : Children)
392 {
393 if (Pair.Value.Spline.IsValid() &&
394 Pair.Value.Spline->GetValueTypeName() == TypeName)
395 {
396 ChannelNames.Add(Pair.Key);
397 }
398 }
399
400 return ChannelNames;
401 }
402
410 {
411 return Children.Remove(Name) > 0;
412 }
413
422 template <typename AttrType>
423 bool CloneAttributeChannel(const FName& SourceName, const FName& DestName)
424 {
425 // Check if destination already exists
426 if (Children.Contains(DestName))
427 {
428 UE_LOG(LogSpline, Warning, TEXT("Cannot clone channel: Destination channel '%s' already exists."),
429 *DestName.ToString());
430 return false;
431 }
432
433 // Get source channel
435 if (!SourceChannel)
436 {
437 UE_LOG(LogSpline, Warning, TEXT("Cannot clone channel: Source channel '%s' not found or type mismatch."),
438 *SourceName.ToString());
439 return false;
440 }
441
442 // Get implementation information from source
443 const FString ImplName = SourceChannel->GetImplementationName();
444
445 // Create new destination channel with same implementation
447 {
448 UE_LOG(LogSpline, Error, TEXT("Failed to create destination channel '%s'."),
449 *DestName.ToString());
450 return false;
451 }
452
453 // Clone the source channel's data to the destination channel
455 if (!DestChannel)
456 {
457 // This should not happen, but just in case
458 UE_LOG(LogSpline, Error, TEXT("Internal error: Unable to retrieve just-created channel '%s'."),
459 *DestName.ToString());
460 return false;
461 }
462
463 // Clone the source to the destination
464 // We need to use the ISplineInterface::Clone() approach and move the clone into our structure
466
467 // Find and update the destination child's spline pointer
468 FChildSpline& DestChild = Children[DestName];
470
471 // Copy mapping settings from source
472 const FChildSpline& SourceChild = *Children.Find(SourceName);
473 DestChild.MappingRange = SourceChild.MappingRange;
474 DestChild.MappingRangeSpace = SourceChild.MappingRangeSpace;
475
476 return true;
477 }
478
485 float MapParameterToChildSpace(FName Name, float Parameter) const
486 {
487 if (const FChildSpline* Child = Children.Find(Name))
488 {
489 return Child->MapParameterToChildSpace(Parameter, GetSpline().GetParameterSpace());
490 }
491
492 return 0.f;
493 }
494
501 float MapParameterFromChildSpace(FName Name, float Parameter) const
502 {
503 if (const FChildSpline* Child = Children.Find(Name))
504 {
505 return Child->MapParameterFromChildSpace(Parameter, GetSpline().GetParameterSpace());
506 }
507
508 return 0.f;
509 }
510
517 {
518 if (const FChildSpline* Child = Children.Find(Name))
519 {
520 return Child->GetParentSpaceRange(GetSpline().GetParameterSpace());
521 }
522
523 return FInterval1f::Empty();
524 }
525
533 template <typename ImplType>
534 ImplType* CreateAttributeChannel(const FName& Name)
535 {
536 // Extract the value type from the implementation type
537 using AttrType = typename ImplType::ValueType;
538
539 // Check if the type is already registered
540 uint32 TypeId = FSplineTypeRegistry::GetTypeId(ImplType::GetSplineTypeName(),
542 // If not registered, register it manually
543 if (TypeId == 0)
544 {
545 // Get type information for registration
546 TypeId = ImplType::GetStaticTypeId();
547 FString ImplName = ImplType::GetSplineTypeName();
548 FString ValueName = TSplineValueTypeTraits<AttrType>::Name;
549
550 // Register with a factory function that creates this specific type
552 TypeId,
553 ImplName,
554 ValueName,
555 []() { return MakeUnique<ImplType>(); }
556 );
557
558 if (!bSuccess)
559 {
560 UE_LOG(LogSpline, Error, TEXT("Failed to register spline type '%s' with value type '%s'"),
561 *ImplName, *ValueName);
562 return nullptr;
563 }
564 }
565
566 // Create the channel using the existing name-based method
567 CreateAttributeChannelInternal<AttrType>(Name, ImplType::GetSplineTypeName());
568
569 // Return the typed implementation
571 }
572
573 template <typename AttrSplineType>
575 {
577
578 for (const TPair<FName, FChildSpline>& Pair : Children)
579 {
580 if (Pair.Value.Spline->GetTypeId() == AttrSplineType::GetStaticTypeId())
581 {
582 Names.Add(Pair.Key);
583 }
584 }
585
586 return Names;
587 }
588
589 template <typename AttrType>
591 {
593
594 for (const TPair<FName, FChildSpline>& Pair : Children)
595 {
596 if (Pair.Value.Spline->GetValueTypeName() == TSplineValueTypeTraits<AttrType>::Name)
597 {
598 Names.Add(Pair.Key);
599 }
600 }
601
602 return Names;
603 }
604
610 bool ClearAttributeChannel(const FName& Name)
611 {
612 if (const FChildSpline* Channel = Children.Find(Name))
613 {
614 Channel->Spline->Clear();
615 return true;
616 }
617 return false;
618 }
619
625 bool HasAttributeChannel(const FName& Name) const
626 {
627 return Children.Contains(Name);
628 }
629
636 template <typename AttrType>
637 AttrType EvaluateAttribute(const FName& Name, float Parameter) const
638 {
640 if (!Channel)
641 {
642 return AttrType();
643 }
644
645 // Map parameter to child space
646 float ChildParameter = MapParameterToChildSpace(Name, Parameter);
647
648 // Evaluate in child space
649 return Channel->Evaluate(ChildParameter);
650 }
651
660 {
661 FChildSpline* Child = Children.Find(Name);
662 if (!Child)
663 {
664 return false;
665 }
666
667 Child->MappingRange = Range;
668 Child->MappingRangeSpace = RangeSpace;
669 return true;
670 }
671
672private:
673
680 template <typename AttrType>
681 bool CreateAttributeChannelInternal(const FName& Name, const FString& ImplName)
682 {
683 // Check if channel already exists
684 if (Children.Contains(Name))
685 {
686 UE_LOG(LogSpline, Warning, TEXT("Failed to create attribute channel '%s': Channel already exists."), *Name.ToString());
687 return false;
688 }
689
690 // Look up the type ID
691 uint32 TypeId = FSplineTypeRegistry::GetTypeId(ImplName, TSplineValueTypeTraits<AttrType>::Name);
692 if (TypeId == 0)
693 {
694 UE_LOG(LogSpline, Error, TEXT("Failed to find type ID for implementation '%s' with value type '%s'."),
695 *ImplName, *TSplineValueTypeTraits<AttrType>::Name);
696 return false;
697 }
698
699 // Create child spline by type ID
701 if (!NewSpline)
702 {
703 UE_LOG(LogSpline, Error, TEXT("Failed to create spline of type '%s'/'%s' (ID: 0x%08X) for attribute channel '%s'."),
704 *ImplName, *TSplineValueTypeTraits<AttrType>::Name, TypeId, *Name.ToString());
705 return false;
706 }
707
708 // Verify that the created spline is of the expected value type
709 if (NewSpline->GetValueTypeName() != TSplineValueTypeTraits<AttrType>::Name)
710 {
711 UE_LOG(LogSpline, Error, TEXT("Created spline has incorrect value type. Expected '%s', got '%s'."),
712 *TSplineValueTypeTraits<AttrType>::Name, *NewSpline->GetValueTypeName());
713 return false;
714 }
715
716 // Add to children map
717 FChildSpline& Child = Children.Add(Name);
718 Child.Spline = MoveTemp(NewSpline);
719 Child.MappingRangeSpace = EMappingRangeSpace::Normalized;
720 Child.MappingRange = FInterval1f(0.0f, 1.0f);
721
722 return true;
723 }
724
726};
727
728} // end namespace UE::Geometry::Spline
729} // end namespace UE::Geometry
730} // end namespace UE
#define ensureAlways( InExpression)
Definition AssertionMacros.h:466
bool bSuccess
Definition ConvexDecomposition3.cpp:819
#define UE_EXPERIMENTAL(Version, Message)
Definition CoreMiscDefines.h:369
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::int64 int64
A 64-bit signed integer.
Definition Platform.h:1127
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
FArchive & operator<<(FArchive &Ar, FEnvQueryDebugProfileData::FStep &Data)
Definition EnvQueryTypes.cpp:489
#define UE_LOG(CategoryName, Verbosity, Format,...)
Definition LogMacros.h:270
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
uint8_t uint8
Definition binka_ue_file_header.h:8
uint32_t uint32
Definition binka_ue_file_header.h:6
Definition Archive.h:1208
virtual void Serialize(void *V, int64 Length)
Definition Archive.h:1689
UE_FORCEINLINE_HINT bool IsLoading() const
Definition Archive.h:236
virtual int64 Tell()
Definition Archive.h:149
virtual void Seek(int64 InPos)
Definition Archive.h:1753
Definition NameTypes.h:617
CORE_API FString ToString() const
Definition UnrealNames.cpp:3537
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
Definition UnrealString.h.inl:34
Definition UniquePtr.h:107
uint32 IdType
Definition SplineTypeId.h:33
static constexpr IdType GenerateTypeId(const TCHAR *ImplName, const TCHAR *ValueTypeName)
Definition SplineTypeId.h:43
static TypeId GetTypeId(const FString &ImplName, const FString &ValueTypeName)
Definition SplineTypeRegistry.h:173
static bool RegisterType(TypeId TypeId, const FString &ImplName, const FString &ValueTypeName, FactoryFunction Factory)
Definition SplineTypeRegistry.h:42
static TUniquePtr< ISplineInterface > CreateSpline(TypeId TypeId)
Definition SplineTypeRegistry.h:90
Definition SplineInterfaces.h:35
Definition MultiSpline.h:28
virtual void Clear() override
Definition MultiSpline.h:273
TArray< FName > GetAttributeChannelNamesByValueType() const
Definition MultiSpline.h:590
virtual bool IsEqual(const ISplineInterface *OtherSpline) const override
Definition MultiSpline.h:283
static const FSplineTypeId::IdType & GetStaticTypeId()
Definition MultiSpline.h:213
bool SetAttributeChannelRange(FName Name, const FInterval1f &Range, EMappingRangeSpace RangeSpace)
Definition MultiSpline.h:659
SplineType & GetSpline()
Definition MultiSpline.h:236
TArray< FName > GetAttributeChannelNamesOfType() const
Definition MultiSpline.h:386
typename TSplineWrapper< SPLINETYPE >::SplineType SplineType
Definition MultiSpline.h:210
bool ClearAttributeChannel(const FName &Name)
Definition MultiSpline.h:610
float MapParameterFromChildSpace(FName Name, float Parameter) const
Definition MultiSpline.h:501
ImplType * CreateAttributeChannel(const FName &Name)
Definition MultiSpline.h:534
bool CloneAttributeChannel(const FName &SourceName, const FName &DestName)
Definition MultiSpline.h:423
bool RemoveAttributeChannel(const FName &Name)
Definition MultiSpline.h:409
virtual FString GetImplementationName() const override
Definition MultiSpline.h:231
virtual bool Serialize(FArchive &Ar) override
Definition MultiSpline.h:294
TArray< FName > GetAttributeChannelNamesBySplineType() const
Definition MultiSpline.h:574
float MapParameterToChildSpace(FName Name, float Parameter) const
Definition MultiSpline.h:485
virtual bool operator==(const TMultiSpline< SplineType > &Other) const
Definition MultiSpline.h:239
virtual FSplineTypeId::IdType GetTypeId() const override
Definition MultiSpline.h:221
bool HasAttributeChannel(const FName &Name) const
Definition MultiSpline.h:625
FInterval1f GetMappedChildSpace(FName Name) const
Definition MultiSpline.h:516
ImplType * GetTypedAttributeChannel(FName Name) const
Definition MultiSpline.h:314
AttrType EvaluateAttribute(const FName &Name, float Parameter) const
Definition MultiSpline.h:637
static FString GetSplineTypeName()
Definition MultiSpline.h:226
TSplineWrapper< SPLINETYPE >::ValueType ValueType
Definition MultiSpline.h:211
const SplineType & GetSpline() const
Definition MultiSpline.h:237
TSplineInterface< AttrType > * GetAttributeChannel(FName Name) const
Definition MultiSpline.h:345
TArray< FName > GetAllAttributeChannelNames() const
Definition MultiSpline.h:372
EMappingRangeSpace
Definition MultiSpline.h:31
Definition SplineTypeRegistry.h:236
Definition SplineInterfaces.h:92
ValueType Evaluate(float Parameter) const
Definition SplineInterfaces.h:117
virtual void Clear() override
Definition Spline.h:32
SPLINETYPE SplineType
Definition Spline.h:26
virtual FInterval1f GetParameterSpace() const override
Definition Spline.h:48
SplineType::ValueType ValueType
Definition Spline.h:27
TInterval1< float > FInterval1f
Definition BoxTypes.h:241
Version
Definition NNEModelData.cpp:15
Definition AdvancedWidgetsModule.cpp:13
Definition Tuple.h:652
static TInterval1< float > Empty()
Definition BoxTypes.h:34