Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
TransformCalculus.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreTypes.h"
6#include "Math/Vector2D.h"
7#include "Math/Vector.h"
8
9
10//////////////////////////////////////////////////////////////////////////
11// Transform calculus.
12//
13// A Transform represents a frame of reference in a local (often orthonormal) coordinate system.
14// Essentially a transform represents conversion from a local space A to another local space B.
15// Thus, it's commonly written as T[AB]. Keeping this notation of spaces explicit allows
16// transformation calculus to be very simple and easily checked using something akin to dimensional analysis:
17//
18// T[AB] * T[BC] => T[AC] (aka, Concatenate(T[AB], T[BC])
19// T[AB]^-1 => T[BA] (aka, Inverse(T[AB])
20//
21// Concatenate is illegal if the LHS output space is not equivalent to the RHS input space:
22//
23// T[AB] * T[BC] <--- OK
24// T[BA] * T[BC] <--- illegal; output space is A, input space is B.
25//
26// So, if you have a spatial hierarchy like so:
27//
28// A
29// / \
30// B C
31// / \ / \
32// D E F G
33// /
34// H
35//
36// You can easily construct the math to move from, say, Space D to Space H using notation
37// purely in terms of the forward transforms that define the hierarchy:
38//
39// T[DH] = T[DB] * T[BA] * T[CA]^-1 * T[GC]^-1 * T[HG]^-1
40//
41// From a code standpoint, this gives us a well-defined set of methods that can be called on anything that
42// can be interpreted as a transform and let it work uniformly. Matrices, Quats, Vectors, Scalars, and other custom classes
43// that are unambiguously defined as transformational spaces can be concatenated together with a well-defined meaning.
44//
45// ------------------------
46// Fundamental Operations
47// ------------------------
48// The fundamental components of the Transform library are a collection of non-member functions (and their overloads):
49// * Concatenate(TransformA, TransformB)
50// * Inverse(Transform)
51// * TransformPoint(Transform, Point)
52// * TransformVector(Transform, Vector)
53// * TransformCast<Result, Transform>
54//
55// These operations are NOT member functions to support existing classes in UE4 without modification,
56// and to more easily support extending the supported types with other existing classes.
57//
58// Concatenate
59// -----------
60// Generalized transform concatenation. It exists to ensure that any two
61// classes that can be thought of as transformational spaces (listed above, along with
62// any user-defined classes), can be combined uniformly with well defined semantics.
63// For example, given A * B in UE4 means two different things if A and B are matrices or quaternions.
64// Concatenate abstracts this notion into an explicit syntax for combining transforms
65// instead of focusing on the mathematical notation.
66//
67// The result of a concatenate operation depends on the types concatenated, but are guaranteed
68// to be another type that supports the fundamental operations. For instance,
69// concatenating two translations results in another translation, but concatenating a rotation
70// followed by a translation might result in a FMatrix, or potentially another class that efficiently
71// contains this transformation (like maybe a class that holds a scalar+quat+vector).
72//
73// Generally you should not have to worry about the return type. Just Concatenate and then use another
74// fundamental operation to transform a position and/or vector through that space. When you need to store the
75// result you can either use auto, assume the type, or use TransformCast (covered below) to ensure the type.
76//
77// In certain rare cases, the result of Concatenate may be ambiguous (FRotator or FQuat?). In such cases,
78// there is a Concatenate template overload that allows you to explicitly specify the ResultType.
79// This should rarely be necessary.
80//
81// Inverse
82// -------
83// Generalized transform inversion. Takes a transform from space A to space B and returns
84// a transform that goes from space B to space A. The output type is guaranteed to be the same.
85//
86// Transform[Point|Vector]
87// --------------------------
88// The primary reason to construct a transform from space A to B is to transform things from
89// space A to space B. TransformPoint does this for points in space, and TransformVector does
90// this for vectors (extents or normals) in space.
91//
92// There are 2D variants for efficiency. All functions assume a non-projective transform (ie,
93// they don't perform a homogeneous divide). The output type is guaranteed to be the same as the input.
94//
95// TransformCast<Result, Transform>
96// ----------------------------------------------
97// The job of the TransformCast is to provide efficient conversion from one transform type
98// to another, when possible. This is typically done to store the transform in a specific variable type after
99// a sequence of Concatenate and Inverse calls.
100//
101// Not all transforms can be cast. For instance, a scale cannot be cast to a translation. Usually
102// the output of a cast operation is a generalized transform type like FTransform, FMatrix, etc.
103//
104// TransformCast supports efficient identity pass-through (Type A -> Type A) so applying a cast in
105// generic code is safe and fast (no copying).
106//
107// ------------------------------------------------------------------------
108// Implementing a custom type.
109// ------------------------------------------------------------------------
110// While UE4 supports a wide variety of transforms, there is sometimes need to support another custom transform type.
111// The core code provides basic scaffolding and a set of recommended practices to make this as easy as
112// possible. This allows most of the functionality to be provided via member functions in the new custom type, while
113// still allowing existing types to be adapted via non-member functions if you choose to do it that way.
114// However, you will need to understand a few of the underlying templatized helpers that make this possible:
115// * TransformConverter<T>
116// * ConcatenateRules<T1, T2>
117//
118// TransformConverter<T>
119// ---------------------
120// TransformConverter<> implements the meat of TransformCast. It does it's work through the templatized static member
121// function Convert<T2>. By default this method uses a conversion ctor to construct a T from a T2. Therefore your
122// class can either provide such conversion ctors or specialize TransformConverter<T>::Convert<T2> for each transform
123// type you can cast to.
124//
125// This class is a template struct with a template member to effectively allow partial specialization of either
126// parameter, which function templates do no support. We need to call this as an explicit template call,
127// so non-template overloads are not an option. We also need this to support partial specialization for
128// the NULL conversion.
129//
130// ConcatenateRules<T1, T2>
131// ------------------------
132// In general, the result of a Concatenate call cannot be predicted by the template code. For instance, what
133// is the result of Concatenate(FQuat, FRotator)? What if there are more than one type that can contain the
134// transform (like FMatrix and FTransform)?
135// Concatenate() generally relies on overloads (template or non-template) to do the work. However, requiring
136// all combinations of all types to be overloaded would be quite tedious, and generally unnecessary. Therefore,
137// Concatenate supports a general template form that first converts both parameters to the appropriate return
138// type via TransformCast<>, then calls Concatentate on those:
139//
140// return Concatenate(TransformCast<Result>(LHS), TransformCast<Result>(RHS));
141//
142// This makes it convenient to automatically support flexible concatenation by leveraging the conversion
143// mechanics of TransformCast<>.
144//
145// But how does one determine the "appropriate" return type? This is done via the ConcatenateRules<T1, T2> template,
146// which simply defines the appropriate ResultType for Concatenate<T1, T2>:
147//
148// typedef XXX ResultType;
149//
150// The default implementation is empty, meaning there is no support available. There is a partial specialization
151// for the same types so the code can always assume that
152//
153// Concatenate<T,T> -> T.
154//
155// Remember that TransformCast<T,T> is a NOOP, so this works efficiently.
156//
157// It is up to the implementor of a custom class to define any additional ConcatenateRules for their new type.
158//
159// Also note that implementing every Concatenate by first upcasting each parameter to the result type may not be very
160// efficient. In those cases, providing direct, non-template overload of Concatenate may be better. See the example
161// below for details.
162//
163// ------------------------------------------------------------------------
164// Example Custom type
165// ------------------------------------------------------------------------
166// Say you wanted to create a type that only supports uniform scale followed by 3D translation.
167// Let's call this type TranformST for shorthand.
168//
169// The core code provides default template implementations that pass through to member functions of the custom
170// type. This allows most of the functionality to be centralized in the new custom type, while still allowing
171// for non-member overloads if you choose to do it that way.
172//
173//
174// The following class skeleton provides the basic signature of a class that supports the Transform Calculus:
175//
176// class FTransformST
177// {
178// public:
179// explicit FTransformST(float InScale); // 1. Used by: TransformConverter
180// explicit FTransformST(const FVector& InTranslation); // 1. Used by: TransformConverter
181// FMatrix ToMatrix() const; // 2. (OPTIONAL) Used by: TransformConverter<FMatrix>
182// FVector TransformPoint(const FVector& Point) const; // 3. Used by: TransformPoint
183// FVector TransformVector(const FVector& Vector) const; // 4. Used by: TransformVector
184// FTransformST Concatenate(const FTransformST& RHS) const; // 5. Used by: Concatenate
185// FTransformST Inverse() const; // 6. Used by: Inverse
186// };
187//
188// inline FTransformST Concatenate(float LHS, const FVector& RHS); // 7. (OPTIONAL) Used by: Concatenate
189// inline FTransformST Concatenate(const FVector& LHS, float RHS); // 7. (OPTIONAL) Used by: Concatenate
190//
191// template<> struct ConcatenateRules<FTransformST, float > { typedef FTransformST ResultType; }; // 8. (OPTIONAL) Used by: Concatenate
192// template<> struct ConcatenateRules<float , FTransformST> { typedef FTransformST ResultType; }; // 8. (OPTIONAL) Used by: Concatenate
193// template<> struct ConcatenateRules<FTransformST, FVector > { typedef FTransformST ResultType; }; // 8. (OPTIONAL) Used by: Concatenate
194// template<> struct ConcatenateRules<FVector , FTransformST> { typedef FTransformST ResultType; }; // 8. (OPTIONAL) Used by: Concatenate
195//
196// template<> struct ConcatenateRules<FTransformST, FMatrix > { typedef FMatrix ResultType; }; // 9. (OPTIONAL) Used by: Concatenate
197// template<> struct ConcatenateRules<FMatrix , FTransformST> { typedef FMatrix ResultType; }; // 9. (OPTIONAL) Used by: Concatenate
198//
199// 1. Provide conversion constructors (can be explicit) to convert a lower level transform into this higher level one.
200// In this case, we can convert any translation or scale to a FTransformST. This will be used by the Concatenate
201// rules below to upcast any lower level types so they can be concatenated together.
202//
203// 2. (OPTIONAL) Provide a ToMatrix function to allow this type to be concatenated with FMatrix automatically (which is a common
204// fundamental transform). There is a specialization of TransformConverter for FMatrix that looks for this member function as
205// a convenience to custom class providers.
206//
207// 3. Provide a TransformPoint method (and perhaps a 2D version) which will be used by the default template
208// implementation of TransformPoint. If you choose not to provide a member function, you can instead provide
209// a non-template overload of TransformPoint(FTransformTS, FVector).
210//
211// 4. Provide a TransformVector method (and perhaps a 2D version) which will be used by the default template
212// implementation of TransformPoint. If you choose not to provide a member function, you can instead provide
213// a non-template overload of TransformVector(FTransformTS, FVector).
214//
215// 5. Provide a Concatenate method which will be used by the default template
216// implementation of Concatenate. If you choose not to provide a member function, you can instead provide
217// a non-template overload of Concatenate(FTransformTS, FTransformTS).
218//
219// 6. Provide a Inverse method which will be used by the default template
220// implementation of Inverse. If you choose not to provide a member function, you can instead provide
221// a non-template overload of Inverse(FTransformTS).
222//
223// 7. Provide some specializations of Concatenate that more efficiently represent the transforms your class supports.
224// In this case, our class can represent an arbitrary combination of uniform scale and translation, so by providing
225// explicit overloads, these more efficient versions will be used instead of promoting both types to FTransformST first.
226//
227// 8. Since we don't provide explicit Concatenate combinations for all possibly types (we could), we provide
228// some ConcatenateRules<> to allow the default Concatenate implementation to work with scalars and transform vectors.
229//
230// 9. We also provide a set of ConcatenateRules for FMatrix. This ends up using the ToMatrix member function we provided in 2.
231//
232//////////////////////////////////////////////////////////////////////////
233
234
235/** Provides default logic (used by TransformCast) to convert one transform type to another via a conversion ctor. */
236template<class TransformType>
237struct TransformConverter
238{
239 /** Efficient NULL conversion. */
240 static const TransformType& Convert(const TransformType& Transform)
241 {
242 return Transform;
243 }
244 /**
245 * Default Conversion via a conversion ctor.
246 * Note we are not using perfect forwarding here. Our types don't generally support move operations, nor do they make sense.
247 * VS 2013 seems to have trouble resolving the specializations below in the presence of perfect forwarding semantics.
248 */
249 template<typename OtherTransformType>
250 static TransformType Convert(const OtherTransformType& Transform)
251 {
252 return TransformType(Transform);
253 }
254};
255
256/**
257 * Casts one TransformType to ResultType using rules laid out by TransformConverter<>::Convert<>().
258 *
259 * Return type uses decltype to support classes that can return by const-ref more efficiently than returning a new value.
260 */
261template<typename ResultType, typename TransformType>
262inline auto TransformCast(const TransformType& Transform) -> decltype(TransformConverter<ResultType>::Convert(Transform))
263{
264 return TransformConverter<ResultType>::Convert(Transform);
265}
266
267/**
268 * Provides default rules defining the result of concatenating two types. By default, nothing is supported
269 * because the code cannot know in general what two types result in after concatenation.
270 */
271template<typename TransformTypeA, typename TransformTypeB>
273{
274};
275
276/** Partial specialization for concatenating two of the same types. Always results in the same type being returned. */
277template<typename TransformType>
278struct ConcatenateRules<TransformType, TransformType>
279{
280 typedef TransformType ResultType;
281};
282
283/**
284 * Concatenates two transforms. Uses TransformCast<> to convert them first.
285 * If more efficient means are available to concatenate two transforms, provide a non-template overload (or possibly a specialization).
286 * Concatenation is performed in left to right order, so the output space of LHS must match the input space of RHS.
287 *
288 * @param LHS Transformation that goes from space A to space B
289 * @param RHS Transformation that goes from space B to space C.
290 * @return a new transform representing the transformation from the input space of LHS to the output space of RHS.
291 */
292template<typename TransformTypeA, typename TransformTypeB>
293inline typename ConcatenateRules<TransformTypeA, TransformTypeB>::ResultType Concatenate(const TransformTypeA& LHS, const TransformTypeB& RHS)
294{
295 typedef typename ConcatenateRules<TransformTypeA, TransformTypeB>::ResultType ReturnType;
296 // If you get a compiler error here about "no member function Concatenate found for TransformType" you know
297 // your transform type doesn't support a Concatenate method. Either add one or provide an overload of Concatenate that does this.
298 return Concatenate(TransformCast<ReturnType>(LHS), TransformCast<ReturnType>(RHS));
299}
300
301
302
303/** Special overload that allows one to explicitly define the result type, which applies TransformCast on each argument first. */
304template<typename ReturnType, typename LHSType, typename RHSType>
305inline ReturnType Concatenate(const LHSType& LHS, const RHSType& RHS)
306{
307 return Concatenate(TransformCast<ReturnType>(LHS), TransformCast<ReturnType>(RHS));
308}
309
310/**
311 * Specialization for concatenating two transforms of the same type.
312 * By default we try to use a member function on the type.
313 *
314 * @param LHS Transformation that goes from space A to space B
315 * @param RHS Transformation that goes from space B to space C.
316 * @return a new transform representing the transformation from the input space of LHS to the output space of RHS.
317 */
318template<typename TransformType>
319inline auto Concatenate(const TransformType& LHS, const TransformType& RHS) -> decltype(LHS.Concatenate(RHS))
320{
321 // If you get a compiler error here about "no member function Concatenate found for TransformType" you know
322 // your transform type doesn't support a Concatenate method. Either add one or provide an overload of Concatenate that does this.
323 return LHS.Concatenate(RHS);
324}
325
326/**
327 * Concatenates three transforms. Uses two-argument Concatenate to do its work,
328 * and infers the return type using decltype.
329 *
330 * @param TransformAToB Transformation that goes from space A to space B
331 * @param TransformBToC Transformation that goes from space B to space C.
332 * @param TransformCToD Transformation that goes from space C to space D.
333 * @return a new Transform representing the transformation from space A to space D.
334 */
335template<typename TransformType1, typename TransformType2, typename TransformType3>
336inline auto Concatenate(const TransformType1& TransformAToB, const TransformType2& TransformBToC, const TransformType3& TransformCToD) -> decltype(Concatenate(Concatenate(TransformAToB, TransformBToC), TransformCToD))
337{
338 return Concatenate(Concatenate(TransformAToB, TransformBToC), TransformCToD);
339}
340
341/**
342 * Concatenates four transforms. Uses two-argument Concatenate to do its work,
343 * and infers the return type using decltype.
344 *
345 * @param TransformAToB Transformation that goes from space A to space B
346 * @param TransformBToC Transformation that goes from space B to space C.
347 * @param TransformCToD Transformation that goes from space C to space D.
348 * @param TransformDToE Transformation that goes from space D to space E.
349 * @return a new Transform representing the transformation from space A to space E.
350 */
351template<typename TransformType1, typename TransformType2, typename TransformType3, typename TransformType4>
352inline auto Concatenate(const TransformType1& TransformAToB, const TransformType2& TransformBToC, const TransformType3& TransformCToD, const TransformType4& TransformDToE) -> decltype(Concatenate(Concatenate(TransformAToB, TransformBToC, TransformCToD), TransformDToE))
353{
354 return Concatenate(Concatenate(TransformAToB, TransformBToC, TransformCToD), TransformDToE);
355}
356
357/**
358 * Concatenates five transforms. Uses two-argument Concatenate to do its work,
359 * and infers the return type using decltype.
360 *
361 * @param TransformAToB Transformation that goes from space A to space B
362 * @param TransformBToC Transformation that goes from space B to space C.
363 * @param TransformCToD Transformation that goes from space C to space D.
364 * @param TransformDToE Transformation that goes from space D to space E.
365 * @param TransformEToF Transformation that goes from space E to space F.
366 * @return a new Transform representing the transformation from space A to space F.
367 */
368template<typename TransformType1, typename TransformType2, typename TransformType3, typename TransformType4, typename TransformType5>
369inline auto Concatenate(const TransformType1& TransformAToB, const TransformType2& TransformBToC, const TransformType3& TransformCToD, const TransformType4& TransformDToE, const TransformType5& TransformEToF) -> decltype(Concatenate(Concatenate(TransformAToB, TransformBToC, TransformCToD, TransformDToE), TransformEToF))
370{
371 return Concatenate(Concatenate(TransformAToB, TransformBToC, TransformCToD, TransformDToE), TransformEToF);
372}
373
374/**
375 * Inverts a transform from space A to space B so it transforms from space B to space A.
376 * By default attempts to call a member function on the transform type.
377 *
378 * @param Transform Input transform from space A to space B.
379 * @return Inverted transform from space B to space A.
380 */
381template<typename TransformType>
382inline auto Inverse(const TransformType& Transform) -> decltype(Transform.Inverse())
383{
384 return Transform.Inverse();
385}
386
387/**
388 * Generic implementation of TransformPoint. Attempts to use a member function of the TransformType.
389 */
390template <typename TransformType, typename PositionType>
391inline PositionType TransformPoint(const TransformType& Transform, const PositionType& Point)
392{
393 return Transform.TransformPoint(Point);
394}
395
396/**
397 * Generic implementation of TransformVector. Attempts to use a member function of the TransformType.
398 */
399template <typename TransformType, typename VectorType>
400inline VectorType TransformVector(const TransformType& Transform, const VectorType& Vector)
401{
402 return Transform.TransformVector(Vector);
403}
404
405//////////////////////////////////////////////////////////////////////////
406// Overloads for uniform Scale.
407//
408// This isn't really 2D or 3D specific, but
409// both 2D and 3D leverage uniform scale, and expect these overloads to be available,
410// so we go ahead and define them here.
411//////////////////////////////////////////////////////////////////////////
412
413/**
414 * Specialization for concatenating two scales.
415 *
416 * @param LHS Scale that goes from space A to space B
417 * @param RHS Scale that goes from space B to space C.
418 * @return a new Scale representing the transformation from the input space of LHS to the output space of RHS.
419 */
420template<typename FloatType, TEMPLATE_REQUIRES(TIsFloatingPoint<FloatType>::Value)>
421inline FloatType Concatenate(FloatType LHS, FloatType RHS)
422{
423 return LHS * RHS;
424}
425
426/**
427 * Inverts a transform from space A to space B so it transforms from space B to space A.
428 * Specialization for uniform scale.
429 *
430 * @param Transform Input transform from space A to space B.
431 * @return Inverted transform from space B to space A.
432 */
433template<typename FloatType, TEMPLATE_REQUIRES(TIsFloatingPoint<FloatType>::Value)>
434inline FloatType Inverse(FloatType Scale)
435{
436 return 1.0f / Scale;
437}
438
439/**
440 * Specialization for uniform Scale.
441 */
442template<typename PositionType>
443inline UE::Math::TVector<PositionType> TransformPoint(float Transform, const UE::Math::TVector<PositionType>& Point)
444{
445 return Transform * Point;
446}
447
448template<typename PositionType>
449inline UE::Math::TVector<PositionType> TransformPoint(double Transform, const UE::Math::TVector<PositionType>& Point)
450{
451 return Transform * Point;
452}
453
454/**
455 * Specialization for uniform Scale.
456 */
457template<typename VectorType>
458inline UE::Math::TVector<VectorType> TransformVector(float Transform, const UE::Math::TVector<VectorType>& Vector)
459{
460 return Transform * Vector;
461}
462
463template<typename VectorType>
464inline UE::Math::TVector<VectorType> TransformVector(double Transform, const UE::Math::TVector<VectorType>& Vector)
465{
466 return Transform * Vector;
467}
UE::Math::TVector< VectorType > TransformVector(float Transform, const UE::Math::TVector< VectorType > &Vector)
FloatType Concatenate(FloatType LHS, FloatType RHS)
auto TransformCast(const TransformType &Transform) -> decltype(TransformConverter< ResultType >::Convert(Transform))
auto Inverse(const TransformType &Transform) -> decltype(Transform.Inverse())
auto Concatenate(const TransformType1 &TransformAToB, const TransformType2 &TransformBToC, const TransformType3 &TransformCToD, const TransformType4 &TransformDToE) -> decltype(Concatenate(Concatenate(TransformAToB, TransformBToC, TransformCToD), TransformDToE))
auto Concatenate(const TransformType &LHS, const TransformType &RHS) -> decltype(LHS.Concatenate(RHS))
auto Concatenate(const TransformType1 &TransformAToB, const TransformType2 &TransformBToC, const TransformType3 &TransformCToD) -> decltype(Concatenate(Concatenate(TransformAToB, TransformBToC), TransformCToD))
UE::Math::TVector< PositionType > TransformPoint(float Transform, const UE::Math::TVector< PositionType > &Point)
VectorType TransformVector(const TransformType &Transform, const VectorType &Vector)
PositionType TransformPoint(const TransformType &Transform, const PositionType &Point)
UE::Math::TVector< PositionType > TransformPoint(double Transform, const UE::Math::TVector< PositionType > &Point)
FloatType Inverse(FloatType Scale)
auto Concatenate(const TransformType1 &TransformAToB, const TransformType2 &TransformBToC, const TransformType3 &TransformCToD, const TransformType4 &TransformDToE, const TransformType5 &TransformEToF) -> decltype(Concatenate(Concatenate(TransformAToB, TransformBToC, TransformCToD, TransformDToE), TransformEToF))
ConcatenateRules< TransformTypeA, TransformTypeB >::ResultType Concatenate(const TransformTypeA &LHS, const TransformTypeB &RHS)
ReturnType Concatenate(const LHSType &LHS, const RHSType &RHS)
UE::Math::TVector< VectorType > TransformVector(double Transform, const UE::Math::TVector< VectorType > &Vector)
#define TEMPLATE_REQUIRES(...)
Definition Vector.h:40