-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathaether.h
27697 lines (25112 loc) · 838 KB
/
aether.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//------------------------------------------------------------------------------
//! aether.h
//------------------------------------------------------------------------------
// Copyright (c) 2025 John Hughes
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files( the "Software" ), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and /or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//------------------------------------------------------------------------------
// Usage:
// Use this module by defining AE_MAIN once in your project above an
// included 'aether.h'. It's required that AE_MAIN is defined in an Objective-C
// '.mm' file on Apple platforms.
//
// Linking:
// Optionally you can define AE_USE_MODULES so linking system dependencies such
// as OpenGL will be handled for you.
//
// Recommendations:
// For bigger projects it's worth defining AE_MAIN in it's own module to limit
// the number of dependencies brought into your own code. For instance
// 'Windows.h' is included with AE_MAIN and this can easily cause naming
// conflicts with gameplay/engine code. The following example could be compiled
// into a single file/module and linked with the application.
// Usage inside of a cpp/mm file could be something like:
#if 0 // ae.cpp/ae.mm start
#define AE_MAIN
#define AE_USE_MODULES // C++ Modules (optional)
#include "aether.h"
#endif // ae.cpp/ae.mm end
//------------------------------------------------------------------------------
// AE_AETHER_H
//------------------------------------------------------------------------------
#ifndef AE_AETHER_H
#define AE_AETHER_H
//------------------------------------------------------------------------------
// Debug build define
//------------------------------------------------------------------------------
#if defined(_DEBUG) || defined(DEBUG) || ( _AE_APPLE_ && !defined(NDEBUG) ) || (defined(__GNUC__) && !defined(__OPTIMIZE__))
#define _AE_DEBUG_ 1
#else
#define _AE_DEBUG_ 0
#endif
//------------------------------------------------------------------------------
// AE_CONFIG_FILE define
//------------------------------------------------------------------------------
//! The path to a user defined configuration header file, something like
//! AE_CONFIG_FILE="aeConfig.h". Should contain defines such as
//! AE_VEC3_CLASS_CONFIG, AE_MAX_META_TYPES_CONFIG, etc (any AE_*_CONFIG's
//! below), which can be used to define custom conversion functions, or override
//! default limits. These defines MUST be consistent for all translation units
//! in a project. It's advised that an AE_CONFIG_FILE is provided globally as a
//! compiler definition to limit the opportunity for any configuration issues.
//! If AE_CONFIG_FILE is not defined, the default configuration will be used.
#ifdef AE_CONFIG_FILE
#include AE_CONFIG_FILE
#endif
//------------------------------------------------------------------------------
// AE_MEMORY_CHECKS define
//------------------------------------------------------------------------------
//! If you define AE_MEMORY_CHECKS=1, all allocations through aether will be
//! tracked and checked for leaks, double-frees, etc. If you use
//! ae::SetGlobalAllocator() the usefulness of AE_MEMORY_CHECKS will be very
//! limited, so it might be worth temporarily disabling your custom allocator to
//! get the full benefits of AE_MEMORY_CHECKS. AE_MEMORY_CHECKS is a heavy
//! diagnostic tool and may have a large performance impact. AE_MEMORY_CHECKS
//! must be defined for all files that include aether.h (using AE_CONFIG_FILE is
//! one way to do this).
//------------------------------------------------------------------------------
#ifndef AE_MEMORY_CHECKS
#define AE_MEMORY_CHECKS 0
#endif
//------------------------------------------------------------------------------
// AE_ENABLE_SOURCE_INFO define
//------------------------------------------------------------------------------
//! Includes additional info about source files and line numbers in logs and
//! asserts. This could be useful to enable in dev builds to get extra
//! information about logged events. AE_ENABLE_SOURCE_INFO must be defined for
//! all files that include aether.h (using AE_CONFIG_FILE is one way to do
//! this).
#ifndef AE_ENABLE_SOURCE_INFO
#define AE_ENABLE_SOURCE_INFO _AE_DEBUG_
#endif
//------------------------------------------------------------------------------
// Platform defines
//------------------------------------------------------------------------------
#define _AE_IOS_ 0
#define _AE_OSX_ 0
#define _AE_APPLE_ 0
#define _AE_WINDOWS_ 0
#define _AE_LINUX_ 0
#define _AE_EMSCRIPTEN_ 0
#if defined(__EMSCRIPTEN__)
#undef _AE_EMSCRIPTEN_
#define _AE_EMSCRIPTEN_ 1
#elif defined(__APPLE__)
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE
#undef _AE_IOS_
#define _AE_IOS_ 1
#elif TARGET_OS_MAC
#undef _AE_OSX_
#define _AE_OSX_ 1
#else
#error "Platform not supported"
#endif
#undef _AE_APPLE_
#define _AE_APPLE_ 1
#elif defined(_MSC_VER)
#undef _AE_WINDOWS_
#define _AE_WINDOWS_ 1
#elif defined(__linux__)
#undef _AE_LINUX_
#define _AE_LINUX_ 1
#else
#error "Platform not supported"
#endif
//------------------------------------------------------------------------------
// Warnings
//------------------------------------------------------------------------------
#if _AE_WINDOWS_
#define AE_POP_WARNINGS
#pragma warning( push )
#pragma warning( disable : 4018 ) // signed/unsigned mismatch
#pragma warning( disable : 4244 ) // conversion from 'float' to 'int32_t'
#pragma warning( disable : 4267 ) // conversion from 'size_t' to 'uint32_t'
#pragma warning( disable : 4800 )
#elif _AE_APPLE_
#define AE_POP_WARNINGS
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
//------------------------------------------------------------------------------
// Macro helpers
//------------------------------------------------------------------------------
//! Returns the number of arguments passed to this macro
#define AE_NARGS(...) AE_EVAL(AE_NARGS_I(__VA_ARGS__,9,8,7,6,5,4,3,2,1,))
//! Combines each argument into a single token
#define AE_GLUE(...) AE_GLUE_I(AE_GLUE_,AE_NARGS(__VA_ARGS__))(__VA_ARGS__)
//! Combines each argument into a single token, but arguments are separated by
//! '::' (double colons).
#define AE_GLUE_TYPE(...) AE_GLUE(AE_GLUE_TYPE_,AE_NARGS(__VA_ARGS__))(__VA_ARGS__)
//! Converts the given argument to a string. Useful for converting the result of
//! another macro invocation into a string.
#define AE_STRINGIFY(S) AE_STRINGIFY_I(S)
//! Returns the Nth element of __VA_ARGS__
#define AE_GET_ELEM(N, ...) AE_GLUE(AE_GET_ELEM_, N)(__VA_ARGS__)
//! Returns the last argument passed to this macro
#define AE_GET_LAST(...) AE_GET_ELEM(AE_NARGS(__VA_ARGS__), _, __VA_ARGS__ ,,,,,,,,,,,) // Get last argument - placeholder decrements by one
//------------------------------------------------------------------------------
// System Headers
//------------------------------------------------------------------------------
#include <algorithm>
#include <array> // @TODO: Remove. For _GetTypeName().
#include <cassert>
#include <chrono>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <functional>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <sstream>
#include <thread> // @TODO: Remove. For Globals::allocatorThread.
#include <type_traits>
#include <typeinfo>
#if AE_MEMORY_CHECKS
#include <unordered_map>
#endif
#include <utility>
#include <vector> // @TODO: Remove. For _RegisterEnum.
//------------------------------------------------------------------------------
// Platform headers
//------------------------------------------------------------------------------
#if _AE_APPLE_
#ifdef __aarch64__
#include <arm_neon.h>
#else
#include <x86intrin.h>
#endif
#elif _AE_WINDOWS_
#include <intrin.h>
#elif _AE_EMSCRIPTEN_
#include <emscripten.h>
#include <emscripten/html5.h>
#include <webgl/webgl1.h> // For Emscripten WebGL API headers (see also webgl/webgl1_ext.h and webgl/webgl2.h)
#endif
#if !_AE_WINDOWS_
#include <cxxabi.h>
#endif
//------------------------------------------------------------------------------
// Platform Utils
//------------------------------------------------------------------------------
#ifndef AE_BREAK
#if _AE_WINDOWS_
#define AE_BREAK() __debugbreak()
#elif _AE_APPLE_
#define AE_BREAK() __builtin_trap()
#elif _AE_EMSCRIPTEN_
#define AE_BREAK() assert( 0 )
#elif defined( __aarch64__ )
#define AE_BREAK() asm( "brk #0" )
#else
#define AE_BREAK() asm( "int $3" )
#endif
#endif
#if _AE_WINDOWS_
#define aeCompilationWarning( _msg ) _Pragma( message _msg )
#else
#define aeCompilationWarning( _msg ) _Pragma( "warning #_msg" )
#endif
#if _AE_LINUX_ || _AE_APPLE_
#define AE_ALIGN( _x ) __attribute__ ((aligned(_x)))
//#elif _AE_WINDOWS_
// @TODO: Windows doesn't support aligned function parameters
//#define AE_ALIGN( _x ) __declspec(align(_x))
#else
#define AE_ALIGN( _x )
#endif
#if _AE_WINDOWS_
#define AE_PACK( ... ) __pragma( pack(push, 1) ) __VA_ARGS__ __pragma( pack(pop))
#else
#define AE_PACK( ... ) __VA_ARGS__ __attribute__((__packed__))
#endif
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
template < typename T, int N > char( &countof_helper( T(&)[ N ] ) )[ N ];
#define countof( _x ) ( (uint32_t)sizeof( countof_helper( _x ) ) ) // @TODO: AE_COUNT_OF
#define AE_CALL_CONST( _tx, _x, _tfn, _fn ) const_cast< _tfn* >( const_cast< const _tx* >( _x )->_fn() );
#define _AE_STATIC_STORAGE template < uint32_t NN = N, typename = std::enable_if_t< NN != 0 > >
#define _AE_DYNAMIC_STORAGE template < uint32_t NN = N, typename = std::enable_if_t< NN == 0 > >
#define _AE_FIXED_POOL template < bool P = Paged, typename = std::enable_if_t< !P > >
#define _AE_PAGED_POOL template < bool P = Paged, typename = std::enable_if_t< P > >
#if !_AE_WINDOWS_
#define AE_DISABLE_INVALID_OFFSET_WARNING _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Winvalid-offsetof\"")
#define AE_ENABLE_INVALID_OFFSET_WARNING _Pragma("GCC diagnostic pop")
#else
#define AE_DISABLE_INVALID_OFFSET_WARNING
#define AE_ENABLE_INVALID_OFFSET_WARNING
#endif
#define AE_DISABLE_COPY_ASSIGNMENT( _t ) _t( const _t& ) = delete; _t& operator=( const _t& ) = delete
namespace ae {
//------------------------------------------------------------------------------
//! \defgroup Platform
//! @{
//------------------------------------------------------------------------------
//! Returns the process ID on Windows, OSX, and Linux. Returns 0 with Emscripten builds.
uint32_t GetPID();
//! Returns the number of virtual cores available.
uint32_t GetMaxConcurrentThreads();
//! Returns true if attached to Visual Studio or Xcode.
bool IsDebuggerAttached();
//! Returns the name of the given class or basic type from an instance. Note
//! that this does not return the name of the derived class if the instance is
//! a base class (get the ae::ClassType of an ae::Object in that case).
template < typename T > const char* GetTypeName();
//! Returns the name of the given class or basic type from an instance. Note
//! that this does not return the name of the derived class if the instance is
//! a base class (get the ae::ClassType of an ae::Object in that case).
template < typename T > const char* GetTypeName( const T& );
//! Returns a monotonically increasing time in seconds, useful for calculating high precision deltas. Time '0' is undefined.
double GetTime();
//! Shows a generic message box
void ShowMessage( const char* msg );
//! Sets the systems clipboard text so it can be pasted into other applications.
void SetClipboardText( const char* text );
//! Gets the systems clipboard text.
std::string GetClipboardText();
//! @} End Platform defgroup
//------------------------------------------------------------------------------
// Tags @TODO: Remove this! All tags should be user specified
//------------------------------------------------------------------------------
using Tag = std::string; // @TODO: Fixed length string
#define AE_ALLOC_TAG_RENDER ae::Tag( "aeGraphics" )
#define AE_ALLOC_TAG_AUDIO ae::Tag( "aeAudio" )
#define AE_ALLOC_TAG_TERRAIN ae::Tag( "aeTerrain" )
#define AE_ALLOC_TAG_NET ae::Tag( "aeNet" )
#define AE_ALLOC_TAG_HOTSPOT ae::Tag( "aeHotSpot" )
#define AE_ALLOC_TAG_MESH ae::Tag( "aeMesh" )
#define AE_ALLOC_TAG_FIXME ae::Tag( "aeFixMe" )
#define AE_ALLOC_TAG_FILE ae::Tag( "aeFile" )
//------------------------------------------------------------------------------
//! \defgroup Allocation
//! Allocation utilities.
//! By default aether-game-utils uses system allocations (malloc / free). The
//! default allocator is thread safe. If this is not okay for your use case,
//! it's advised that you implement your own ae::Allocator with dlmalloc or
//! similar and then call ae::SetGlobalAllocator() with your allocator at
//! program start.
//! @{
//------------------------------------------------------------------------------
//! ae::Allocator base class
//! Inherit from this to manage how allocations are handled. ALL ALLOCATIONS are
//! made through the ae::Allocator interface. This allows for custom memory
//! management, such as pooling, tracking, etc. See ae::_DefaultAllocator for a
//! simple example of how to implement an allocator.
//------------------------------------------------------------------------------
class Allocator
{
public:
virtual ~Allocator();
//! Should return 'bytes' with minimum alignment of 'alignment'. Optionally, a
//! tag should be used to select a pool of memory, or for diagnostics/debugging.
virtual void* Allocate( ae::Tag tag, uint32_t bytes, uint32_t alignment ) = 0;
//! Should attempt to expand or contract allocations made with Allocate() to
//! match size 'bytes'. On failure this function should return nullptr.
virtual void* Reallocate( void* data, uint32_t bytes, uint32_t alignment ) = 0;
//! Free memory allocated with ae::Allocator::Allocate() or ae::Allocator::Reallocate().
virtual void Free( void* data ) = 0;
//! Used for safety checks.
virtual bool IsThreadSafe() const = 0;
};
//! The given ae::Allocator is used for all memory allocations. You must call
//! ae::SetGlobalAllocator() before any allocations are made or else a default
//! allocator which uses malloc / free will be used. The set value can be retrieved
//! with ae::GetGlobalAllocator().
void SetGlobalAllocator( Allocator* alloc );
//! Get the custom allocator set with ae::SetGlobalAllocator(). If no custom
//! allocator is set before the first allocation is made, this will return a
//! default ae::Allocator which uses malloc / free. If ae::SetGlobalAllocator() has
//! never been called and no allocations have been made, this will return nullptr.
Allocator* GetGlobalAllocator();
//! Allocates and constructs an array of 'count' elements of type T. an ae::Tag
//! must be specifed and should represent the allocation type. Type T must have a
//! default constructor. All arrays allocated with this function should be freed with
//! ae::Delete(). Uses ae::GetGlobalAllocator() and ae::Allocator::Allocate() internally.
template < typename T > T* NewArray( ae::Tag tag, uint32_t count );
//! Allocates and constructs a single element of type T. an ae::Tag must be specified
//! and should represent the allocation type. All 'args' are passed to the constructor
//! of T. All allocations should be freed with ae::Delete(). Uses ae::GetGlobalAllocator()
//! and ae::Allocator::Allocate() internally.
template < typename T, typename ... Args > T* New( ae::Tag tag, Args ... args );
//! Should be called to destruct and free all allocations made with ae::New()
//! and ae::NewArray(). Uses ae::GetGlobalAllocator() and ae::Allocator::Free()
//! internally.
template < typename T > void Delete( T* obj );
// C style allocations
void* Allocate( ae::Tag tag, uint32_t bytes, uint32_t alignment );
void* Reallocate( void* data, uint32_t bytes, uint32_t alignment );
void Free( void* data );
//! @} End Allocation defgroup
//------------------------------------------------------------------------------
// ae::Scratch< T > class
//! Can be used for scoped allocations within a single frame. Because this uses
//! a stack internally it can be used to make many cheap allocations, while
//! avoiding memory fragmentation. Up to kMaxScratchSize bytes may be allocated
//! at a time. Allocated objects will have their constructors and destructors
//! called in ae::Scratch() and ~ae::Scratch respectively.
//------------------------------------------------------------------------------
template < typename T >
class Scratch
{
public:
Scratch( uint32_t count );
~Scratch();
T* Data();
uint32_t Length() const;
T& operator[] ( int32_t index );
const T& operator[] ( int32_t index ) const;
T& GetSafe( int32_t index );
const T& GetSafe( int32_t index ) const;
//! The max cumulative size of the internal scratch stack
static const uint32_t kMaxScratchSize = 4 * 1024 * 1024;
private:
T* m_data;
uint32_t m_size;
uint32_t m_prevOffsetCheck;
};
//------------------------------------------------------------------------------
//! \defgroup Math
//! @{
//------------------------------------------------------------------------------
constexpr float PI = 3.14159265358979323846f;
constexpr float TWO_PI = 2.0f * PI;
constexpr float HALF_PI = 0.5f * PI;
constexpr float QUARTER_PI = 0.25f * PI;
constexpr float Pi = 3.14159265358979323846f;
constexpr float TwoPi = 2.0f * PI;
constexpr float HalfPi = 0.5f * PI;
constexpr float QuarterPi = 0.25f * PI;
enum class Axis { None, X, Y, Z, NegativeX, NegativeY, NegativeZ };
//------------------------------------------------------------------------------
// Standard math operations
//------------------------------------------------------------------------------
inline float Pow( float x, float e );
inline float Cos( float x );
inline float Sin( float x );
inline float Tan( float x );
inline float Acos( float x );
inline float Asin( float x );
inline float Atan( float x );
inline float Atan2( float y, float x );
inline float Sqrt( float x );
inline uint32_t Mod( uint32_t i, uint32_t n );
inline int Mod( int32_t i, int32_t n );
inline float Mod( float f, float n );
inline int32_t Ceil( float f );
inline int32_t Floor( float f );
inline int32_t Round( float f );
inline float Abs( float x );
inline int32_t Abs( int32_t x );
//------------------------------------------------------------------------------
// Range functions
//------------------------------------------------------------------------------
template < typename T0, typename T1, typename... TTT >
constexpr auto Min( const T0& v0, const T1& v1, const TTT&... tail );
template < typename T0, typename T1, typename... TTT >
constexpr auto Max( const T0& v0, const T1& v1, const TTT&... tail );
template < typename T > inline T Clip( T x, T min, T max );
inline float Clip01( float x );
//------------------------------------------------------------------------------
// Interpolation
//------------------------------------------------------------------------------
template< typename T0, typename T1 > T0 Lerp( T0 start, T0 end, T1 t );
inline float AngleDifference( float end, float start );
inline float LerpAngle( float start, float end, float t );
inline float Delerp( float start, float end, float value );
inline float Delerp01( float start, float end, float value );
template< typename T > T DtLerp( T start, float snappiness, float dt, T end );
template< typename T > T DtSlerp( T start, float snappiness, float dt, T end );
inline float DtLerpAngle( float start, float snappiness, float dt, float end );
// @TODO: Cleanup duplicate interpolation functions
template< typename T > T CosineInterpolate( T start, T end, float t );
namespace Interpolation
{
template< typename T > T Linear( T start, T end, float t );
template< typename T > T Cosine( T start, T end, float t );
}
//------------------------------------------------------------------------------
// Angle functions
//------------------------------------------------------------------------------
inline float DegToRad( float degrees );
inline float RadToDeg( float radians );
//------------------------------------------------------------------------------
// Type specific limits
//------------------------------------------------------------------------------
template< typename T > constexpr T MaxValue();
template< typename T > constexpr T MinValue();
// Forward declare to avoid https://stackoverflow.com/questions/7774188/explicit-specialization-after-instantiation
template<> constexpr float MaxValue< float >();
template<> constexpr float MinValue< float >();
template<> constexpr double MaxValue< double >();
template<> constexpr double MinValue< double >();
//------------------------------------------------------------------------------
// ae::Random functions
//------------------------------------------------------------------------------
extern uint64_t _randomSeed;
void RandomSeed();
inline float Random01( uint64_t* seed = &_randomSeed );
inline bool RandomBool( uint64_t* seed = &_randomSeed );
inline int32_t Random( int32_t minInclusive, int32_t maxExclusive, uint64_t* seed = &_randomSeed );
inline float Random( float min, float max, uint64_t* seed = &_randomSeed );
template < typename T >
class RandomValue
{
public:
RandomValue( uint64_t* seed = &_randomSeed ) : m_seed( seed ) {}
RandomValue( T min, T max, uint64_t* seed = &_randomSeed );
RandomValue( T value, uint64_t* seed = &_randomSeed );
void SetMin( T min );
void SetMax( T max );
T GetMin() const;
T GetMax() const;
T Get() const;
operator T() const;
private:
uint64_t* m_seed = nullptr;
T m_min = T();
T m_max = T();
};
//------------------------------------------------------------------------------
// Vector math utilities
//------------------------------------------------------------------------------
struct Vec2;
struct Vec3;
struct Vec4;
class Matrix4;
class Quaternion;
//------------------------------------------------------------------------------
// ae::Vec2 shared member functions
// ae::Vec3 shared member functions
// ae::Vec4 shared member functions
//------------------------------------------------------------------------------
// @NOTE: Vec2 Vec3 and Vec4 share these functions. They act on each component
// of the vector, so in the case of Vec4 a dot product is implemented as
// (a.x*b.x)+(a.y*b.y)+(a.z*b.z)+(a.w*b.w).
template < typename T >
struct VecT
{
VecT() = default;
VecT( bool ) = delete;
bool operator==( const T& v ) const;
bool operator!=( const T& v ) const;
float operator[]( uint32_t idx ) const;
float& operator[]( uint32_t idx );
T operator-() const;
T operator*( float s ) const;
T operator/( float s ) const;
T operator+( const T& v ) const;
T operator-( const T& v ) const;
T operator*( const T& v ) const;
T operator/( const T& v ) const;
void operator*=( float s );
void operator/=( float s );
void operator+=( const T& v );
void operator-=( const T& v );
void operator*=( const T& v );
void operator/=( const T& v );
static float Dot( const T& v0, const T& v1 );
float Dot( const T& v ) const;
float Length() const;
float LengthSquared() const;
float Normalize();
float SafeNormalize( float epsilon = 0.000001f );
T NormalizeCopy() const;
T SafeNormalizeCopy( float epsilon = 0.000001f ) const;
float Trim( float length );
T TrimCopy( float length ) const;
bool IsNAN() const;
};
#if _AE_WINDOWS_
#pragma warning(disable:26495) // Vecs are left uninitialized for performance
#endif
//------------------------------------------------------------------------------
// ae::Vec2 struct
//------------------------------------------------------------------------------
struct AE_ALIGN( 8 ) Vec2 : public VecT< Vec2 >
{
Vec2() = default; //!< Trivial default constructor for performance of vertex arrays etc
Vec2( const Vec2& ) = default;
explicit Vec2( float v );
Vec2( float x, float y );
explicit Vec2( const float* xy );
explicit Vec2( struct Int2 i2 );
static Vec2 FromAngle( float angle );
struct Int2 NearestCopy() const;
struct Int2 FloorCopy() const;
struct Int2 CeilCopy() const;
Vec2 RotateCopy( float rotation ) const;
float GetAngle() const;
Vec2 DtSlerp( const Vec2& end, float snappiness, float dt, float epsilon = 0.0001f ) const;
Vec2 Slerp( const Vec2& end, float t, float epsilon = 0.0001f ) const;
static Vec2 Reflect( Vec2 v, Vec2 n );
//! Define conversion functions etc for ae::Vec2. See AE_CONFIG_FILE for more info.
#ifdef AE_VEC2_CLASS_CONFIG
AE_VEC2_CLASS_CONFIG
#endif
union
{
struct
{
float x;
float y;
};
float data[ 2 ];
};
};
//------------------------------------------------------------------------------
// ae::Vec3 struct
//------------------------------------------------------------------------------
struct AE_ALIGN( 16 ) Vec3 : public VecT< Vec3 >
{
Vec3() = default; //!< Trivial constructor for performance of vertex arrays etc
explicit Vec3( float v );
Vec3( float x, float y, float z );
explicit Vec3( const float* xyz );
explicit Vec3( struct Int3 i3 );
Vec3( Vec2 xy, float z );
explicit Vec3( Vec2 xy );
explicit operator Vec2() const;
static Vec3 XZY( Vec2 xz, float y );
void SetXY( Vec2 xy );
void SetXZ( Vec2 xz );
Vec2 GetXY() const;
Vec2 GetXZ() const;
struct Int3 NearestCopy() const;
struct Int3 FloorCopy() const;
struct Int3 CeilCopy() const;
void AddRotationXY( float rotation ); // @TODO: Support Y up
Vec3 AddRotationXYCopy( float rotation ) const;
float GetAngleBetween( const Vec3& v, float epsilon = 0.0001f ) const;
Vec3 RotateCopy( Vec3 axis, float angle ) const;
Vec3 Lerp( const Vec3& end, float t ) const;
Vec3 DtSlerp( const Vec3& end, float snappiness, float dt, float epsilon = 0.0001f ) const;
Vec3 Slerp( const Vec3& end, float t, float epsilon = 0.0001f ) const;
static Vec3 Cross( const Vec3& v0, const Vec3& v1 );
Vec3 Cross( const Vec3& v ) const;
void ZeroAxis( Vec3 axis ); // Zero component along arbitrary axis (ie vec dot axis == 0)
void ZeroDirection( Vec3 direction ); // Zero component along positive half of axis (ie vec dot dir > 0)
Vec3 ZeroAxisCopy( Vec3 axis ) const; // Zero component along arbitrary axis (ie vec dot axis == 0)
Vec3 ZeroDirectionCopy( Vec3 direction ) const; // Zero component along positive half of axis (ie vec dot dir > 0)
static Vec3 ProjectPoint( const class Matrix4& projection, Vec3 p );
//! Define conversion functions etc for ae::Vec3. See AE_CONFIG_FILE for more info.
#ifdef AE_VEC3_CLASS_CONFIG
AE_VEC3_CLASS_CONFIG
#endif
union
{
struct
{
float x;
float y;
float z;
};
float data[ 3 ];
};
float pad;
};
//------------------------------------------------------------------------------
// ae::Vec4 struct
//------------------------------------------------------------------------------
struct AE_ALIGN( 16 ) Vec4 : public VecT< Vec4 >
{
Vec4() = default; //!< Trivial default constructor for performance of vertex arrays etc
Vec4( const Vec4& ) = default;
explicit Vec4( float f );
explicit Vec4( float xyz, float w );
Vec4( float x, float y, float z, float w );
Vec4( Vec3 xyz, float w );
Vec4( Vec2 xy, float z, float w );
Vec4( Vec2 xy, Vec2 zw );
explicit operator Vec2() const;
explicit operator Vec3() const;
Vec4( const float* xyz, float w );
explicit Vec4( const float* xyzw );
void SetXY( Vec2 xy );
void SetXZ( Vec2 xz );
void SetZW( Vec2 zw );
void SetXYZ( Vec3 xyz );
Vec2 GetXY() const;
Vec2 GetXZ() const;
Vec2 GetZW() const;
Vec3 GetXYZ() const;
//! Define conversion functions etc for ae::Vec4. See AE_CONFIG_FILE for more info.
#ifdef AE_VEC4_CLASS_CONFIG
AE_VEC4_CLASS_CONFIG
#endif
union
{
struct
{
float x;
float y;
float z;
float w;
};
float data[ 4 ];
};
};
//------------------------------------------------------------------------------
// ae::Matrix4 struct
//------------------------------------------------------------------------------
//! Stored column major, ie. the elements of the basis vectors are stored
//! contiguously in memory.
class AE_ALIGN( 16 ) Matrix4
{
public:
Matrix4() = default; //!< Trivial default constructor for performance
Matrix4( const ae::Matrix4& ) = default;
// Construction helpers
static ae::Matrix4 Identity();
static ae::Matrix4 Translation( float tx, float ty, float tz );
static ae::Matrix4 Translation( const ae::Vec3& p );
static ae::Matrix4 Rotation( ae::Vec3 forward0, ae::Vec3 up0, ae::Vec3 forward1, ae::Vec3 up1 );
static ae::Matrix4 RotationX( float angle );
static ae::Matrix4 RotationY( float angle );
static ae::Matrix4 RotationZ( float angle );
static ae::Matrix4 Scaling( float s );
static ae::Matrix4 Scaling( const ae::Vec3& s );
static ae::Matrix4 Scaling( float sx, float sy, float sz );
static ae::Matrix4 WorldToView( ae::Vec3 position, ae::Vec3 forward, ae::Vec3 up );
static ae::Matrix4 ViewToProjection( float fov, float aspectRatio, float nearPlane, float farPlane );
// Set transformation properties
void SetTranslation( float x, float y, float z );
void SetTranslation( const ae::Vec3& t );
void SetRotation( const ae::Quaternion& r );
void SetScale( const ae::Vec3& s );
void SetTranspose();
void SetInverse();
void SetAxis( uint32_t column, const ae::Vec3& v );
void SetAxis( uint32_t column, const ae::Vec4& v );
void SetRow( uint32_t row, const ae::Vec3& v );
void SetRow( uint32_t row, const ae::Vec4& v );
// Get transformation properties
ae::Vec3 GetTranslation() const;
ae::Quaternion GetRotation() const;
ae::Vec3 GetScale() const;
ae::Matrix4 GetTranspose() const;
ae::Matrix4 GetInverse() const; // @TODO: Handle non-invertible matrices in API
ae::Matrix4 GetNormalMatrix() const;
ae::Matrix4 GetScaleRemoved() const;
ae::Vec3 GetAxis( uint32_t column ) const;
ae::Vec4 GetColumn( uint32_t column ) const;
ae::Vec4 GetRow( uint32_t row ) const;
//! Matrix multiplication
ae::Matrix4 operator*( const ae::Matrix4& m ) const;
//! Matrix multiplication
void operator*=( const ae::Matrix4& m );
//! Vector multiplication
ae::Vec4 operator*( const ae::Vec4& v ) const;
//! Transform the given vector as if it had a w component of 1. No perspective
//! divide is done on the result, instead use operator*(ae::Vec4) for
//! perspective transformations.
ae::Vec3 TransformPoint3x4( ae::Vec3 v ) const;
//! Transform the given vector as if it had a w component of 0. No perspective
//! divide is done on the result, instead use operator*(ae::Vec4) for
//! perspective transformations.
ae::Vec3 TransformVector3x4( ae::Vec3 v ) const;
// Comparison
bool operator==( const ae::Matrix4& o ) const { return memcmp( o.data, data, sizeof(data) ) == 0; }
bool operator!=( const ae::Matrix4& o ) const { return !operator== ( o ); }
bool IsNAN() const;
//! Define conversion functions etc for ae::Matrix4. See AE_CONFIG_FILE for more info.
#ifdef AE_MAT4_CLASS_CONFIG
AE_MAT4_CLASS_CONFIG
#endif
union
{
ae::Vec4 columns[ 4 ];
float data[ 16 ];
};
};
inline std::ostream& operator << ( std::ostream& os, const ae::Matrix4& mat );
//------------------------------------------------------------------------------
// ae::Quaternion class
//------------------------------------------------------------------------------
class AE_ALIGN( 16 ) Quaternion
{
public:
union
{
struct
{
float i;
float j;
float k;
float r;
};
float data[ 4 ];
};
Quaternion() = default;
Quaternion( const Quaternion& ) = default;
Quaternion( const float i, const float j, const float k, const float r ) : i(i), j(j), k(k), r(r) {}
explicit Quaternion( Vec3 v ) : i(v.x), j(v.y), k(v.z), r(0.0f) {}
Quaternion( Vec3 forward, Vec3 up, bool prioritizeUp = true );
Quaternion( Vec3 axis, float angle );
static Quaternion Identity() { return Quaternion( 0.0f, 0.0f, 0.0f, 1.0f ); }
void Normalize();
Quaternion NormalizeCopy() const;
bool operator==( const Quaternion& q ) const;
bool operator!=( const Quaternion& q ) const;
Quaternion& operator*= ( const Quaternion& q );
Quaternion operator* ( const Quaternion& q ) const;
Quaternion operator- () const;
float Dot( const Quaternion& q ) const;
Quaternion operator* ( float s ) const;
void AddScaledVector( const Vec3& v, float s );
void RotateByVector( const Vec3& v );
void SetDirectionXY( const Vec3& v );
Vec3 GetDirectionXY() const;
void ZeroXY();
void GetAxisAngle( Vec3* axis, float* angle ) const;
void AddRotationXY( float rotation );
Quaternion Nlerp( Quaternion end, float t ) const;
Matrix4 GetTransformMatrix() const;
Quaternion GetInverse() const;
Quaternion& SetInverse();
Vec3 Rotate( Vec3 v ) const;
//! Returns a quaternion which rotates this quaternion to the given quaternion.
//! ie: this = reference * relative
Quaternion RelativeCopy( const Quaternion& reference ) const;
//! Returns the twist and swing components of this quaternion around the
//! given \p axis. The \p twistOut component is the rotation around the axis, and
//! the \p swingOut component is the rotation around the axis' orthogonal plane.
void GetTwistSwing( Vec3 axis, Quaternion* twistOut, Quaternion* swingOut ) const;
//! Define conversion functions etc for ae::Quaternion. See AE_CONFIG_FILE for more info.
#ifdef AE_QUAT_CLASS_CONFIG
AE_QUAT_CLASS_CONFIG
#endif
};
inline std::ostream& operator << ( std::ostream& os, const Quaternion& quat );
//------------------------------------------------------------------------------
// ae::Int2 shared member functions
// ae::Int3 shared member functions
//------------------------------------------------------------------------------
// @NOTE: Int2 and Int3 share these functions
template < typename T >
struct IntT
{
IntT() = default;
IntT( bool ) = delete;
bool operator==( const T& v ) const;
bool operator!=( const T& v ) const;
int32_t operator[]( uint32_t idx ) const;
int32_t& operator[]( uint32_t idx );
T operator-() const;
T operator+( const T& v ) const;
T operator-( const T& v ) const;
T operator*( const T& v ) const;
T operator/( const T& v ) const;
void operator+=( const T& v );
void operator-=( const T& v );
void operator*=( const T& v );
void operator/=( const T& v );
T operator*( int32_t s ) const;
T operator/( int32_t s ) const;
void operator*=( int32_t s );
void operator/=( int32_t s );
};
template < typename T >
inline std::ostream& operator<<( std::ostream& os, const IntT< T >& v );
//------------------------------------------------------------------------------
// ae::Int2 class
//------------------------------------------------------------------------------
struct AE_ALIGN( 8 ) Int2 : public IntT< Int2 >
{
union
{
struct
{
int32_t x;
int32_t y;
};
int32_t data[ 2 ];
};
Int2() = default;
Int2( const Int2& ) = default;
explicit Int2( int32_t _v );
explicit Int2( const struct Int3& v );
Int2( int32_t _x, int32_t _y );
// @NOTE: No automatic conversion from ae::Vec2 because rounding type should be explicit!
};
//------------------------------------------------------------------------------
// ae::Int3 class
//------------------------------------------------------------------------------
struct AE_ALIGN( 16 ) Int3 : public IntT< Int3 >
{
union
{
struct
{
int32_t x;
int32_t y;
int32_t z;
};
int32_t data[ 3 ];
};
int32_t pad;
Int3() = default;
Int3( const Int3& ) = default;
explicit Int3( int32_t _v );
Int3( int32_t _x, int32_t _y, int32_t _z );
Int3( Int2 xy, int32_t _z );
Int3( const int32_t( &v )[ 3 ] );
Int3( const int32_t( &v )[ 4 ] );
explicit Int3( int32_t*& v );
explicit Int3( const int32_t*& v );
// @NOTE: No conversion from ae::Vec3 because rounding type should be explicit!
Int2 GetXY() const;
Int2 GetXZ() const;
void SetXY( Int2 xy );
void SetXZ( Int2 xz );
};
//------------------------------------------------------------------------------
// ae::Sphere class
//------------------------------------------------------------------------------
class Sphere
{
public:
Sphere() = default;
Sphere( ae::Vec3 center, float radius ) : center( center ), radius( radius ) {}
explicit Sphere( const class OBB& obb );
void Expand( ae::Vec3 p );
// @TODO: IntersectLine() which should have hit0Out and hit1Out
bool IntersectRay( ae::Vec3 origin, ae::Vec3 direction, ae::Vec3* pOut = nullptr, float* tOut = nullptr ) const;
bool IntersectTriangle( ae::Vec3 t0, ae::Vec3 t1, ae::Vec3 t2, ae::Vec3* outNearestIntersectionPoint ) const;
ae::Vec3 GetNearestPointOnSurface( ae::Vec3 p, float* signedDistOut = nullptr ) const;
ae::Vec3 center = ae::Vec3( 0.0f );
float radius = 0.5f;
};
//------------------------------------------------------------------------------
// ae::Plane class
//! A plane in the form of ax+by+cz+d=0. This means that n<x,y,z>*d equals the