Jolt physics 5.1 (#922)

This commit is contained in:
Turánszki János
2024-08-13 07:48:41 +02:00
committed by GitHub
parent 7449063784
commit bc2c5c8ce0
109 changed files with 1159 additions and 512 deletions
+6
View File
@@ -81,6 +81,12 @@ inline const char *GetConfigurationString()
#endif
#ifdef JPH_DEBUG
"(Debug) "
#endif
#if defined(__cpp_rtti) && __cpp_rtti
"(C++ RTTI) "
#endif
#if defined(__cpp_exceptions) && __cpp_exceptions
"(C++ Exceptions) "
#endif
;
}
+20 -14
View File
@@ -6,13 +6,22 @@
#ifdef JPH_USE_NEON
// Constructing NEON values
#ifdef JPH_COMPILER_MSVC
JPH_NAMESPACE_BEGIN
// Constructing NEON values
#define JPH_NEON_INT32x4(v1, v2, v3, v4) { int64_t(v1) + (int64_t(v2) << 32), int64_t(v3) + (int64_t(v4) << 32) }
#define JPH_NEON_UINT32x4(v1, v2, v3, v4) { uint64_t(v1) + (uint64_t(v2) << 32), uint64_t(v3) + (uint64_t(v4) << 32) }
#define JPH_NEON_INT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { int64_t(v1) + (int64_t(v2) << 8) + (int64_t(v3) << 16) + (int64_t(v4) << 24) + (int64_t(v5) << 32) + (int64_t(v6) << 40) + (int64_t(v7) << 48) + (int64_t(v8) << 56), int64_t(v9) + (int64_t(v10) << 8) + (int64_t(v11) << 16) + (int64_t(v12) << 24) + (int64_t(v13) << 32) + (int64_t(v14) << 40) + (int64_t(v15) << 48) + (int64_t(v16) << 56) }
#define JPH_NEON_UINT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { uint64_t(v1) + (uint64_t(v2) << 8) + (uint64_t(v3) << 16) + (uint64_t(v4) << 24) + (uint64_t(v5) << 32) + (uint64_t(v6) << 40) + (uint64_t(v7) << 48) + (uint64_t(v8) << 56), uint64_t(v9) + (uint64_t(v10) << 8) + (uint64_t(v11) << 16) + (uint64_t(v12) << 24) + (uint64_t(v13) << 32) + (uint64_t(v14) << 40) + (uint64_t(v15) << 48) + (uint64_t(v16) << 56) }
#else
#define JPH_NEON_INT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 }
#define JPH_NEON_UINT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 }
#define JPH_NEON_INT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 }
#define JPH_NEON_UINT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 }
#endif
// MSVC and GCC prior to version 12 don't define __builtin_shufflevector
#if defined(JPH_COMPILER_MSVC) || (defined(JPH_COMPILER_GCC) && __GNUC__ < 12)
JPH_NAMESPACE_BEGIN
// Generic shuffle vector template
template <unsigned I1, unsigned I2, unsigned I3, unsigned I4>
@@ -30,13 +39,13 @@
template <>
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 2, 2>(float32x4_t inV1, float32x4_t inV2)
{
return vcombine_f32(vget_low_f32(inV1), vdup_lane_s32(vget_high_f32(inV1), 0));
return vcombine_f32(vget_low_f32(inV1), vdup_lane_f32(vget_high_f32(inV1), 0));
}
template <>
JPH_INLINE float32x4_t NeonShuffleFloat32x4<0, 1, 3, 3>(float32x4_t inV1, float32x4_t inV2)
{
return vcombine_f32(vget_low_f32(inV1), vdup_lane_s32(vget_high_f32(inV1), 1));
return vcombine_f32(vget_low_f32(inV1), vdup_lane_f32(vget_high_f32(inV1), 1));
}
template <>
@@ -48,13 +57,13 @@
template <>
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 0, 3, 2>(float32x4_t inV1, float32x4_t inV2)
{
return vcombine_f32(vrev64_f32(vget_low_f32(inV1)), vrev64_f32(vget_high_f32(inV1)));
return vcombine_f32(vrev64_f32(vget_low_f32(inV1)), vrev64_f32(vget_high_f32(inV1)));
}
template <>
JPH_INLINE float32x4_t NeonShuffleFloat32x4<2, 2, 1, 0>(float32x4_t inV1, float32x4_t inV2)
{
return vcombine_f32(vdup_lane_s32(vget_high_f32(inV1), 0), vrev64_f32(vget_low_f32(inV1)));
return vcombine_f32(vdup_lane_f32(vget_high_f32(inV1), 0), vrev64_f32(vget_low_f32(inV1)));
}
template <>
@@ -67,22 +76,19 @@
template <>
JPH_INLINE float32x4_t NeonShuffleFloat32x4<1, 2, 0, 0>(float32x4_t inV1, float32x4_t inV2)
{
static int8x16_t table = JPH_NEON_INT8x16(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03);
return vreinterpretq_f32_u8(vqtbl1q_u8(vreinterpretq_u8_f32(inV1), table));
static uint8x16_t table = JPH_NEON_UINT8x16(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03);
return vreinterpretq_f32_u8(vqtbl1q_u8(vreinterpretq_u8_f32(inV1), table));
}
// Shuffle a vector
#define JPH_NEON_SHUFFLE_F32x4(vec1, vec2, index1, index2, index3, index4) NeonShuffleFloat32x4<index1, index2, index3, index4>(vec1, vec2)
#define JPH_NEON_SHUFFLE_U32x4(vec1, vec2, index1, index2, index3, index4) vreinterpretq_u32_f32((NeonShuffleFloat32x4<index1, index2, index3, index4>(vreinterpretq_f32_u32(vec1), vreinterpretq_f32_u32(vec2))))
JPH_NAMESPACE_END
#else
// Constructing NEON values
#define JPH_NEON_INT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 }
#define JPH_NEON_UINT32x4(v1, v2, v3, v4) { v1, v2, v3, v4 }
#define JPH_NEON_INT8x16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) { v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 }
// Shuffle a vector
#define JPH_NEON_SHUFFLE_F32x4(vec1, vec2, index1, index2, index3, index4) __builtin_shufflevector(vec1, vec2, index1, index2, index3, index4)
#define JPH_NEON_SHUFFLE_U32x4(vec1, vec2, index1, index2, index3, index4) __builtin_shufflevector(vec1, vec2, index1, index2, index3, index4)
#endif
#endif // JPH_USE_NEON
+5 -5
View File
@@ -194,8 +194,8 @@ public:
clear();
reserve(size_type(inList.size()));
for (typename std::initializer_list<T>::iterator i = inList.begin(); i != inList.end(); ++i)
::new (&mElements[mSize++]) T(*i);
for (const T &v : inList)
::new (&mElements[mSize++]) T(v);
}
/// Default constructor
@@ -582,13 +582,13 @@ namespace std
std::size_t ret = 0;
// Hash length first
JPH::HashCombine(ret, inRHS.size());
JPH::HashCombine(ret, inRHS.size());
// Then hash elements
for (const T &t : inRHS)
JPH::HashCombine(ret, t);
JPH::HashCombine(ret, t);
return ret;
return ret;
}
};
}
+12 -8
View File
@@ -6,8 +6,8 @@
// Jolt library version
#define JPH_VERSION_MAJOR 5
#define JPH_VERSION_MINOR 0
#define JPH_VERSION_PATCH 1
#define JPH_VERSION_MINOR 1
#define JPH_VERSION_PATCH 0
// Determine which features the library was compiled with
#ifdef JPH_DOUBLE_PRECISION
@@ -86,12 +86,12 @@
#elif defined(__FreeBSD__)
#define JPH_PLATFORM_FREEBSD
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
#define JPH_PLATFORM_MACOS
#else
#define JPH_PLATFORM_IOS
#endif
#include <TargetConditionals.h>
#if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE
#define JPH_PLATFORM_MACOS
#else
#define JPH_PLATFORM_IOS
#endif
#elif defined(__EMSCRIPTEN__)
#define JPH_PLATFORM_WASM
#endif
@@ -318,6 +318,7 @@
JPH_GCC_SUPPRESS_WARNING("-Wclass-memaccess") \
JPH_GCC_SUPPRESS_WARNING("-Wpedantic") \
JPH_GCC_SUPPRESS_WARNING("-Wunused-parameter") \
JPH_GCC_SUPPRESS_WARNING("-Wmaybe-uninitialized") \
\
JPH_MSVC_SUPPRESS_WARNING(4619) /* #pragma warning: there is no warning number 'XXXX' */ \
JPH_MSVC_SUPPRESS_WARNING(4514) /* 'X' : unreferenced inline function has been removed */ \
@@ -405,6 +406,9 @@ JPH_SUPPRESS_WARNINGS_STD_BEGIN
#include <functional>
#include <algorithm>
#include <cstdint>
#ifdef JPH_COMPILER_MSVC
#include <malloc.h> // for alloca
#endif
#if defined(JPH_USE_SSE)
#include <immintrin.h>
#elif defined(JPH_USE_NEON)
+2 -2
View File
@@ -75,11 +75,11 @@ public:
FPControlWord()
{
uint64 val;
asm volatile("mrs %0, fpcr" : "=r" (val));
asm volatile("mrs %0, fpcr" : "=r" (val));
mPrevState = val;
val &= ~Mask;
val |= Value;
asm volatile("msr fpcr, %0" : /* no output */ : "r" (val));
asm volatile("msr fpcr, %0" : /* no output */ : "r" (val));
}
~FPControlWord()
+13 -13
View File
@@ -46,7 +46,7 @@ template <typename T>
inline void HashCombineHelper(size_t &ioSeed, const T &inValue)
{
std::hash<T> hasher;
ioSeed ^= hasher(inValue) + 0x9e3779b9 + (ioSeed << 6) + (ioSeed >> 2);
ioSeed ^= hasher(inValue) + 0x9e3779b9 + (ioSeed << 6) + (ioSeed >> 2);
}
/// Hash combiner to use a custom struct in an unordered map or set
@@ -55,9 +55,9 @@ inline void HashCombineHelper(size_t &ioSeed, const T &inValue)
///
/// struct SomeHashKey
/// {
/// std::string key1;
/// std::string key2;
/// bool key3;
/// std::string key1;
/// std::string key2;
/// bool key3;
/// };
///
/// JPH_MAKE_HASHABLE(SomeHashKey, t.key1, t.key2, t.key3)
@@ -76,22 +76,22 @@ JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
#define JPH_MAKE_HASH_STRUCT(type, name, ...) \
struct [[nodiscard]] name \
{ \
std::size_t operator()(const type &t) const \
std::size_t operator()(const type &t) const \
{ \
std::size_t ret = 0; \
::JPH::HashCombine(ret, __VA_ARGS__); \
return ret; \
} \
};
std::size_t ret = 0; \
::JPH::HashCombine(ret, __VA_ARGS__); \
return ret; \
} \
};
#define JPH_MAKE_HASHABLE(type, ...) \
JPH_SUPPRESS_WARNING_PUSH \
JPH_SUPPRESS_WARNINGS \
namespace std \
namespace std \
{ \
template<> \
template<> \
JPH_MAKE_HASH_STRUCT(type, hash<type>, __VA_ARGS__) \
} \
} \
JPH_SUPPRESS_WARNING_POP
JPH_SUPPRESS_WARNING_POP
+1 -1
View File
@@ -30,7 +30,7 @@ JPH_EXPORT extern TraceFunction Trace;
#define JPH_IF_ENABLE_ASSERTS(...) __VA_ARGS__
#else
#define JPH_ASSERT(...) ((void)0)
#define JPH_ASSERT(...) ((void)0)
#define JPH_IF_ENABLE_ASSERTS(...)
#endif // JPH_ENABLE_ASSERTS
+3 -3
View File
@@ -33,9 +33,9 @@ JPH_NAMESPACE_BEGIN
/// barrier->AddJob(third_job);
/// job_system->WaitForJobs(barrier);
///
/// // Clean up
/// job_system->DestroyBarrier(barrier);
/// delete job_system;
/// // Clean up
/// job_system->DestroyBarrier(barrier);
/// delete job_system;
///
/// Jobs are guaranteed to be started in the order that their dependency counter becomes zero (in case they're scheduled on a background thread)
/// or in the order they're added to the barrier (when dependency count is zero and when executing on the thread that calls WaitForJobs).
@@ -46,8 +46,9 @@ JobSystemThreadPool::JobSystemThreadPool(uint inMaxJobs, uint inMaxBarriers, int
Init(inMaxJobs, inMaxBarriers, inNumThreads);
}
void JobSystemThreadPool::StartThreads(int inNumThreads)
void JobSystemThreadPool::StartThreads([[maybe_unused]] int inNumThreads)
{
#if !defined(JPH_CPU_WASM) || defined(__EMSCRIPTEN_PTHREADS__) // If we're running without threads support we cannot create threads and we ignore the inNumThreads parameter
// Auto detect number of threads
if (inNumThreads < 0)
inNumThreads = thread::hardware_concurrency() - 1;
@@ -69,6 +70,7 @@ void JobSystemThreadPool::StartThreads(int inNumThreads)
mThreads.reserve(inNumThreads);
for (int i = 0; i < inNumThreads; ++i)
mThreads.emplace_back([this, i] { ThreadMain(i); });
#endif
}
JobSystemThreadPool::~JobSystemThreadPool()
@@ -70,7 +70,7 @@ private:
/// Jobs queue for the barrier
static constexpr uint cMaxJobs = 2048;
static_assert(IsPowerOf2(cMaxJobs)); // We do bit operations and require max jobs to be a power of 2
atomic<Job *> mJobs[cMaxJobs]; ///< List of jobs that are part of this barrier, nullptrs for empty slots
atomic<Job *> mJobs[cMaxJobs]; ///< List of jobs that are part of this barrier, nullptrs for empty slots
alignas(JPH_CACHE_LINE_SIZE) atomic<uint> mJobReadIndex { 0 }; ///< First job that could be valid (modulo cMaxJobs), can be nullptr if other thread is still working on adding the job
alignas(JPH_CACHE_LINE_SIZE) atomic<uint> mJobWriteIndex { 0 }; ///< First job that can be written (modulo cMaxJobs)
atomic<int> mNumToAcquire { 0 }; ///< Number of times the semaphore has been released, the barrier should acquire the semaphore this many times (written at the same time as mJobWriteIndex so ok to put in same cache line)
+1 -1
View File
@@ -145,7 +145,7 @@ public:
bool operator != (const Iterator &inRHS) const { return !(*this == inRHS); }
/// Convert to key value pair
KeyValue & operator * ();
KeyValue & operator * ();
/// Next item
Iterator & operator ++ ();
+3 -3
View File
@@ -25,10 +25,10 @@ JPH_ALLOC_SCOPE void *JPH_ALLOC_FN(Allocate)(size_t inSize)
return malloc(inSize);
}
JPH_ALLOC_SCOPE void *JPH_ALLOC_FN(Reallocate)(void *inBlock, size_t inSize)
JPH_ALLOC_SCOPE void *JPH_ALLOC_FN(Reallocate)(void *inBlock, [[maybe_unused]] size_t inOldSize, size_t inNewSize)
{
JPH_ASSERT(inSize > 0);
return realloc(inBlock, inSize);
JPH_ASSERT(inNewSize > 0);
return realloc(inBlock, inNewSize);
}
JPH_ALLOC_SCOPE void JPH_ALLOC_FN(Free)(void *inBlock)
+2 -2
View File
@@ -10,7 +10,7 @@ JPH_NAMESPACE_BEGIN
// Normal memory allocation, must be at least 8 byte aligned on 32 bit platform and 16 byte aligned on 64 bit platform
using AllocateFunction = void *(*)(size_t inSize);
using ReallocateFunction = void *(*)(void *inBlock, size_t inSize);
using ReallocateFunction = void *(*)(void *inBlock, size_t inOldSize, size_t inNewSize);
using FreeFunction = void (*)(void *inBlock);
// Aligned memory allocation
@@ -42,7 +42,7 @@ JPH_EXPORT void RegisterDefaultAllocator();
// Directly define the allocation functions
JPH_EXPORT void *Allocate(size_t inSize);
JPH_EXPORT void *Reallocate(void *inBlock, size_t inSize);
JPH_EXPORT void *Reallocate(void *inBlock, size_t inOldSize, size_t inNewSize);
JPH_EXPORT void Free(void *inBlock);
JPH_EXPORT void *AlignedAllocate(size_t inSize, size_t inAlignment);
JPH_EXPORT void AlignedFree(void *inBlock);
+9 -4
View File
@@ -13,10 +13,15 @@ JPH_SUPPRESS_WARNINGS_STD_BEGIN
#include <fstream>
JPH_SUPPRESS_WARNINGS_STD_END
#ifdef JPH_PROFILE_ENABLED
JPH_NAMESPACE_BEGIN
#if defined(JPH_EXTERNAL_PROFILE) && defined(JPH_SHARED_LIBRARY)
ProfileStartMeasurementFunction ProfileStartMeasurement = [](const char *, uint32, uint8 *) { };
ProfileEndMeasurementFunction ProfileEndMeasurement = [](uint8 *) { };
#elif defined(JPH_PROFILE_ENABLED)
//////////////////////////////////////////////////////////////////////////////////////////
// Profiler
//////////////////////////////////////////////////////////////////////////////////////////
@@ -341,6 +346,6 @@ void Profiler::DumpChart(const char *inTag, const Threads &inThreads, const KeyT
</tbody></table></body></html>)";
}
JPH_NAMESPACE_END
#endif // JPH_PROFILE_ENABLED
JPH_NAMESPACE_END
+18 -1
View File
@@ -17,16 +17,31 @@ JPH_SUPPRESS_WARNINGS_STD_END
JPH_NAMESPACE_BEGIN
#ifdef JPH_SHARED_LIBRARY
/// Functions called when a profiler measurement starts or stops, need to be overridden by the user.
using ProfileStartMeasurementFunction = void (*)(const char *inName, uint32 inColor, uint8 *ioUserData);
using ProfileEndMeasurementFunction = void (*)(uint8 *ioUserData);
JPH_EXPORT extern ProfileStartMeasurementFunction ProfileStartMeasurement;
JPH_EXPORT extern ProfileEndMeasurementFunction ProfileEndMeasurement;
#endif // JPH_SHARED_LIBRARY
/// Create this class on the stack to start sampling timing information of a particular scope.
///
/// Left unimplemented intentionally. Needs to be implemented by the user of the library.
/// For statically linked builds, this is left unimplemented intentionally. Needs to be implemented by the user of the library.
/// On construction a measurement should start, on destruction it should be stopped.
/// For dynamically linked builds, the user should override the ProfileStartMeasurement and ProfileEndMeasurement functions.
class alignas(16) ExternalProfileMeasurement : public NonCopyable
{
public:
/// Constructor
#ifdef JPH_SHARED_LIBRARY
JPH_INLINE ExternalProfileMeasurement(const char *inName, uint32 inColor = 0) { ProfileStartMeasurement(inName, inColor, mUserData); }
JPH_INLINE ~ExternalProfileMeasurement() { ProfileEndMeasurement(mUserData); }
#else
ExternalProfileMeasurement(const char *inName, uint32 inColor = 0);
~ExternalProfileMeasurement();
#endif
private:
uint8 mUserData[64];
@@ -42,6 +57,8 @@ JPH_SUPPRESS_WARNING_PUSH
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
// Dummy implementations
#define JPH_PROFILE_START(name)
#define JPH_PROFILE_END()
#define JPH_PROFILE_THREAD_START(name)
#define JPH_PROFILE_THREAD_END()
#define JPH_PROFILE_NEXTFRAME()
+1 -1
View File
@@ -308,7 +308,7 @@ public: \
#define JPH_DECLARE_RTTI_WITH_NAMESPACE_FOR_FACTORY(linkage, name_space, class_name) \
namespace name_space { \
class class_name; \
class class_name; \
linkage RTTI * GetRTTIOfType(class class_name *); \
}
+6 -6
View File
@@ -108,7 +108,7 @@ public:
inline ~Ref() { Release(); }
/// Assignment operators
inline Ref<T> & operator = (T *inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }
inline Ref<T> & operator = (T *inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }
inline Ref<T> & operator = (const Ref<T> &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }
inline Ref<T> & operator = (Ref<T> &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }
@@ -116,7 +116,7 @@ public:
inline operator T *() const { return mPtr; }
/// Access like a normal pointer
inline T * operator -> () const { return mPtr; }
inline T * operator -> () const { return mPtr; }
inline T & operator * () const { return *mPtr; }
/// Comparison
@@ -126,7 +126,7 @@ public:
inline bool operator != (const Ref<T> &inRHS) const { return mPtr != inRHS.mPtr; }
/// Get pointer
inline T * GetPtr() const { return mPtr; }
inline T * GetPtr() const { return mPtr; }
/// INTERNAL HELPER FUNCTION USED BY SERIALIZATION
void ** InternalGetPointer() { return reinterpret_cast<void **>(&mPtr); }
@@ -160,7 +160,7 @@ public:
inline ~RefConst() { Release(); }
/// Assignment operators
inline RefConst<T> & operator = (const T * inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }
inline RefConst<T> & operator = (const T * inRHS) { if (mPtr != inRHS) { Release(); mPtr = inRHS; AddRef(); } return *this; }
inline RefConst<T> & operator = (const RefConst<T> &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }
inline RefConst<T> & operator = (RefConst<T> &&inRHS) noexcept { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; inRHS.mPtr = nullptr; } return *this; }
inline RefConst<T> & operator = (const Ref<T> &inRHS) { if (mPtr != inRHS.mPtr) { Release(); mPtr = inRHS.mPtr; AddRef(); } return *this; }
@@ -170,7 +170,7 @@ public:
inline operator const T * () const { return mPtr; }
/// Access like a normal pointer
inline const T * operator -> () const { return mPtr; }
inline const T * operator -> () const { return mPtr; }
inline const T & operator * () const { return *mPtr; }
/// Comparison
@@ -182,7 +182,7 @@ public:
inline bool operator != (const Ref<T> &inRHS) const { return mPtr != inRHS.mPtr; }
/// Get pointer
inline const T * GetPtr() const { return mPtr; }
inline const T * GetPtr() const { return mPtr; }
/// INTERNAL HELPER FUNCTION USED BY SERIALIZATION
void ** InternalGetPointer() { return const_cast<void **>(reinterpret_cast<const void **>(&mPtr)); }
-3
View File
@@ -6,9 +6,6 @@
JPH_NAMESPACE_BEGIN
// GCC doesn't properly detect that mState is used to ensure that mResult is initialized
JPH_GCC_SUPPRESS_WARNING("-Wmaybe-uninitialized")
/// Helper class that either contains a valid result or an error
template <class Type>
class Result
+2 -2
View File
@@ -60,10 +60,10 @@ public:
/// Reallocate memory
template <bool has_reallocate_v = has_reallocate, typename = std::enable_if_t<has_reallocate_v>>
inline pointer reallocate(pointer inOldPointer, [[maybe_unused]] size_type inOldSize, size_type inNewSize)
inline pointer reallocate(pointer inOldPointer, size_type inOldSize, size_type inNewSize)
{
JPH_ASSERT(inNewSize > 0); // Reallocating to zero size is implementation dependent, so we don't allow it
return pointer(Reallocate(inOldPointer, inNewSize * sizeof(value_type)));
return pointer(Reallocate(inOldPointer, inOldSize * sizeof(value_type), inNewSize * sizeof(value_type)));
}
/// Free memory
+5 -5
View File
@@ -26,8 +26,8 @@ public:
explicit StaticArray(std::initializer_list<T> inList)
{
JPH_ASSERT(inList.size() <= N);
for (typename std::initializer_list<T>::iterator i = inList.begin(); i != inList.end(); ++i)
::new (reinterpret_cast<T *>(&mElements[mSize++])) T(*i);
for (const T &v : inList)
::new (reinterpret_cast<T *>(&mElements[mSize++])) T(v);
}
/// Copy constructor
@@ -311,13 +311,13 @@ namespace std
std::size_t ret = 0;
// Hash length first
JPH::HashCombine(ret, inRHS.size());
JPH::HashCombine(ret, inRHS.size());
// Then hash elements
for (const T &t : inRHS)
JPH::HashCombine(ret, t);
JPH::HashCombine(ret, t);
return ret;
return ret;
}
};
}
+3 -3
View File
@@ -35,7 +35,7 @@ public:
template <class T, class A, std::enable_if_t<std::is_trivially_copyable_v<T>, bool> = true>
void Read(Array<T, A> &outT)
{
typename Array<T, A>::size_type len = outT.size(); // Initialize to previous array size, this is used for validation in the StateRecorder class
uint32 len = uint32(outT.size()); // Initialize to previous array size, this is used for validation in the StateRecorder class
Read(len);
if (!IsEOF() && !IsFailed())
{
@@ -60,7 +60,7 @@ public:
template <class Type, class Traits, class Allocator>
void Read(std::basic_string<Type, Traits, Allocator> &outString)
{
typename std::basic_string<Type, Traits, Allocator>::size_type len = 0;
uint32 len = 0;
Read(len);
if (!IsEOF() && !IsFailed())
{
@@ -75,7 +75,7 @@ public:
template <class T, class A, typename F>
void Read(Array<T, A> &outT, const F &inReadElement)
{
typename Array<T, A>::size_type len = outT.size(); // Initialize to previous array size, this is used for validation in the StateRecorder class
uint32 len = uint32(outT.size()); // Initialize to previous array size, this is used for validation in the StateRecorder class
Read(len);
if (!IsEOF() && !IsFailed())
{
+3 -3
View File
@@ -32,7 +32,7 @@ public:
template <class T, class A, std::enable_if_t<std::is_trivially_copyable_v<T>, bool> = true>
void Write(const Array<T, A> &inT)
{
typename Array<T, A>::size_type len = inT.size();
uint32 len = uint32(inT.size());
Write(len);
if (!IsFailed())
{
@@ -54,7 +54,7 @@ public:
template <class Type, class Traits, class Allocator>
void Write(const std::basic_string<Type, Traits, Allocator> &inString)
{
typename std::basic_string<Type, Traits, Allocator>::size_type len = inString.size();
uint32 len = uint32(inString.size());
Write(len);
if (!IsFailed())
WriteBytes(inString.data(), len * sizeof(Type));
@@ -64,7 +64,7 @@ public:
template <class T, class A, typename F>
void Write(const Array<T, A> &inT, const F &inWriteElement)
{
typename Array<T, A>::size_type len = inT.size();
uint32 len = uint32(inT.size());
Write(len);
if (!IsFailed())
for (typename Array<T, A>::size_type i = 0; i < len; ++i)
+3 -2
View File
@@ -126,7 +126,8 @@ Result<Ref<Type>> RestoreObjectReference(StreamIn &inStream, IDToObjectMap<Type>
template <class ArrayType, class ValueType>
void SaveObjectArray(StreamOut &inStream, const ArrayType &inArray, ObjectToIDMap<ValueType> *ioObjectToIDMap)
{
inStream.Write(size_t(inArray.size()));
uint32 len = uint32(inArray.size());
inStream.Write(len);
for (const ValueType *value: inArray)
SaveObjectReference(inStream, value, ioObjectToIDMap);
}
@@ -137,7 +138,7 @@ Result<ArrayType> RestoreObjectArray(StreamIn &inStream, IDToObjectMap<ValueType
{
Result<ArrayType> result;
size_t len;
uint32 len;
inStream.Read(len);
if (inStream.IsEOF() || inStream.IsFailed())
{
+3 -3
View File
@@ -15,9 +15,9 @@ template<typename T>
String ConvertToString(const T &inValue)
{
using OStringStream = std::basic_ostringstream<char, std::char_traits<char>, STLAllocator<char>>;
OStringStream oss;
oss << inValue;
return oss.str();
OStringStream oss;
oss << inValue;
return oss.str();
}
/// Calculate the FNV-1a hash of inString.
+68 -2
View File
@@ -86,16 +86,40 @@ public:
}
}
// Check if no allocations have been made
/// Check if no allocations have been made
bool IsEmpty() const
{
return mTop == 0;
}
/// Get the total size of the fixed buffer
uint GetSize() const
{
return mSize;
}
/// Get current usage in bytes of the buffer
uint GetUsage() const
{
return mTop;
}
/// Check if an allocation of inSize can be made in this fixed buffer allocator
bool CanAllocate(uint inSize) const
{
return mTop + AlignUp(inSize, JPH_RVECTOR_ALIGNMENT) <= mSize;
}
/// Check if memory block at inAddress is owned by this allocator
bool OwnsMemory(const void *inAddress) const
{
return inAddress >= mBase && inAddress < mBase + mSize;
}
private:
uint8 * mBase; ///< Base address of the memory block
uint mSize; ///< Size of the memory block
uint mTop = 0; ///< Current top of the stack
uint mTop = 0; ///< End of currently allocated area
};
/// Implementation of the TempAllocator that just falls back to malloc/free
@@ -119,4 +143,46 @@ public:
}
};
/// Implementation of the TempAllocator that tries to allocate from a large preallocated block, but falls back to malloc when it is exhausted
class JPH_EXPORT TempAllocatorImplWithMallocFallback final : public TempAllocator
{
public:
JPH_OVERRIDE_NEW_DELETE
/// Constructs the allocator with an initial fixed block if inSize
explicit TempAllocatorImplWithMallocFallback(uint inSize) :
mAllocator(inSize)
{
}
// See: TempAllocator
virtual void * Allocate(uint inSize) override
{
if (mAllocator.CanAllocate(inSize))
return mAllocator.Allocate(inSize);
else
return mFallbackAllocator.Allocate(inSize);
}
// See: TempAllocator
virtual void Free(void *inAddress, uint inSize) override
{
if (inAddress == nullptr)
{
JPH_ASSERT(inSize == 0);
}
else
{
if (mAllocator.OwnsMemory(inAddress))
mAllocator.Free(inAddress, inSize);
else
mFallbackAllocator.Free(inAddress, inSize);
}
}
private:
TempAllocatorImpl mAllocator;
TempAllocatorMalloc mFallbackAllocator;
};
JPH_NAMESPACE_END
@@ -255,7 +255,7 @@ private:
#endif
const Positions & mPositions; ///< List of positions (some of them are part of the hull)
Faces mFaces; ///< List of faces that are part of the hull (if !mRemoved)
Faces mFaces; ///< List of faces that are part of the hull (if !mRemoved)
struct Coplanar
{
@@ -692,9 +692,9 @@ public:
#endif
private:
TriangleFactory mFactory; ///< Factory to create new triangles and remove old ones
TriangleFactory mFactory; ///< Factory to create new triangles and remove old ones
const Points & mPositions; ///< List of positions (some of them are part of the hull)
TriangleQueue mTriangleQueue; ///< List of triangles that are part of the hull that still need to be checked (if !mRemoved)
TriangleQueue mTriangleQueue; ///< List of triangles that are part of the hull that still need to be checked (if !mRemoved)
#if defined(JPH_EPA_CONVEX_BUILDER_VALIDATE) || defined(JPH_EPA_CONVEX_BUILDER_DRAW)
Triangles mTriangles; ///< The list of all triangles in this hull (for debug purposes)
+1 -7
View File
@@ -73,7 +73,7 @@ private:
}
#ifdef JPH_GJK_DEBUG
Trace("GetClosest: set = 0b%s, v = [%s], |v| = %g", NibbleToBinary(set), ConvertToString(v).c_str(), (double)v.Length());
Trace("GetClosest: set = 0b%s, v = [%s], |v| = %g", NibbleToBinary(set), ConvertToString(v).c_str(), (double)v.Length());
#endif
float v_len_sq = v.LengthSq();
@@ -115,10 +115,6 @@ private:
mNumPoints = num_points;
}
// GCC 11.3 thinks the assignments to mP, mQ and mY below may use uninitialized variables
JPH_SUPPRESS_WARNING_PUSH
JPH_GCC_SUPPRESS_WARNING("-Wmaybe-uninitialized")
// Remove points that are not in the set, only updates mP
void UpdatePointSetP(uint32 inSet)
{
@@ -161,8 +157,6 @@ private:
mNumPoints = num_points;
}
JPH_SUPPRESS_WARNING_POP
// Calculate closest points on A and B
void CalculatePointAAndB(Vec3 &outPointA, Vec3 &outPointB) const
{
+1 -1
View File
@@ -26,7 +26,7 @@ public:
}
// Properties
inline Vec3 GetCenter() const { return Vec3::sLoadFloat3Unsafe(mCenter); }
inline Vec3 GetCenter() const { return Vec3::sLoadFloat3Unsafe(mCenter); }
inline float GetRadius() const { return mRadius; }
/// Test if two spheres overlap
+28 -3
View File
@@ -499,7 +499,14 @@ endif()
target_include_directories(Jolt PUBLIC
$<BUILD_INTERFACE:${PHYSICS_REPO_ROOT}>
$<INSTALL_INTERFACE:include/>)
target_precompile_headers(Jolt PRIVATE ${JOLT_PHYSICS_ROOT}/Jolt.h)
# Code coverage doesn't work when using precompiled headers
target_precompile_headers(Jolt PRIVATE "$<$<NOT:$<CONFIG:ReleaseCoverage>>:${JOLT_PHYSICS_ROOT}/Jolt.h>")
if (NOT CPP_EXCEPTIONS_ENABLED)
# Disable use of exceptions in MSVC's STL
target_compile_definitions(Jolt PUBLIC $<$<BOOL:${MSVC}>:_HAS_EXCEPTIONS=0>)
endif()
# Set the debug/non-debug build flags
target_compile_definitions(Jolt PUBLIC "$<$<CONFIG:Debug>:_DEBUG>")
@@ -619,12 +626,19 @@ else()
# XCode builds for multiple architectures, we can't set global flags
elseif (CROSS_COMPILE_ARM OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64")
# ARM64 uses no special commandline flags
elseif (EMSCRIPTEN)
if (USE_WASM_SIMD)
# Jolt currently doesn't implement the WASM specific SIMD intrinsics so uses the SSE 4.2 intrinsics
# See: https://emscripten.org/docs/porting/simd.html#webassembly-simd-intrinsics
# Note that this does not require the browser to actually support SSE 4.2 it merely means that it can translate those instructions to WASM SIMD instructions
target_compile_options(Jolt PUBLIC -msimd128 -msse4.2)
endif()
elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i386")
# x86 and x86_64
# On 32-bit builds we need to default to using SSE instructions, the x87 FPU instructions have higher intermediate precision
# On 32-bit builds we need to default to using SSE instructions, the x87 FPU instructions have higher intermediate precision
# which will cause problems in the collision detection code (the effect is similar to leaving FMA on, search for
# JPH_PRECISE_MATH_ON for the locations where this is a problem).
if (USE_AVX512)
target_compile_options(Jolt PUBLIC -mavx512f -mavx512vl -mavx512dq -mavx2 -mbmi -mpopcnt -mlzcnt -mf16c)
elseif (USE_AVX2)
@@ -658,3 +672,14 @@ else()
EMIT_X86_INSTRUCTION_SET_DEFINITIONS()
endif()
endif()
# On Unix flavors we need the pthread library
if (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows") AND NOT EMSCRIPTEN)
target_compile_options(Jolt PUBLIC -pthread)
endif()
if (EMSCRIPTEN)
# We need more than the default 64KB stack and 16MB memory
# Also disable warning: running limited binaryen optimizations because DWARF info requested (or indirectly required)
target_link_options(Jolt PUBLIC -sSTACK_SIZE=1048576 -sINITIAL_MEMORY=134217728 -Wno-limited-postlink-optimizations)
endif()
+1 -1
View File
@@ -73,7 +73,7 @@ public:
/// Prepare to convert to float vector 3 rounding towards zero (returns DVec3 that can be converted to a Vec3 to get the rounding)
JPH_INLINE DVec3 PrepareRoundToZero() const;
/// Prepare to convert to float vector 3 rounding towards positive/negative inf (returns DVec3 that can be converted to a Vec3 to get the rounding)
/// Prepare to convert to float vector 3 rounding towards positive/negative inf (returns DVec3 that can be converted to a Vec3 to get the rounding)
JPH_INLINE DVec3 PrepareRoundToInf() const;
/// Convert to float vector 3 rounding down
+38 -32
View File
@@ -44,7 +44,7 @@ DVec3::DVec3(double inX, double inY, double inZ)
mValue.mLow = _mm_set_pd(inY, inX);
mValue.mHigh = _mm_set1_pd(inZ);
#elif defined(JPH_USE_NEON)
mValue.val[0] = vcombine_f64(vcreate_f64(*reinterpret_cast<uint64 *>(&inX)), vcreate_f64(*reinterpret_cast<uint64 *>(&inY)));
mValue.val[0] = vcombine_f64(vcreate_f64(BitCast<uint64>(inX)), vcreate_f64(BitCast<uint64>(inY)));
mValue.val[1] = vdupq_n_f64(inZ);
#else
mF64[0] = inX;
@@ -232,7 +232,7 @@ DVec3 DVec3::sEquals(DVec3Arg inV1, DVec3Arg inV2)
#elif defined(JPH_USE_SSE)
return DVec3({ _mm_cmpeq_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmpeq_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) });
#elif defined(JPH_USE_NEON)
return DVec3({ vreinterpretq_u64_f64(vceqq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vceqq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
return DVec3({ vreinterpretq_f64_u64(vceqq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_f64_u64(vceqq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
#else
return DVec3(inV1.mF64[0] == inV2.mF64[0]? cTrue : cFalse,
inV1.mF64[1] == inV2.mF64[1]? cTrue : cFalse,
@@ -247,7 +247,7 @@ DVec3 DVec3::sLess(DVec3Arg inV1, DVec3Arg inV2)
#elif defined(JPH_USE_SSE)
return DVec3({ _mm_cmplt_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmplt_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) });
#elif defined(JPH_USE_NEON)
return DVec3({ vreinterpretq_u64_f64(vcltq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcltq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
return DVec3({ vreinterpretq_f64_u64(vcltq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_f64_u64(vcltq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
#else
return DVec3(inV1.mF64[0] < inV2.mF64[0]? cTrue : cFalse,
inV1.mF64[1] < inV2.mF64[1]? cTrue : cFalse,
@@ -262,7 +262,7 @@ DVec3 DVec3::sLessOrEqual(DVec3Arg inV1, DVec3Arg inV2)
#elif defined(JPH_USE_SSE)
return DVec3({ _mm_cmple_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmple_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) });
#elif defined(JPH_USE_NEON)
return DVec3({ vreinterpretq_u64_f64(vcleq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcleq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
return DVec3({ vreinterpretq_f64_u64(vcleq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_f64_u64(vcleq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
#else
return DVec3(inV1.mF64[0] <= inV2.mF64[0]? cTrue : cFalse,
inV1.mF64[1] <= inV2.mF64[1]? cTrue : cFalse,
@@ -277,7 +277,7 @@ DVec3 DVec3::sGreater(DVec3Arg inV1, DVec3Arg inV2)
#elif defined(JPH_USE_SSE)
return DVec3({ _mm_cmpgt_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmpgt_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) });
#elif defined(JPH_USE_NEON)
return DVec3({ vreinterpretq_u64_f64(vcgtq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcgtq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
return DVec3({ vreinterpretq_f64_u64(vcgtq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_f64_u64(vcgtq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
#else
return DVec3(inV1.mF64[0] > inV2.mF64[0]? cTrue : cFalse,
inV1.mF64[1] > inV2.mF64[1]? cTrue : cFalse,
@@ -292,7 +292,7 @@ DVec3 DVec3::sGreaterOrEqual(DVec3Arg inV1, DVec3Arg inV2)
#elif defined(JPH_USE_SSE)
return DVec3({ _mm_cmpge_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_cmpge_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) });
#elif defined(JPH_USE_NEON)
return DVec3({ vreinterpretq_u64_f64(vcgeq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_u64_f64(vcgeq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
return DVec3({ vreinterpretq_f64_u64(vcgeq_f64(inV1.mValue.val[0], inV2.mValue.val[0])), vreinterpretq_f64_u64(vcgeq_f64(inV1.mValue.val[1], inV2.mValue.val[1])) });
#else
return DVec3(inV1.mF64[0] >= inV2.mF64[0]? cTrue : cFalse,
inV1.mF64[1] >= inV2.mF64[1]? cTrue : cFalse,
@@ -323,7 +323,8 @@ DVec3 DVec3::sSelect(DVec3Arg inV1, DVec3Arg inV2, DVec3Arg inControl)
Type v = { _mm_blendv_pd(inV1.mValue.mLow, inV2.mValue.mLow, inControl.mValue.mLow), _mm_blendv_pd(inV1.mValue.mHigh, inV2.mValue.mHigh, inControl.mValue.mHigh) };
return sFixW(v);
#elif defined(JPH_USE_NEON)
Type v = { vbslq_f64(vshrq_n_s64(inControl.mValue.val[0], 63), inV2.mValue.val[0], inV1.mValue.val[0]), vbslq_f64(vshrq_n_s64(inControl.mValue.val[1], 63), inV2.mValue.val[1], inV1.mValue.val[1]) };
Type v = { vbslq_f64(vreinterpretq_u64_s64(vshrq_n_s64(vreinterpretq_s64_f64(inControl.mValue.val[0]), 63)), inV2.mValue.val[0], inV1.mValue.val[0]),
vbslq_f64(vreinterpretq_u64_s64(vshrq_n_s64(vreinterpretq_s64_f64(inControl.mValue.val[1]), 63)), inV2.mValue.val[1], inV1.mValue.val[1]) };
return sFixW(v);
#else
DVec3 result;
@@ -343,7 +344,8 @@ DVec3 DVec3::sOr(DVec3Arg inV1, DVec3Arg inV2)
#elif defined(JPH_USE_SSE)
return DVec3({ _mm_or_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_or_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) });
#elif defined(JPH_USE_NEON)
return DVec3({ vorrq_s64(inV1.mValue.val[0], inV2.mValue.val[0]), vorrq_s64(inV1.mValue.val[1], inV2.mValue.val[1]) });
return DVec3({ vreinterpretq_f64_u64(vorrq_u64(vreinterpretq_u64_f64(inV1.mValue.val[0]), vreinterpretq_u64_f64(inV2.mValue.val[0]))),
vreinterpretq_f64_u64(vorrq_u64(vreinterpretq_u64_f64(inV1.mValue.val[1]), vreinterpretq_u64_f64(inV2.mValue.val[1]))) });
#else
return DVec3(BitCast<double>(BitCast<uint64>(inV1.mF64[0]) | BitCast<uint64>(inV2.mF64[0])),
BitCast<double>(BitCast<uint64>(inV1.mF64[1]) | BitCast<uint64>(inV2.mF64[1])),
@@ -358,7 +360,8 @@ DVec3 DVec3::sXor(DVec3Arg inV1, DVec3Arg inV2)
#elif defined(JPH_USE_SSE)
return DVec3({ _mm_xor_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_xor_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) });
#elif defined(JPH_USE_NEON)
return DVec3({ veorq_s64(inV1.mValue.val[0], inV2.mValue.val[0]), veorq_s64(inV1.mValue.val[1], inV2.mValue.val[1]) });
return DVec3({ vreinterpretq_f64_u64(veorq_u64(vreinterpretq_u64_f64(inV1.mValue.val[0]), vreinterpretq_u64_f64(inV2.mValue.val[0]))),
vreinterpretq_f64_u64(veorq_u64(vreinterpretq_u64_f64(inV1.mValue.val[1]), vreinterpretq_u64_f64(inV2.mValue.val[1]))) });
#else
return DVec3(BitCast<double>(BitCast<uint64>(inV1.mF64[0]) ^ BitCast<uint64>(inV2.mF64[0])),
BitCast<double>(BitCast<uint64>(inV1.mF64[1]) ^ BitCast<uint64>(inV2.mF64[1])),
@@ -373,7 +376,8 @@ DVec3 DVec3::sAnd(DVec3Arg inV1, DVec3Arg inV2)
#elif defined(JPH_USE_SSE)
return DVec3({ _mm_and_pd(inV1.mValue.mLow, inV2.mValue.mLow), _mm_and_pd(inV1.mValue.mHigh, inV2.mValue.mHigh) });
#elif defined(JPH_USE_NEON)
return DVec3({ vandq_s64(inV1.mValue.val[0], inV2.mValue.val[0]), vandq_s64(inV1.mValue.val[1], inV2.mValue.val[1]) });
return DVec3({ vreinterpretq_f64_u64(vandq_u64(vreinterpretq_u64_f64(inV1.mValue.val[0]), vreinterpretq_u64_f64(inV2.mValue.val[0]))),
vreinterpretq_f64_u64(vandq_u64(vreinterpretq_u64_f64(inV1.mValue.val[1]), vreinterpretq_u64_f64(inV2.mValue.val[1]))) });
#else
return DVec3(BitCast<double>(BitCast<uint64>(inV1.mF64[0]) & BitCast<uint64>(inV2.mF64[0])),
BitCast<double>(BitCast<uint64>(inV1.mF64[1]) & BitCast<uint64>(inV2.mF64[1])),
@@ -730,11 +734,11 @@ DVec3 DVec3::Cross(DVec3Arg inV2) const
{
#if defined(JPH_USE_AVX2)
__m256d t1 = _mm256_permute4x64_pd(inV2.mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
t1 = _mm256_mul_pd(t1, mValue);
__m256d t2 = _mm256_permute4x64_pd(mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
t2 = _mm256_mul_pd(t2, inV2.mValue);
__m256d t3 = _mm256_sub_pd(t1, t2);
return _mm256_permute4x64_pd(t3, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
t1 = _mm256_mul_pd(t1, mValue);
__m256d t2 = _mm256_permute4x64_pd(mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
t2 = _mm256_mul_pd(t2, inV2.mValue);
__m256d t3 = _mm256_sub_pd(t1, t2);
return _mm256_permute4x64_pd(t3, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
#else
return DVec3(mF64[1] * inV2.mF64[2] - mF64[2] * inV2.mF64[1],
mF64[2] * inV2.mF64[0] - mF64[0] * inV2.mF64[2],
@@ -746,10 +750,10 @@ double DVec3::Dot(DVec3Arg inV2) const
{
#if defined(JPH_USE_AVX)
__m256d mul = _mm256_mul_pd(mValue, inV2.mValue);
__m128d xy = _mm256_castpd256_pd128(mul);
__m128d xy = _mm256_castpd256_pd128(mul);
__m128d yx = _mm_shuffle_pd(xy, xy, 1);
__m128d sum = _mm_add_pd(xy, yx);
__m128d zw = _mm256_extractf128_pd(mul, 1);
__m128d zw = _mm256_extractf128_pd(mul, 1);
sum = _mm_add_pd(sum, zw);
return _mm_cvtsd_f64(sum);
#elif defined(JPH_USE_SSE)
@@ -760,9 +764,9 @@ double DVec3::Dot(DVec3Arg inV2) const
sum = _mm_add_pd(sum, z);
return _mm_cvtsd_f64(sum);
#elif defined(JPH_USE_NEON)
float64x2_t mul_low = vmulq_f64(mValue.val[0], inV2.mValue.val[0]);
float64x2_t mul_high = vmulq_f64(mValue.val[1], inV2.mValue.val[1]);
return vaddvq_f64(mul_low) + vgetq_lane_f64(mul_high, 0);
float64x2_t mul_low = vmulq_f64(mValue.val[0], inV2.mValue.val[0]);
float64x2_t mul_high = vmulq_f64(mValue.val[1], inV2.mValue.val[1]);
return vaddvq_f64(mul_low) + vgetq_lane_f64(mul_high, 0);
#else
double dot = 0.0;
for (int i = 0; i < 3; i++)
@@ -830,9 +834,10 @@ DVec3 DVec3::GetSign() const
__m128d one = _mm_set1_pd(1.0);
return DVec3({ _mm_or_pd(_mm_and_pd(mValue.mLow, minus_one), one), _mm_or_pd(_mm_and_pd(mValue.mHigh, minus_one), one) });
#elif defined(JPH_USE_NEON)
float64x2_t minus_one = vdupq_n_f64(-1.0f);
float64x2_t one = vdupq_n_f64(1.0f);
return DVec3({ vorrq_s64(vandq_s64(mValue.val[0], minus_one), one), vorrq_s64(vandq_s64(mValue.val[1], minus_one), one) });
uint64x2_t minus_one = vreinterpretq_u64_f64(vdupq_n_f64(-1.0f));
uint64x2_t one = vreinterpretq_u64_f64(vdupq_n_f64(1.0f));
return DVec3({ vreinterpretq_f64_u64(vorrq_u64(vandq_u64(vreinterpretq_u64_f64(mValue.val[0]), minus_one), one)),
vreinterpretq_f64_u64(vorrq_u64(vandq_u64(vreinterpretq_u64_f64(mValue.val[1]), minus_one), one)) });
#else
return DVec3(std::signbit(mF64[0])? -1.0 : 1.0,
std::signbit(mF64[1])? -1.0 : 1.0,
@@ -851,8 +856,9 @@ DVec3 DVec3::PrepareRoundToZero() const
__m128d mask = _mm_castsi128_pd(_mm_set1_epi64x(int64_t(~cDoubleToFloatMantissaLoss)));
return DVec3({ _mm_and_pd(mValue.mLow, mask), _mm_and_pd(mValue.mHigh, mask) });
#elif defined(JPH_USE_NEON)
float64x2_t mask = vreinterpretq_f64_u64(vdupq_n_u64(~cDoubleToFloatMantissaLoss));
return DVec3({ vandq_s64(mValue.val[0], mask), vandq_s64(mValue.val[1], mask) });
uint64x2_t mask = vdupq_n_u64(~cDoubleToFloatMantissaLoss);
return DVec3({ vreinterpretq_f64_u64(vandq_u64(vreinterpretq_u64_f64(mValue.val[0]), mask)),
vreinterpretq_f64_u64(vandq_u64(vreinterpretq_u64_f64(mValue.val[1]), mask)) });
#else
double x = BitCast<double>(BitCast<uint64>(mF64[0]) & ~cDoubleToFloatMantissaLoss);
double y = BitCast<double>(BitCast<uint64>(mF64[1]) & ~cDoubleToFloatMantissaLoss);
@@ -889,15 +895,15 @@ DVec3 DVec3::PrepareRoundToInf() const
__m128d value_or_mantissa_loss_high = _mm_or_pd(mValue.mHigh, _mm_castsi128_pd(mantissa_loss));
return DVec3({ _mm_blendv_pd(value_or_mantissa_loss_low, mValue.mLow, is_zero_low), _mm_blendv_pd(value_or_mantissa_loss_high, mValue.mHigh, is_zero_high) });
#elif defined(JPH_USE_NEON)
float64x2_t mantissa_loss = vreinterpretq_f64_u64(vdupq_n_u64(cDoubleToFloatMantissaLoss));
uint64x2_t mantissa_loss = vdupq_n_u64(cDoubleToFloatMantissaLoss);
float64x2_t zero = vdupq_n_f64(0.0);
float64x2_t value_and_mantissa_loss_low = vandq_s64(mValue.val[0], mantissa_loss);
float64x2_t is_zero_low = vceqq_f64(value_and_mantissa_loss_low, zero);
float64x2_t value_or_mantissa_loss_low = vorrq_s64(mValue.val[0], mantissa_loss);
float64x2_t value_and_mantissa_loss_high = vandq_s64(mValue.val[1], mantissa_loss);
float64x2_t value_and_mantissa_loss_low = vreinterpretq_f64_u64(vandq_u64(vreinterpretq_u64_f64(mValue.val[0]), mantissa_loss));
uint64x2_t is_zero_low = vceqq_f64(value_and_mantissa_loss_low, zero);
float64x2_t value_or_mantissa_loss_low = vreinterpretq_f64_u64(vorrq_u64(vreinterpretq_u64_f64(mValue.val[0]), mantissa_loss));
float64x2_t value_and_mantissa_loss_high = vreinterpretq_f64_u64(vandq_u64(vreinterpretq_u64_f64(mValue.val[1]), mantissa_loss));
float64x2_t value_low = vbslq_f64(is_zero_low, mValue.val[0], value_or_mantissa_loss_low);
float64x2_t is_zero_high = vceqq_f64(value_and_mantissa_loss_high, zero);
float64x2_t value_or_mantissa_loss_high = vorrq_s64(mValue.val[1], mantissa_loss);
uint64x2_t is_zero_high = vceqq_f64(value_and_mantissa_loss_high, zero);
float64x2_t value_or_mantissa_loss_high = vreinterpretq_f64_u64(vorrq_u64(vreinterpretq_u64_f64(mValue.val[1]), mantissa_loss));
float64x2_t value_high = vbslq_f64(is_zero_high, mValue.val[1], value_or_mantissa_loss_high);
return DVec3({ value_low, value_high });
#else
+41 -39
View File
@@ -4,7 +4,7 @@
#pragma once
#include <Jolt/Core/FPFlushDenormals.h>
#include <Jolt/Core/FPException.h>
JPH_NAMESPACE_BEGIN
@@ -28,9 +28,9 @@ JPH_NAMESPACE_BEGIN
template <class Vector, class Matrix>
bool EigenValueSymmetric(const Matrix &inMatrix, Matrix &outEigVec, Vector &outEigVal)
{
// This algorithm works with very small numbers and can trigger invalid float exceptions when not flushing denormals
FPFlushDenormals flush_denormals;
(void)flush_denormals;
// This algorithm can generate infinite values, see comment below
FPExceptionDisableInvalid disable_invalid;
(void)disable_invalid;
// Maximum number of sweeps to make
const int cMaxSweeps = 50;
@@ -70,9 +70,10 @@ bool EigenValueSymmetric(const Matrix &inMatrix, Matrix &outEigVec, Vector &outE
for (uint ip = 0; ip < n - 1; ++ip)
for (uint iq = ip + 1; iq < n; ++iq)
sm += abs(a(ip, iq));
float avg_sm = sm / Square(n);
// Normal return, convergence to machine underflow
if (sm == 0.0f)
if (avg_sm < FLT_MIN) // Original code: sm == 0.0f, when the average is denormal, we also consider it machine underflow
{
// Sanity checks
#ifdef JPH_ENABLE_ASSERTS
@@ -93,68 +94,69 @@ bool EigenValueSymmetric(const Matrix &inMatrix, Matrix &outEigVec, Vector &outE
}
// On the first three sweeps use a fraction of the sum of the off diagonal elements as threshold
float tresh = sweep < 4? 0.2f * sm / Square(n) : 0.0f;
// Note that we pick a minimum threshold of FLT_MIN because dividing by a denormalized number is likely to result in infinity.
float tresh = sweep < 4? 0.2f * avg_sm : FLT_MIN; // Original code: 0.0f instead of FLT_MIN
for (uint ip = 0; ip < n - 1; ++ip)
for (uint iq = ip + 1; iq < n; ++iq)
{
float g = 100.0f * abs(a(ip, iq));
float &a_pq = a(ip, iq);
float &eigval_p = outEigVal[ip];
float &eigval_q = outEigVal[iq];
float abs_a_pq = abs(a_pq);
float g = 100.0f * abs_a_pq;
// After four sweeps, skip the rotation if the off-diagonal element is small
if (sweep > 4
&& abs(outEigVal[ip]) + g == abs(outEigVal[ip])
&& abs(outEigVal[iq]) + g == abs(outEigVal[iq]))
&& abs(eigval_p) + g == abs(eigval_p)
&& abs(eigval_q) + g == abs(eigval_q))
{
a(ip, iq) = 0.0f;
a_pq = 0.0f;
}
else if (abs(a(ip, iq)) > tresh)
else if (abs_a_pq > tresh)
{
float h = outEigVal[iq] - outEigVal[ip];
float h = eigval_q - eigval_p;
float abs_h = abs(h);
float t;
if (abs(h) + g == abs(h))
if (abs_h + g == abs_h)
{
t = a(ip, iq) / h;
t = a_pq / h;
}
else
{
float theta = 0.5f * h / a(ip, iq); // Warning: Can become inf if a(ip, iq) too small
t = 1.0f / (abs(theta) + sqrt(1.0f + theta * theta)); // Warning: Squaring large value can make it inf
float theta = 0.5f * h / a_pq; // Warning: Can become infinite if a(ip, iq) is very small which may trigger an invalid float exception
t = 1.0f / (abs(theta) + sqrt(1.0f + theta * theta)); // If theta becomes inf, t will be 0 so the infinite is not a problem for the algorithm
if (theta < 0.0f) t = -t;
}
float c = 1.0f / sqrt(1.0f + t * t);
float s = t * c;
float tau = s / (1.0f + c);
h = t * a(ip, iq);
h = t * a_pq;
a(ip, iq) = 0.0f;
a_pq = 0.0f;
// !Modification from Numerical Recipes!
// h can become infinite due to numerical overflow, this only happens when a(ip, iq) is very small
// so we can safely set a(ip, iq) to zero and skip the rotation, see lines marked with 'Warning' above.
if (!isnan(h))
{
z[ip] -= h;
z[iq] += h;
z[ip] -= h;
z[iq] += h;
outEigVal[ip] -= h;
outEigVal[iq] += h;
eigval_p -= h;
eigval_q += h;
#define JPH_EVS_ROTATE(a, i, j, k, l) \
g = a(i, j), \
h = a(k, l), \
a(i, j) = g - s * (h + g * tau), \
a(k, l) = h + s * (g - h * tau)
#define JPH_EVS_ROTATE(a, i, j, k, l) \
g = a(i, j), \
h = a(k, l), \
a(i, j) = g - s * (h + g * tau), \
a(k, l) = h + s * (g - h * tau)
uint j;
for (j = 0; j < ip; ++j) JPH_EVS_ROTATE(a, j, ip, j, iq);
for (j = ip + 1; j < iq; ++j) JPH_EVS_ROTATE(a, ip, j, j, iq);
for (j = iq + 1; j < n; ++j) JPH_EVS_ROTATE(a, ip, j, iq, j);
for (j = 0; j < n; ++j) JPH_EVS_ROTATE(outEigVec, j, ip, j, iq);
uint j;
for (j = 0; j < ip; ++j) JPH_EVS_ROTATE(a, j, ip, j, iq);
for (j = ip + 1; j < iq; ++j) JPH_EVS_ROTATE(a, ip, j, j, iq);
for (j = iq + 1; j < n; ++j) JPH_EVS_ROTATE(a, ip, j, iq, j);
for (j = 0; j < n; ++j) JPH_EVS_ROTATE(outEigVec, j, ip, j, iq);
#undef JPH_EVS_ROTATE
}
#undef JPH_EVS_ROTATE
}
}
+1 -1
View File
@@ -193,7 +193,7 @@ JPH_INLINE Vec4 ToFloat(UVec4Arg inValue)
#if defined(JPH_USE_F16C)
return _mm_cvtph_ps(inValue.mValue);
#elif defined(JPH_USE_NEON)
return vcvt_f32_f16(vreinterpret_f16_f32(vget_low_f32(inValue.mValue)));
return vcvt_f32_f16(vreinterpret_f16_u32(vget_low_u32(inValue.mValue)));
#else
return ToFloatFallback(inValue);
#endif
+3 -1
View File
@@ -116,7 +116,9 @@ inline uint CountTrailingZeros(uint32 inValue)
_BitScanForward(&result, inValue);
return result;
#else
return __builtin_clz(__builtin_bitreverse32(inValue));
if (inValue == 0)
return 32;
return __builtin_ctz(inValue);
#endif
#elif defined(JPH_CPU_E2K)
return inValue ? __builtin_ctz(inValue) : 32;
+1 -1
View File
@@ -81,7 +81,7 @@ public:
JPH_INLINE Vec3 GetXYZ() const { return Vec3(mValue); }
/// Get the quaternion as a Vec4
JPH_INLINE Vec4 GetXYZW() const { return mValue; }
JPH_INLINE Vec4 GetXYZW() const { return mValue; }
/// Set individual components
JPH_INLINE void SetX(float inX) { mValue.SetX(inX); }
+4 -4
View File
@@ -73,11 +73,11 @@ Quat Quat::operator * (QuatArg inRHS) const
Quat Quat::sRotation(Vec3Arg inAxis, float inAngle)
{
// returns [inAxis * sin(0.5f * inAngle), cos(0.5f * inAngle)]
// returns [inAxis * sin(0.5f * inAngle), cos(0.5f * inAngle)]
JPH_ASSERT(inAxis.IsNormalized());
Vec4 s, c;
Vec4::sReplicate(0.5f * inAngle).SinCos(s, c);
return Quat(Vec4::sSelect(Vec4(inAxis) * s, c, UVec4(0, 0, 0, 0xffffffffU)));
return Quat(Vec4::sSelect(Vec4(inAxis) * s, c, UVec4(0, 0, 0, 0xffffffffU)));
}
void Quat::GetAxisAngle(Vec3 &outAxis, float &outAngle) const
@@ -237,12 +237,12 @@ Quat Quat::LERP(QuatArg inDestination, float inFraction) const
Quat Quat::SLERP(QuatArg inDestination, float inFraction) const
{
// Difference at which to LERP instead of SLERP
// Difference at which to LERP instead of SLERP
const float delta = 0.0001f;
// Calc cosine
float sign_scale1 = 1.0f;
float cos_omega = Dot(inDestination);
float cos_omega = Dot(inDestination);
// Adjust signs (if necessary)
if (cos_omega < 0.0f)
+1 -1
View File
@@ -26,7 +26,7 @@ using RMat44Arg = DMat44Arg;
// Define real to float
using Real = float;
using Real3 = Float3;
using RVec3 = Vec3;
using RVec3 = Vec3;
using RVec3Arg = Vec3Arg;
using RMat44 = Mat44;
using RMat44Arg = Mat44Arg;
+24 -19
View File
@@ -36,7 +36,7 @@ UVec4 UVec4::Swizzle() const
#if defined(JPH_USE_SSE)
return _mm_shuffle_epi32(mValue, _MM_SHUFFLE(SwizzleW, SwizzleZ, SwizzleY, SwizzleX));
#elif defined(JPH_USE_NEON)
return JPH_NEON_SHUFFLE_F32x4(mValue, mValue, SwizzleX, SwizzleY, SwizzleZ, SwizzleW);
return JPH_NEON_SHUFFLE_U32x4(mValue, mValue, SwizzleX, SwizzleY, SwizzleZ, SwizzleW);
#else
return UVec4(mU32[SwizzleX], mU32[SwizzleY], mU32[SwizzleZ], mU32[SwizzleW]);
#endif
@@ -103,7 +103,12 @@ UVec4 UVec4::sGatherInt4(const uint32 *inBase, UVec4Arg inOffsets)
#ifdef JPH_USE_AVX2
return _mm_i32gather_epi32(reinterpret_cast<const int *>(inBase), inOffsets.mValue, Scale);
#else
return Vec4::sGatherFloat4<Scale>(reinterpret_cast<const float *>(inBase), inOffsets).ReinterpretAsInt();
const uint8 *base = reinterpret_cast<const uint8 *>(inBase);
uint32 x = *reinterpret_cast<const uint32 *>(base + inOffsets.GetX() * Scale);
uint32 y = *reinterpret_cast<const uint32 *>(base + inOffsets.GetY() * Scale);
uint32 z = *reinterpret_cast<const uint32 *>(base + inOffsets.GetZ() * Scale);
uint32 w = *reinterpret_cast<const uint32 *>(base + inOffsets.GetW() * Scale);
return UVec4(x, y, z, w);
#endif
}
@@ -154,7 +159,7 @@ UVec4 UVec4::sSelect(UVec4Arg inV1, UVec4Arg inV2, UVec4Arg inControl)
#if defined(JPH_USE_SSE4_1)
return _mm_castps_si128(_mm_blendv_ps(_mm_castsi128_ps(inV1.mValue), _mm_castsi128_ps(inV2.mValue), _mm_castsi128_ps(inControl.mValue)));
#elif defined(JPH_USE_NEON)
return vbslq_u32(vshrq_n_s32(inControl.mValue, 31), inV2.mValue, inV1.mValue);
return vbslq_u32(vreinterpretq_u32_s32(vshrq_n_s32(vreinterpretq_s32_u32(inControl.mValue), 31)), inV2.mValue, inV1.mValue);
#else
UVec4 result;
for (int i = 0; i < 4; i++)
@@ -323,7 +328,7 @@ Vec4 UVec4::ToFloat() const
#if defined(JPH_USE_SSE)
return _mm_cvtepi32_ps(mValue);
#elif defined(JPH_USE_NEON)
return vcvtq_f32_s32(mValue);
return vcvtq_f32_u32(mValue);
#else
return Vec4((float)mU32[0], (float)mU32[1], (float)mU32[2], (float)mU32[3]);
#endif
@@ -334,7 +339,7 @@ Vec4 UVec4::ReinterpretAsFloat() const
#if defined(JPH_USE_SSE)
return Vec4(_mm_castsi128_ps(mValue));
#elif defined(JPH_USE_NEON)
return vreinterpretq_f32_s32(mValue);
return vreinterpretq_f32_u32(mValue);
#else
return *reinterpret_cast<const Vec4 *>(this);
#endif
@@ -369,7 +374,7 @@ int UVec4::CountTrues() const
#if defined(JPH_USE_SSE)
return CountBits(_mm_movemask_ps(_mm_castsi128_ps(mValue)));
#elif defined(JPH_USE_NEON)
return vaddvq_u32(vshrq_n_u32(mValue, 31));
return vaddvq_u32(vshrq_n_u32(mValue, 31));
#else
return (mU32[0] >> 31) + (mU32[1] >> 31) + (mU32[2] >> 31) + (mU32[3] >> 31);
#endif
@@ -380,8 +385,8 @@ int UVec4::GetTrues() const
#if defined(JPH_USE_SSE)
return _mm_movemask_ps(_mm_castsi128_ps(mValue));
#elif defined(JPH_USE_NEON)
int32x4_t shift = JPH_NEON_INT32x4(0, 1, 2, 3);
return vaddvq_u32(vshlq_u32(vshrq_n_u32(mValue, 31), shift));
int32x4_t shift = JPH_NEON_INT32x4(0, 1, 2, 3);
return vaddvq_u32(vshlq_u32(vshrq_n_u32(mValue, 31), shift));
#else
return (mU32[0] >> 31) | ((mU32[1] >> 31) << 1) | ((mU32[2] >> 31) << 2) | ((mU32[3] >> 31) << 3);
#endif
@@ -443,7 +448,7 @@ UVec4 UVec4::ArithmeticShiftRight() const
#if defined(JPH_USE_SSE)
return _mm_srai_epi32(mValue, Count);
#elif defined(JPH_USE_NEON)
return vshrq_n_s32(mValue, Count);
return vreinterpretq_u32_s32(vshrq_n_s32(vreinterpretq_s32_u32(mValue), Count));
#else
return UVec4(uint32(int32_t(mU32[0]) >> Count),
uint32(int32_t(mU32[1]) >> Count),
@@ -457,9 +462,9 @@ UVec4 UVec4::Expand4Uint16Lo() const
#if defined(JPH_USE_SSE)
return _mm_unpacklo_epi16(mValue, _mm_castps_si128(_mm_setzero_ps()));
#elif defined(JPH_USE_NEON)
int16x4_t value = vget_low_s16(mValue);
int16x4_t zero = vdup_n_s16(0);
return vcombine_s16(vzip1_s16(value, zero), vzip2_s16(value, zero));
uint16x4_t value = vget_low_u16(vreinterpretq_u16_u32(mValue));
uint16x4_t zero = vdup_n_u16(0);
return vreinterpretq_u32_u16(vcombine_u16(vzip1_u16(value, zero), vzip2_u16(value, zero)));
#else
return UVec4(mU32[0] & 0xffff,
(mU32[0] >> 16) & 0xffff,
@@ -473,9 +478,9 @@ UVec4 UVec4::Expand4Uint16Hi() const
#if defined(JPH_USE_SSE)
return _mm_unpackhi_epi16(mValue, _mm_castps_si128(_mm_setzero_ps()));
#elif defined(JPH_USE_NEON)
int16x4_t value = vget_high_s16(mValue);
int16x4_t zero = vdup_n_s16(0);
return vcombine_s16(vzip1_s16(value, zero), vzip2_s16(value, zero));
uint16x4_t value = vget_high_u16(vreinterpretq_u16_u32(mValue));
uint16x4_t zero = vdup_n_u16(0);
return vreinterpretq_u32_u16(vcombine_u16(vzip1_u16(value, zero), vzip2_u16(value, zero)));
#else
return UVec4(mU32[2] & 0xffff,
(mU32[2] >> 16) & 0xffff,
@@ -489,7 +494,7 @@ UVec4 UVec4::Expand4Byte0() const
#if defined(JPH_USE_SSE4_1)
return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff03), int(0xffffff02), int(0xffffff01), int(0xffffff00)));
#elif defined(JPH_USE_NEON)
int8x16_t idx = JPH_NEON_INT8x16(0x00, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x7f, 0x7f, 0x02, 0x7f, 0x7f, 0x7f, 0x03, 0x7f, 0x7f, 0x7f);
uint8x16_t idx = JPH_NEON_UINT8x16(0x00, 0x7f, 0x7f, 0x7f, 0x01, 0x7f, 0x7f, 0x7f, 0x02, 0x7f, 0x7f, 0x7f, 0x03, 0x7f, 0x7f, 0x7f);
return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx));
#else
UVec4 result;
@@ -504,7 +509,7 @@ UVec4 UVec4::Expand4Byte4() const
#if defined(JPH_USE_SSE4_1)
return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff07), int(0xffffff06), int(0xffffff05), int(0xffffff04)));
#elif defined(JPH_USE_NEON)
int8x16_t idx = JPH_NEON_INT8x16(0x04, 0x7f, 0x7f, 0x7f, 0x05, 0x7f, 0x7f, 0x7f, 0x06, 0x7f, 0x7f, 0x7f, 0x07, 0x7f, 0x7f, 0x7f);
uint8x16_t idx = JPH_NEON_UINT8x16(0x04, 0x7f, 0x7f, 0x7f, 0x05, 0x7f, 0x7f, 0x7f, 0x06, 0x7f, 0x7f, 0x7f, 0x07, 0x7f, 0x7f, 0x7f);
return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx));
#else
UVec4 result;
@@ -519,7 +524,7 @@ UVec4 UVec4::Expand4Byte8() const
#if defined(JPH_USE_SSE4_1)
return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff0b), int(0xffffff0a), int(0xffffff09), int(0xffffff08)));
#elif defined(JPH_USE_NEON)
int8x16_t idx = JPH_NEON_INT8x16(0x08, 0x7f, 0x7f, 0x7f, 0x09, 0x7f, 0x7f, 0x7f, 0x0a, 0x7f, 0x7f, 0x7f, 0x0b, 0x7f, 0x7f, 0x7f);
uint8x16_t idx = JPH_NEON_UINT8x16(0x08, 0x7f, 0x7f, 0x7f, 0x09, 0x7f, 0x7f, 0x7f, 0x0a, 0x7f, 0x7f, 0x7f, 0x0b, 0x7f, 0x7f, 0x7f);
return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx));
#else
UVec4 result;
@@ -534,7 +539,7 @@ UVec4 UVec4::Expand4Byte12() const
#if defined(JPH_USE_SSE4_1)
return _mm_shuffle_epi8(mValue, _mm_set_epi32(int(0xffffff0f), int(0xffffff0e), int(0xffffff0d), int(0xffffff0c)));
#elif defined(JPH_USE_NEON)
int8x16_t idx = JPH_NEON_INT8x16(0x0c, 0x7f, 0x7f, 0x7f, 0x0d, 0x7f, 0x7f, 0x7f, 0x0e, 0x7f, 0x7f, 0x7f, 0x0f, 0x7f, 0x7f, 0x7f);
uint8x16_t idx = JPH_NEON_UINT8x16(0x0c, 0x7f, 0x7f, 0x7f, 0x0d, 0x7f, 0x7f, 0x7f, 0x0e, 0x7f, 0x7f, 0x7f, 0x0f, 0x7f, 0x7f, 0x7f);
return vreinterpretq_u32_s8(vqtbl1q_s8(vreinterpretq_s8_u32(mValue), idx));
#else
UVec4 result;
+45 -45
View File
@@ -57,9 +57,9 @@ Vec3::Vec3(const Float3 &inV)
Type xy = _mm_unpacklo_ps(x, y);
mValue = _mm_shuffle_ps(xy, z, _MM_SHUFFLE(0, 0, 1, 0)); // Assure Z and W are the same
#elif defined(JPH_USE_NEON)
float32x2_t xy = vld1_f32(&inV.x);
float32x2_t zz = vdup_n_f32(inV.z); // Assure Z and W are the same
mValue = vcombine_f32(xy, zz);
float32x2_t xy = vld1_f32(&inV.x);
float32x2_t zz = vdup_n_f32(inV.z); // Assure Z and W are the same
mValue = vcombine_f32(xy, zz);
#else
mF32[0] = inV[0];
mF32[1] = inV[1];
@@ -75,9 +75,9 @@ Vec3::Vec3(float inX, float inY, float inZ)
#if defined(JPH_USE_SSE)
mValue = _mm_set_ps(inZ, inZ, inY, inX);
#elif defined(JPH_USE_NEON)
uint32x2_t xy = vcreate_f32(static_cast<uint64>(*reinterpret_cast<uint32 *>(&inX)) | (static_cast<uint64>(*reinterpret_cast<uint32 *>(&inY)) << 32));
uint32x2_t zz = vcreate_f32(static_cast<uint64>(*reinterpret_cast<uint32* >(&inZ)) | (static_cast<uint64>(*reinterpret_cast<uint32 *>(&inZ)) << 32));
mValue = vcombine_f32(xy, zz);
uint32x2_t xy = vcreate_u32(static_cast<uint64>(BitCast<uint32>(inX)) | (static_cast<uint64>(BitCast<uint32>(inY)) << 32));
uint32x2_t zz = vreinterpret_u32_f32(vdup_n_f32(inZ));
mValue = vreinterpretq_f32_u32(vcombine_u32(xy, zz));
#else
mF32[0] = inX;
mF32[1] = inY;
@@ -272,7 +272,7 @@ Vec3 Vec3::sSelect(Vec3Arg inV1, Vec3Arg inV2, UVec4Arg inControl)
Type v = _mm_blendv_ps(inV1.mValue, inV2.mValue, _mm_castsi128_ps(inControl.mValue));
return sFixW(v);
#elif defined(JPH_USE_NEON)
Type v = vbslq_f32(vshrq_n_s32(inControl.mValue, 31), inV2.mValue, inV1.mValue);
Type v = vbslq_f32(vreinterpretq_u32_s32(vshrq_n_s32(vreinterpretq_s32_u32(inControl.mValue), 31)), inV2.mValue, inV1.mValue);
return sFixW(v);
#else
Vec3 result;
@@ -290,7 +290,7 @@ Vec3 Vec3::sOr(Vec3Arg inV1, Vec3Arg inV2)
#if defined(JPH_USE_SSE)
return _mm_or_ps(inV1.mValue, inV2.mValue);
#elif defined(JPH_USE_NEON)
return vorrq_s32(inV1.mValue, inV2.mValue);
return vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(inV1.mValue), vreinterpretq_u32_f32(inV2.mValue)));
#else
return Vec3(UVec4::sOr(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat());
#endif
@@ -301,7 +301,7 @@ Vec3 Vec3::sXor(Vec3Arg inV1, Vec3Arg inV2)
#if defined(JPH_USE_SSE)
return _mm_xor_ps(inV1.mValue, inV2.mValue);
#elif defined(JPH_USE_NEON)
return veorq_s32(inV1.mValue, inV2.mValue);
return vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(inV1.mValue), vreinterpretq_u32_f32(inV2.mValue)));
#else
return Vec3(UVec4::sXor(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat());
#endif
@@ -312,7 +312,7 @@ Vec3 Vec3::sAnd(Vec3Arg inV1, Vec3Arg inV2)
#if defined(JPH_USE_SSE)
return _mm_and_ps(inV1.mValue, inV2.mValue);
#elif defined(JPH_USE_NEON)
return vandq_s32(inV1.mValue, inV2.mValue);
return vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(inV1.mValue), vreinterpretq_u32_f32(inV2.mValue)));
#else
return Vec3(UVec4::sAnd(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat());
#endif
@@ -591,18 +591,18 @@ Vec3 Vec3::Cross(Vec3Arg inV2) const
{
#if defined(JPH_USE_SSE)
Type t1 = _mm_shuffle_ps(inV2.mValue, inV2.mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
t1 = _mm_mul_ps(t1, mValue);
Type t2 = _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
t2 = _mm_mul_ps(t2, inV2.mValue);
Type t3 = _mm_sub_ps(t1, t2);
return _mm_shuffle_ps(t3, t3, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
t1 = _mm_mul_ps(t1, mValue);
Type t2 = _mm_shuffle_ps(mValue, mValue, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
t2 = _mm_mul_ps(t2, inV2.mValue);
Type t3 = _mm_sub_ps(t1, t2);
return _mm_shuffle_ps(t3, t3, _MM_SHUFFLE(0, 0, 2, 1)); // Assure Z and W are the same
#elif defined(JPH_USE_NEON)
Type t1 = JPH_NEON_SHUFFLE_F32x4(inV2.mValue, inV2.mValue, 1, 2, 0, 0); // Assure Z and W are the same
t1 = vmulq_f32(t1, mValue);
Type t2 = JPH_NEON_SHUFFLE_F32x4(mValue, mValue, 1, 2, 0, 0); // Assure Z and W are the same
t2 = vmulq_f32(t2, inV2.mValue);
Type t3 = vsubq_f32(t1, t2);
return JPH_NEON_SHUFFLE_F32x4(t3, t3, 1, 2, 0, 0); // Assure Z and W are the same
t1 = vmulq_f32(t1, mValue);
Type t2 = JPH_NEON_SHUFFLE_F32x4(mValue, mValue, 1, 2, 0, 0); // Assure Z and W are the same
t2 = vmulq_f32(t2, inV2.mValue);
Type t3 = vsubq_f32(t1, t2);
return JPH_NEON_SHUFFLE_F32x4(t3, t3, 1, 2, 0, 0); // Assure Z and W are the same
#else
return Vec3(mF32[1] * inV2.mF32[2] - mF32[2] * inV2.mF32[1],
mF32[2] * inV2.mF32[0] - mF32[0] * inV2.mF32[2],
@@ -615,9 +615,9 @@ Vec3 Vec3::DotV(Vec3Arg inV2) const
#if defined(JPH_USE_SSE4_1)
return _mm_dp_ps(mValue, inV2.mValue, 0x7f);
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
mul = vsetq_lane_f32(0, mul, 3);
return vdupq_n_f32(vaddvq_f32(mul));
return vdupq_n_f32(vaddvq_f32(mul));
#else
float dot = 0.0f;
for (int i = 0; i < 3; i++)
@@ -631,9 +631,9 @@ Vec4 Vec3::DotV4(Vec3Arg inV2) const
#if defined(JPH_USE_SSE4_1)
return _mm_dp_ps(mValue, inV2.mValue, 0x7f);
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
mul = vsetq_lane_f32(0, mul, 3);
return vdupq_n_f32(vaddvq_f32(mul));
return vdupq_n_f32(vaddvq_f32(mul));
#else
float dot = 0.0f;
for (int i = 0; i < 3; i++)
@@ -647,9 +647,9 @@ float Vec3::Dot(Vec3Arg inV2) const
#if defined(JPH_USE_SSE4_1)
return _mm_cvtss_f32(_mm_dp_ps(mValue, inV2.mValue, 0x7f));
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
mul = vsetq_lane_f32(0, mul, 3);
return vaddvq_f32(mul);
return vaddvq_f32(mul);
#else
float dot = 0.0f;
for (int i = 0; i < 3; i++)
@@ -663,9 +663,9 @@ float Vec3::LengthSq() const
#if defined(JPH_USE_SSE4_1)
return _mm_cvtss_f32(_mm_dp_ps(mValue, mValue, 0x7f));
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, mValue);
float32x4_t mul = vmulq_f32(mValue, mValue);
mul = vsetq_lane_f32(0, mul, 3);
return vaddvq_f32(mul);
return vaddvq_f32(mul);
#else
float len_sq = 0.0f;
for (int i = 0; i < 3; i++)
@@ -679,10 +679,10 @@ float Vec3::Length() const
#if defined(JPH_USE_SSE4_1)
return _mm_cvtss_f32(_mm_sqrt_ss(_mm_dp_ps(mValue, mValue, 0x7f)));
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, mValue);
float32x4_t mul = vmulq_f32(mValue, mValue);
mul = vsetq_lane_f32(0, mul, 3);
float32x2_t sum = vdup_n_f32(vaddvq_f32(mul));
return vget_lane_f32(vsqrt_f32(sum), 0);
float32x2_t sum = vdup_n_f32(vaddvq_f32(mul));
return vget_lane_f32(vsqrt_f32(sum), 0);
#else
return sqrt(LengthSq());
#endif
@@ -704,10 +704,10 @@ Vec3 Vec3::Normalized() const
#if defined(JPH_USE_SSE4_1)
return _mm_div_ps(mValue, _mm_sqrt_ps(_mm_dp_ps(mValue, mValue, 0x7f)));
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, mValue);
float32x4_t mul = vmulq_f32(mValue, mValue);
mul = vsetq_lane_f32(0, mul, 3);
float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul));
return vdivq_f32(mValue, vsqrtq_f32(sum));
float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul));
return vdivq_f32(mValue, vsqrtq_f32(sum));
#else
return *this / Length();
#endif
@@ -727,12 +727,12 @@ Vec3 Vec3::NormalizedOr(Vec3Arg inZeroValue) const
return _mm_blendv_ps(_mm_div_ps(mValue, _mm_sqrt_ps(len_sq)), inZeroValue.mValue, is_zero);
#endif // JPH_FLOATING_POINT_EXCEPTIONS_ENABLED
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, mValue);
float32x4_t mul = vmulq_f32(mValue, mValue);
mul = vsetq_lane_f32(0, mul, 3);
float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul));
float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul));
float32x4_t len = vsqrtq_f32(sum);
float32x4_t is_zero = vceqq_f32(len, vdupq_n_f32(0));
return vbslq_f32(is_zero, inZeroValue.mValue, vdivq_f32(mValue, len));
uint32x4_t is_zero = vceqq_f32(len, vdupq_n_f32(0));
return vbslq_f32(is_zero, inZeroValue.mValue, vdivq_f32(mValue, len));
#else
float len_sq = LengthSq();
if (len_sq == 0.0f)
@@ -771,9 +771,9 @@ void Vec3::StoreFloat3(Float3 *outV) const
t = t.Swizzle<SWIZZLE_Y, SWIZZLE_UNUSED, SWIZZLE_UNUSED>();
_mm_store_ss(&outV->z, t.mValue);
#elif defined(JPH_USE_NEON)
float32x2_t xy = vget_low_f32(mValue);
vst1_f32(&outV->x, xy);
vst1q_lane_f32(&outV->z, mValue, 2);
float32x2_t xy = vget_low_f32(mValue);
vst1_f32(&outV->x, xy);
vst1q_lane_f32(&outV->z, mValue, 2);
#else
outV->x = mF32[0];
outV->y = mF32[1];
@@ -842,11 +842,11 @@ Vec3 Vec3::GetSign() const
#elif defined(JPH_USE_NEON)
Type minus_one = vdupq_n_f32(-1.0f);
Type one = vdupq_n_f32(1.0f);
return vorrq_s32(vandq_s32(mValue, minus_one), one);
return vreinterpretq_f32_u32(vorrq_u32(vandq_u32(vreinterpretq_u32_f32(mValue), vreinterpretq_u32_f32(minus_one)), vreinterpretq_u32_f32(one)));
#else
return Vec3(signbit(mF32[0])? -1.0f : 1.0f,
signbit(mF32[1])? -1.0f : 1.0f,
signbit(mF32[2])? -1.0f : 1.0f);
return Vec3(std::signbit(mF32[0])? -1.0f : 1.0f,
std::signbit(mF32[1])? -1.0f : 1.0f,
std::signbit(mF32[2])? -1.0f : 1.0f);
#endif
}
+28 -28
View File
@@ -32,9 +32,9 @@ Vec4::Vec4(float inX, float inY, float inZ, float inW)
#if defined(JPH_USE_SSE)
mValue = _mm_set_ps(inW, inZ, inY, inX);
#elif defined(JPH_USE_NEON)
uint32x2_t xy = vcreate_f32(static_cast<uint64>(*reinterpret_cast<uint32 *>(&inX)) | (static_cast<uint64>(*reinterpret_cast<uint32 *>(&inY)) << 32));
uint32x2_t zw = vcreate_f32(static_cast<uint64>(*reinterpret_cast<uint32* >(&inZ)) | (static_cast<uint64>(*reinterpret_cast<uint32 *>(&inW)) << 32));
mValue = vcombine_f32(xy, zw);
uint32x2_t xy = vcreate_u32(static_cast<uint64>(BitCast<uint32>(inX)) | (static_cast<uint64>(BitCast<uint32>(inY)) << 32));
uint32x2_t zw = vcreate_u32(static_cast<uint64>(BitCast<uint32>(inZ)) | (static_cast<uint64>(BitCast<uint32>(inW)) << 32));
mValue = vreinterpretq_f32_u32(vcombine_u32(xy, zw));
#else
mF32[0] = inX;
mF32[1] = inY;
@@ -256,7 +256,7 @@ Vec4 Vec4::sSelect(Vec4Arg inV1, Vec4Arg inV2, UVec4Arg inControl)
#if defined(JPH_USE_SSE4_1)
return _mm_blendv_ps(inV1.mValue, inV2.mValue, _mm_castsi128_ps(inControl.mValue));
#elif defined(JPH_USE_NEON)
return vbslq_f32(vshrq_n_s32(inControl.mValue, 31), inV2.mValue, inV1.mValue);
return vbslq_f32(vreinterpretq_u32_s32(vshrq_n_s32(vreinterpretq_s32_u32(inControl.mValue), 31)), inV2.mValue, inV1.mValue);
#else
Vec4 result;
for (int i = 0; i < 4; i++)
@@ -270,7 +270,7 @@ Vec4 Vec4::sOr(Vec4Arg inV1, Vec4Arg inV2)
#if defined(JPH_USE_SSE)
return _mm_or_ps(inV1.mValue, inV2.mValue);
#elif defined(JPH_USE_NEON)
return vorrq_s32(inV1.mValue, inV2.mValue);
return vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(inV1.mValue), vreinterpretq_u32_f32(inV2.mValue)));
#else
return UVec4::sOr(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat();
#endif
@@ -281,7 +281,7 @@ Vec4 Vec4::sXor(Vec4Arg inV1, Vec4Arg inV2)
#if defined(JPH_USE_SSE)
return _mm_xor_ps(inV1.mValue, inV2.mValue);
#elif defined(JPH_USE_NEON)
return veorq_s32(inV1.mValue, inV2.mValue);
return vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(inV1.mValue), vreinterpretq_u32_f32(inV2.mValue)));
#else
return UVec4::sXor(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat();
#endif
@@ -292,7 +292,7 @@ Vec4 Vec4::sAnd(Vec4Arg inV1, Vec4Arg inV2)
#if defined(JPH_USE_SSE)
return _mm_and_ps(inV1.mValue, inV2.mValue);
#elif defined(JPH_USE_NEON)
return vandq_s32(inV1.mValue, inV2.mValue);
return vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(inV1.mValue), vreinterpretq_u32_f32(inV2.mValue)));
#else
return UVec4::sAnd(inV1.ReinterpretAsInt(), inV2.ReinterpretAsInt()).ReinterpretAsFloat();
#endif
@@ -619,8 +619,8 @@ Vec4 Vec4::DotV(Vec4Arg inV2) const
#if defined(JPH_USE_SSE4_1)
return _mm_dp_ps(mValue, inV2.mValue, 0xff);
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
return vdupq_n_f32(vaddvq_f32(mul));
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
return vdupq_n_f32(vaddvq_f32(mul));
#else
// Brackets placed so that the order is consistent with the vectorized version
return Vec4::sReplicate((mF32[0] * inV2.mF32[0] + mF32[1] * inV2.mF32[1]) + (mF32[2] * inV2.mF32[2] + mF32[3] * inV2.mF32[3]));
@@ -632,8 +632,8 @@ float Vec4::Dot(Vec4Arg inV2) const
#if defined(JPH_USE_SSE4_1)
return _mm_cvtss_f32(_mm_dp_ps(mValue, inV2.mValue, 0xff));
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
return vaddvq_f32(mul);
float32x4_t mul = vmulq_f32(mValue, inV2.mValue);
return vaddvq_f32(mul);
#else
// Brackets placed so that the order is consistent with the vectorized version
return (mF32[0] * inV2.mF32[0] + mF32[1] * inV2.mF32[1]) + (mF32[2] * inV2.mF32[2] + mF32[3] * inV2.mF32[3]);
@@ -645,8 +645,8 @@ float Vec4::LengthSq() const
#if defined(JPH_USE_SSE4_1)
return _mm_cvtss_f32(_mm_dp_ps(mValue, mValue, 0xff));
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, mValue);
return vaddvq_f32(mul);
float32x4_t mul = vmulq_f32(mValue, mValue);
return vaddvq_f32(mul);
#else
// Brackets placed so that the order is consistent with the vectorized version
return (mF32[0] * mF32[0] + mF32[1] * mF32[1]) + (mF32[2] * mF32[2] + mF32[3] * mF32[3]);
@@ -658,9 +658,9 @@ float Vec4::Length() const
#if defined(JPH_USE_SSE4_1)
return _mm_cvtss_f32(_mm_sqrt_ss(_mm_dp_ps(mValue, mValue, 0xff)));
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, mValue);
float32x2_t sum = vdup_n_f32(vaddvq_f32(mul));
return vget_lane_f32(vsqrt_f32(sum), 0);
float32x4_t mul = vmulq_f32(mValue, mValue);
float32x2_t sum = vdup_n_f32(vaddvq_f32(mul));
return vget_lane_f32(vsqrt_f32(sum), 0);
#else
// Brackets placed so that the order is consistent with the vectorized version
return sqrt((mF32[0] * mF32[0] + mF32[1] * mF32[1]) + (mF32[2] * mF32[2] + mF32[3] * mF32[3]));
@@ -690,12 +690,12 @@ Vec4 Vec4::GetSign() const
#elif defined(JPH_USE_NEON)
Type minus_one = vdupq_n_f32(-1.0f);
Type one = vdupq_n_f32(1.0f);
return vorrq_s32(vandq_s32(mValue, minus_one), one);
return vreinterpretq_f32_u32(vorrq_u32(vandq_u32(vreinterpretq_u32_f32(mValue), vreinterpretq_u32_f32(minus_one)), vreinterpretq_u32_f32(one)));
#else
return Vec4(signbit(mF32[0])? -1.0f : 1.0f,
signbit(mF32[1])? -1.0f : 1.0f,
signbit(mF32[2])? -1.0f : 1.0f,
signbit(mF32[3])? -1.0f : 1.0f);
return Vec4(std::signbit(mF32[0])? -1.0f : 1.0f,
std::signbit(mF32[1])? -1.0f : 1.0f,
std::signbit(mF32[2])? -1.0f : 1.0f,
std::signbit(mF32[3])? -1.0f : 1.0f);
#endif
}
@@ -704,9 +704,9 @@ Vec4 Vec4::Normalized() const
#if defined(JPH_USE_SSE4_1)
return _mm_div_ps(mValue, _mm_sqrt_ps(_mm_dp_ps(mValue, mValue, 0xff)));
#elif defined(JPH_USE_NEON)
float32x4_t mul = vmulq_f32(mValue, mValue);
float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul));
return vdivq_f32(mValue, vsqrtq_f32(sum));
float32x4_t mul = vmulq_f32(mValue, mValue);
float32x4_t sum = vdupq_n_f32(vaddvq_f32(mul));
return vdivq_f32(mValue, vsqrtq_f32(sum));
#else
return *this / Length();
#endif
@@ -717,7 +717,7 @@ void Vec4::StoreFloat4(Float4 *outV) const
#if defined(JPH_USE_SSE)
_mm_storeu_ps(&outV->x, mValue);
#elif defined(JPH_USE_NEON)
vst1q_f32(&outV->x, mValue);
vst1q_f32(&outV->x, mValue);
#else
for (int i = 0; i < 4; ++i)
(&outV->x)[i] = mF32[i];
@@ -751,10 +751,10 @@ int Vec4::GetSignBits() const
#if defined(JPH_USE_SSE)
return _mm_movemask_ps(mValue);
#elif defined(JPH_USE_NEON)
int32x4_t shift = JPH_NEON_INT32x4(0, 1, 2, 3);
return vaddvq_u32(vshlq_u32(vshrq_n_u32(vreinterpretq_u32_f32(mValue), 31), shift));
int32x4_t shift = JPH_NEON_INT32x4(0, 1, 2, 3);
return vaddvq_u32(vshlq_u32(vshrq_n_u32(vreinterpretq_u32_f32(mValue), 31), shift));
#else
return (signbit(mF32[0])? 1 : 0) | (signbit(mF32[1])? 2 : 0) | (signbit(mF32[2])? 4 : 0) | (signbit(mF32[3])? 8 : 0);
return (std::signbit(mF32[0])? 1 : 0) | (std::signbit(mF32[1])? 2 : 0) | (std::signbit(mF32[2])? 4 : 0) | (std::signbit(mF32[3])? 8 : 0);
#endif
}
@@ -17,7 +17,7 @@ public:
JPH_OVERRIDE_NEW_DELETE
/// Constructor
explicit ObjectStreamBinaryIn(istream &inStream);
explicit ObjectStreamBinaryIn(istream &inStream);
///@name Input type specific operations
virtual bool ReadDataType(EOSDataType &outType) override;
@@ -17,7 +17,7 @@ public:
JPH_OVERRIDE_NEW_DELETE
/// Constructor and destructor
explicit ObjectStreamBinaryOut(ostream &inStream);
explicit ObjectStreamBinaryOut(ostream &inStream);
///@name Output type specific operations
virtual void WriteDataType(EOSDataType inType) override;
@@ -88,7 +88,7 @@ public:
protected:
/// Constructor
explicit ObjectStreamIn(istream &inStream);
explicit ObjectStreamIn(istream &inStream);
/// Determine the type and version of an object stream
static bool GetInfo(istream &inStream, EStreamType &outType, int &outVersion, int &outRevision);
@@ -112,7 +112,7 @@ private:
struct ClassDescription
{
ClassDescription() = default;
explicit ClassDescription(const RTTI *inRTTI) : mRTTI(inRTTI) { }
explicit ClassDescription(const RTTI *inRTTI) : mRTTI(inRTTI) { }
const RTTI * mRTTI = nullptr;
Array<AttributeDescription> mAttributes;
@@ -73,7 +73,7 @@ protected:
static ObjectStreamOut * Open(EStreamType inType, ostream &inStream);
/// Constructor
explicit ObjectStreamOut(ostream &inStream);
explicit ObjectStreamOut(ostream &inStream);
ostream & mStream;
@@ -30,41 +30,41 @@ bool ObjectStreamTextIn::ReadDataType(EOSDataType &outType)
else if (token == "pointer")
outType = EOSDataType::Pointer;
else if (token == "array")
outType = EOSDataType::Array;
outType = EOSDataType::Array;
else if (token == "uint8")
outType = EOSDataType::T_uint8;
outType = EOSDataType::T_uint8;
else if (token == "uint16")
outType = EOSDataType::T_uint16;
outType = EOSDataType::T_uint16;
else if (token == "int")
outType = EOSDataType::T_int;
outType = EOSDataType::T_int;
else if (token == "uint32")
outType = EOSDataType::T_uint32;
outType = EOSDataType::T_uint32;
else if (token == "uint64")
outType = EOSDataType::T_uint64;
outType = EOSDataType::T_uint64;
else if (token == "float")
outType = EOSDataType::T_float;
outType = EOSDataType::T_float;
else if (token == "double")
outType = EOSDataType::T_double;
outType = EOSDataType::T_double;
else if (token == "bool")
outType = EOSDataType::T_bool;
outType = EOSDataType::T_bool;
else if (token == "string")
outType = EOSDataType::T_String;
outType = EOSDataType::T_String;
else if (token == "float3")
outType = EOSDataType::T_Float3;
outType = EOSDataType::T_Float3;
else if (token == "double3")
outType = EOSDataType::T_Double3;
outType = EOSDataType::T_Double3;
else if (token == "vec3")
outType = EOSDataType::T_Vec3;
outType = EOSDataType::T_Vec3;
else if (token == "dvec3")
outType = EOSDataType::T_DVec3;
outType = EOSDataType::T_DVec3;
else if (token == "vec4")
outType = EOSDataType::T_Vec4;
outType = EOSDataType::T_Vec4;
else if (token == "quat")
outType = EOSDataType::T_Quat;
outType = EOSDataType::T_Quat;
else if (token == "mat44")
outType = EOSDataType::T_Mat44;
outType = EOSDataType::T_Mat44;
else if (token == "dmat44")
outType = EOSDataType::T_DMat44;
outType = EOSDataType::T_DMat44;
else
{
Trace("ObjectStreamTextIn: Found unknown data type.");
@@ -51,7 +51,7 @@ JPH_NAMESPACE_BEGIN
{ \
if (inPointer) \
ioStream.WritePointerData(GetRTTI(inPointer), (void *)inPointer); \
else \
else \
ioStream.WritePointerData(nullptr, nullptr); \
} \
void OSWriteDataType(IObjectStreamOut &ioStream, class_name *) \
+9 -9
View File
@@ -36,12 +36,6 @@ class alignas(JPH_RVECTOR_ALIGNMENT) JPH_EXPORT_GCC_BUG_WORKAROUND Body : public
public:
JPH_OVERRIDE_NEW_DELETE
/// Default constructor
Body() = default;
/// Destructor
~Body() { JPH_ASSERT(mMotionProperties == nullptr); }
/// Get the id of this body
inline const BodyID & GetID() const { return mID; }
@@ -234,13 +228,13 @@ public:
inline RVec3 GetPosition() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mPosition - mRotation * mShape->GetCenterOfMass(); }
/// World space rotation of the body
inline Quat GetRotation() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mRotation; }
inline Quat GetRotation() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mRotation; }
/// Calculates the transform of this body
inline RMat44 GetWorldTransform() const;
/// Gets the world space position of this body's center of mass
inline RVec3 GetCenterOfMassPosition() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mPosition; }
inline RVec3 GetCenterOfMassPosition() const { JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::Read)); return mPosition; }
/// Calculates the transform for this body's center of mass
inline RMat44 GetCenterOfMassTransform() const;
@@ -287,7 +281,7 @@ public:
/// Update position using an Euler step (used during position integrate & constraint solving)
inline void AddPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime) { JPH_ASSERT(IsRigidBody()); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); mPosition += mMotionProperties->LockTranslation(inLinearVelocityTimesDeltaTime); JPH_ASSERT(!mPosition.IsNaN()); }
inline void SubPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime) { JPH_ASSERT(IsRigidBody()); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); mPosition -= mMotionProperties->LockTranslation(inLinearVelocityTimesDeltaTime); JPH_ASSERT(!mPosition.IsNaN()); }
inline void SubPositionStep(Vec3Arg inLinearVelocityTimesDeltaTime) { JPH_ASSERT(IsRigidBody()); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sPositionAccess, BodyAccess::EAccess::ReadWrite)); mPosition -= mMotionProperties->LockTranslation(inLinearVelocityTimesDeltaTime); JPH_ASSERT(!mPosition.IsNaN()); }
/// Update rotation using an Euler step (using during position integrate & constraint solving)
inline void AddRotationStep(Vec3Arg inAngularVelocityTimesDeltaTime);
@@ -336,9 +330,15 @@ public:
private:
friend class BodyManager;
friend class BodyWithMotionProperties;
friend class SoftBodyWithMotionPropertiesAndShape;
Body() = default; ///< Bodies must be created through BodyInterface::CreateBody
explicit Body(bool); ///< Alternative constructor that initializes all members
~Body() { JPH_ASSERT(mMotionProperties == nullptr); } ///< Bodies must be destroyed through BodyInterface::DestroyBody
inline void GetSleepTestPoints(RVec3 *outPoints) const; ///< Determine points to test for checking if body is sleeping: COM, COM + largest bounding box axis, COM + second largest bounding box axis
enum class EFlags : uint8
+32 -4
View File
@@ -12,7 +12,7 @@ JPH_NAMESPACE_BEGIN
class Body;
/// Class function to filter out bodies, returns true if test should collide with body
class BodyFilter : public NonCopyable
class JPH_EXPORT BodyFilter : public NonCopyable
{
public:
/// Destructor
@@ -32,7 +32,7 @@ public:
};
/// A simple body filter implementation that ignores a single, specified body
class IgnoreSingleBodyFilter : public BodyFilter
class JPH_EXPORT IgnoreSingleBodyFilter : public BodyFilter
{
public:
/// Constructor, pass the body you want to ignore
@@ -52,7 +52,7 @@ private:
};
/// A simple body filter implementation that ignores multiple, specified bodies
class IgnoreMultipleBodiesFilter : public BodyFilter
class JPH_EXPORT IgnoreMultipleBodiesFilter : public BodyFilter
{
public:
/// Remove all bodies from the filter
@@ -83,9 +83,37 @@ private:
Array<BodyID> mBodyIDs;
};
/// Ignores a single body and chains the filter to another filter
class JPH_EXPORT IgnoreSingleBodyFilterChained : public BodyFilter
{
public:
/// Constructor
explicit IgnoreSingleBodyFilterChained(const BodyID inBodyID, const BodyFilter &inFilter) :
mBodyID(inBodyID),
mFilter(inFilter)
{
}
/// Filter function. Returns true if we should collide with inBodyID
virtual bool ShouldCollide(const BodyID &inBodyID) const override
{
return inBodyID != mBodyID && mFilter.ShouldCollide(inBodyID);
}
/// Filter function. Returns true if we should collide with inBody (this is called after the body is locked and makes it possible to filter based on body members)
virtual bool ShouldCollideLocked(const Body &inBody) const override
{
return mFilter.ShouldCollideLocked(inBody);
}
private:
BodyID mBodyID;
const BodyFilter & mFilter;
};
#ifdef JPH_DEBUG_RENDERER
/// Class function to filter out bodies for debug rendering, returns true if body should be rendered
class BodyDrawFilter : public NonCopyable
class JPH_EXPORT BodyDrawFilter : public NonCopyable
{
public:
/// Destructor
@@ -83,10 +83,12 @@ public:
/// @param outBodies If not null on input, this will contain a list of body pointers corresponding to inBodyIDs that can be destroyed afterwards (caller assumes ownership over these).
void UnassignBodyIDs(const BodyID *inBodyIDs, int inNumber, Body **outBodies);
/// Destroy a body
/// Destroy a body.
/// Make sure that you remove the body from the physics system using BodyInterface::RemoveBody before calling this function.
void DestroyBody(const BodyID &inBodyID);
/// Destroy multiple bodies
/// Make sure that you remove the bodies from the physics system using BodyInterface::RemoveBody before calling this function.
void DestroyBodies(const BodyID *inBodyIDs, int inNumber);
/// Add body to the physics system.
@@ -188,7 +188,7 @@ Body *BodyManager::AllocateBody(const BodyCreationSettings &inBodyCreationSettin
}
else
{
body = new Body;
body = new Body;
}
body->mBodyType = EBodyType::RigidBody;
body->mShape = inBodyCreationSettings.GetShape();
@@ -403,7 +403,7 @@ Body *BodyManager::RemoveBodyInternal(const BodyID &inBodyID)
// Validate that it can be removed
JPH_ASSERT(body->GetID() == inBodyID);
JPH_ASSERT(!body->IsActive());
JPH_ASSERT(!body->IsInBroadPhase());
JPH_ASSERT(!body->IsInBroadPhase(), "Use BodyInterface::RemoveBody to remove this body first!");
// Push the id onto the freelist
mBodies[idx] = (Body *)mBodyIDFreeListStart;
@@ -554,7 +554,7 @@ void BodyManager::ActivateBodies(const BodyID *inBodyIDs, int inNumber)
Body &body = *mBodies[body_id.GetIndex()];
JPH_ASSERT(body.GetID() == body_id);
JPH_ASSERT(body.IsInBroadPhase());
JPH_ASSERT(body.IsInBroadPhase(), "Use BodyInterface::AddBody to add the body first!");
if (!body.IsStatic())
{
@@ -591,7 +591,7 @@ void BodyManager::DeactivateBodies(const BodyID *inBodyIDs, int inNumber)
Body &body = *mBodies[body_id.GetIndex()];
JPH_ASSERT(body.GetID() == body_id);
JPH_ASSERT(body.IsInBroadPhase());
JPH_ASSERT(body.IsInBroadPhase(), "Use BodyInterface::AddBody to add the body first!");
if (body.mMotionProperties != nullptr
&& body.mMotionProperties->mIndexInActiveBodies != Body::cInactiveIndex)
@@ -102,10 +102,10 @@ public:
void SetInverseMass(float inInverseMass) { mInvMass = inInverseMass; }
/// Diagonal of inverse inertia matrix: D. Should only be called on a dynamic object (static or kinematic bodies have infinite mass so should be treated as D = 0)
inline Vec3 GetInverseInertiaDiagonal() const { JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); return mInvInertiaDiagonal; }
inline Vec3 GetInverseInertiaDiagonal() const { JPH_ASSERT(mCachedMotionType == EMotionType::Dynamic); return mInvInertiaDiagonal; }
/// Rotation (R) that takes inverse inertia diagonal to local space: \f$I_{body}^{-1} = R \: D \: R^{-1}\f$
inline Quat GetInertiaRotation() const { return mInertiaRotation; }
inline Quat GetInertiaRotation() const { return mInertiaRotation; }
/// Set the inverse inertia tensor in local space by setting the diagonal and the rotation: \f$I_{body}^{-1} = R \: D \: R^{-1}\f$.
/// Note that mass and inertia are linearly related (e.g. inertia of a sphere with mass m and radius r is \f$2/5 \: m \: r^2\f$).
@@ -114,10 +114,10 @@ public:
void SetInverseInertia(Vec3Arg inDiagonal, QuatArg inRot) { mInvInertiaDiagonal = inDiagonal; mInertiaRotation = inRot; }
/// Get inverse inertia matrix (\f$I_{body}^{-1}\f$). Will be a matrix of zeros for a static or kinematic object.
inline Mat44 GetLocalSpaceInverseInertia() const;
inline Mat44 GetLocalSpaceInverseInertia() const;
/// Same as GetLocalSpaceInverseInertia() but doesn't check if the body is dynamic
inline Mat44 GetLocalSpaceInverseInertiaUnchecked() const;
inline Mat44 GetLocalSpaceInverseInertiaUnchecked() const;
/// Get inverse inertia matrix (\f$I^{-1}\f$) for a given object rotation (translation will be ignored). Zero if object is static or kinematic.
inline Mat44 GetInverseInertiaForRotation(Mat44Arg inRotation) const;
@@ -191,7 +191,7 @@ public:
inline void AddLinearVelocityStep(Vec3Arg inLinearVelocityChange) { JPH_DET_LOG("AddLinearVelocityStep: " << inLinearVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity = LockTranslation(mLinearVelocity + inLinearVelocityChange); JPH_ASSERT(!mLinearVelocity.IsNaN()); }
inline void SubLinearVelocityStep(Vec3Arg inLinearVelocityChange) { JPH_DET_LOG("SubLinearVelocityStep: " << inLinearVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mLinearVelocity = LockTranslation(mLinearVelocity - inLinearVelocityChange); JPH_ASSERT(!mLinearVelocity.IsNaN()); }
inline void AddAngularVelocityStep(Vec3Arg inAngularVelocityChange) { JPH_DET_LOG("AddAngularVelocityStep: " << inAngularVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity += inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); }
inline void SubAngularVelocityStep(Vec3Arg inAngularVelocityChange) { JPH_DET_LOG("SubAngularVelocityStep: " << inAngularVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity -= inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); }
inline void SubAngularVelocityStep(Vec3Arg inAngularVelocityChange) { JPH_DET_LOG("SubAngularVelocityStep: " << inAngularVelocityChange); JPH_ASSERT(BodyAccess::sCheckRights(BodyAccess::sVelocityAccess, BodyAccess::EAccess::ReadWrite)); mAngularVelocity -= inAngularVelocityChange; JPH_ASSERT(!mAngularVelocity.IsNaN()); }
///@}
/// Apply the gyroscopic force (aka Dzhanibekov effect, see https://en.wikipedia.org/wiki/Tennis_racket_theorem)
@@ -315,4 +315,9 @@ bool Character::SetShape(const Shape *inShape, float inMaxPenetrationDepth, bool
return true;
}
TransformedShape Character::GetTransformedShape(bool inLockBodies) const
{
return sGetBodyInterface(mSystem, inLockBodies).GetTransformedShape(mBodyID);
}
JPH_NAMESPACE_END
@@ -6,6 +6,7 @@
#include <Jolt/Physics/Character/CharacterBase.h>
#include <Jolt/Physics/Collision/ObjectLayer.h>
#include <Jolt/Physics/Collision/TransformedShape.h>
#include <Jolt/Physics/EActivation.h>
JPH_NAMESPACE_BEGIN
@@ -105,6 +106,9 @@ public:
/// Calculate the world transform of the character
RMat44 GetWorldTransform(bool inLockBodies = true) const;
/// Get the layer of the character
ObjectLayer GetLayer() const { return mLayer; }
/// Update the layer of the character
void SetLayer(ObjectLayer inLayer, bool inLockBodies = true);
@@ -112,6 +116,9 @@ public:
/// if the new shape collides before switching shape. Returns true if the switch succeeded.
bool SetShape(const Shape *inShape, float inMaxPenetrationDepth, bool inLockBodies = true);
/// Get the transformed shape that represents the volume of the character, can be used for collision checks.
TransformedShape GetTransformedShape(bool inLockBodies = true) const;
/// @brief Get all contacts for the character at a particular location
/// @param inPosition Position to test.
/// @param inRotation Rotation at which to test the shape.
@@ -100,10 +100,10 @@ public:
bool IsSupported() const { return mGroundState == EGroundState::OnGround || mGroundState == EGroundState::OnSteepGround; }
/// Get the contact point with the ground
RVec3 GetGroundPosition() const { return mGroundPosition; }
RVec3 GetGroundPosition() const { return mGroundPosition; }
/// Get the contact normal with the ground
Vec3 GetGroundNormal() const { return mGroundNormal; }
Vec3 GetGroundNormal() const { return mGroundNormal; }
/// Velocity in world space of ground
Vec3 GetGroundVelocity() const { return mGroundVelocity; }
@@ -6,6 +6,7 @@
#include <Jolt/Physics/Character/CharacterVirtual.h>
#include <Jolt/Physics/Body/Body.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/PhysicsSystem.h>
#include <Jolt/Physics/Collision/ShapeCast.h>
#include <Jolt/Physics/Collision/CollideShape.h>
@@ -20,6 +21,68 @@
JPH_NAMESPACE_BEGIN
void CharacterVsCharacterCollisionSimple::Remove(const CharacterVirtual *inCharacter)
{
Array<CharacterVirtual *>::iterator i = std::find(mCharacters.begin(), mCharacters.end(), inCharacter);
if (i != mCharacters.end())
mCharacters.erase(i);
}
void CharacterVsCharacterCollisionSimple::CollideCharacter(const CharacterVirtual *inCharacter, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector) const
{
// Make shape 1 relative to inBaseOffset
Mat44 transform1 = inCenterOfMassTransform.PostTranslated(-inBaseOffset).ToMat44();
const Shape *shape = inCharacter->GetShape();
CollideShapeSettings settings = inCollideShapeSettings;
// Iterate over all characters
for (const CharacterVirtual *c : mCharacters)
if (c != inCharacter
&& !ioCollector.ShouldEarlyOut())
{
// Collector needs to know which character we're colliding with
ioCollector.SetUserData(reinterpret_cast<uint64>(c));
// Make shape 2 relative to inBaseOffset
Mat44 transform2 = c->GetCenterOfMassTransform().PostTranslated(-inBaseOffset).ToMat44();
// We need to add the padding of character 2 so that we will detect collision with its outer shell
settings.mMaxSeparationDistance = inCollideShapeSettings.mMaxSeparationDistance + c->GetCharacterPadding();
// Note that this collides against the character's shape without padding, this will be corrected for in CharacterVirtual::GetContactsAtPosition
CollisionDispatch::sCollideShapeVsShape(shape, c->GetShape(), Vec3::sReplicate(1.0f), Vec3::sReplicate(1.0f), transform1, transform2, SubShapeIDCreator(), SubShapeIDCreator(), settings, ioCollector);
}
// Reset the user data
ioCollector.SetUserData(0);
}
void CharacterVsCharacterCollisionSimple::CastCharacter(const CharacterVirtual *inCharacter, RMat44Arg inCenterOfMassTransform, Vec3Arg inDirection, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector) const
{
// Convert shape cast relative to inBaseOffset
Mat44 transform1 = inCenterOfMassTransform.PostTranslated(-inBaseOffset).ToMat44();
ShapeCast shape_cast(inCharacter->GetShape(), Vec3::sReplicate(1.0f), transform1, inDirection);
// Iterate over all characters
for (const CharacterVirtual *c : mCharacters)
if (c != inCharacter
&& !ioCollector.ShouldEarlyOut())
{
// Collector needs to know which character we're colliding with
ioCollector.SetUserData(reinterpret_cast<uint64>(c));
// Make shape 2 relative to inBaseOffset
Mat44 transform2 = c->GetCenterOfMassTransform().PostTranslated(-inBaseOffset).ToMat44();
// Note that this collides against the character's shape without padding, this will be corrected for in CharacterVirtual::GetFirstContactForSweep
CollisionDispatch::sCastShapeVsShapeWorldSpace(shape_cast, inShapeCastSettings, c->GetShape(), Vec3::sReplicate(1.0f), { }, transform2, SubShapeIDCreator(), SubShapeIDCreator(), ioCollector);
}
// Reset the user data
ioCollector.SetUserData(0);
}
CharacterVirtual::CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem) :
CharacterBase(inSettings, inSystem),
mBackFaceMode(inSettings->mBackFaceMode),
@@ -41,6 +104,30 @@ CharacterVirtual::CharacterVirtual(const CharacterVirtualSettings *inSettings, R
// Copy settings
SetMaxStrength(inSettings->mMaxStrength);
SetMass(inSettings->mMass);
// Create an inner rigid body if requested
if (inSettings->mInnerBodyShape != nullptr)
{
BodyCreationSettings settings(inSettings->mInnerBodyShape, GetInnerBodyPosition(), mRotation, EMotionType::Kinematic, inSettings->mInnerBodyLayer);
settings.mAllowSleeping = false; // Disable sleeping so that we will receive sensor callbacks
settings.mUserData = inUserData;
mInnerBodyID = inSystem->GetBodyInterface().CreateAndAddBody(settings, EActivation::Activate);
}
}
CharacterVirtual::~CharacterVirtual()
{
if (!mInnerBodyID.IsInvalid())
{
mSystem->GetBodyInterface().RemoveBody(mInnerBodyID);
mSystem->GetBodyInterface().DestroyBody(mInnerBodyID);
}
}
void CharacterVirtual::UpdateInnerBodyTransform()
{
if (!mInnerBodyID.IsInvalid())
mSystem->GetBodyInterface().SetPositionAndRotation(mInnerBodyID, GetInnerBodyPosition(), mRotation, EActivation::DontActivate);
}
void CharacterVirtual::GetAdjustedBodyVelocity(const Body& inBody, Vec3 &outLinearVelocity, Vec3 &outAngularVelocity) const
@@ -104,6 +191,20 @@ void CharacterVirtual::sFillContactProperties(const CharacterVirtual *inCharacte
outContact.mMaterial = inCollector.GetContext()->GetMaterial(inResult.mSubShapeID2);
}
void CharacterVirtual::sFillCharacterContactProperties(Contact &outContact, CharacterVirtual *inOtherCharacter, RVec3Arg inBaseOffset, const CollideShapeResult &inResult)
{
outContact.mPosition = inBaseOffset + inResult.mContactPointOn2;
outContact.mLinearVelocity = inOtherCharacter->GetLinearVelocity();
outContact.mSurfaceNormal = outContact.mContactNormal = -inResult.mPenetrationAxis.NormalizedOr(Vec3::sZero());
outContact.mDistance = -inResult.mPenetrationDepth;
outContact.mCharacterB = inOtherCharacter;
outContact.mSubShapeIDB = inResult.mSubShapeID2;
outContact.mMotionTypeB = EMotionType::Kinematic; // Other character is kinematic, we can't directly move it
outContact.mIsSensorB = false;
outContact.mUserData = inOtherCharacter->GetUserData();
outContact.mMaterial = PhysicsMaterial::sDefault;
}
void CharacterVirtual::ContactCollector::AddHit(const CollideShapeResult &inResult)
{
// If we exceed our contact limit, try to clean up near-duplicate contacts
@@ -122,7 +223,7 @@ void CharacterVirtual::ContactCollector::AddHit(const CollideShapeResult &inResu
for (int j = i - 1; j >= 0; --j)
{
Contact &contact_j = mContacts[j];
if (contact_i.mBodyB == contact_j.mBodyB // Same body
if (contact_i.IsSameBody(contact_j)
&& contact_i.mContactNormal.Dot(contact_j.mContactNormal) > mHitReductionCosMaxAngle) // Very similar contact normals
{
// Remove the contact with the biggest distance
@@ -160,22 +261,35 @@ void CharacterVirtual::ContactCollector::AddHit(const CollideShapeResult &inResu
}
}
BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2);
if (lock.SucceededAndIsInBroadPhase())
if (inResult.mBodyID2.IsInvalid())
{
// Assuming this is a hit against another character
JPH_ASSERT(mOtherCharacter != nullptr);
// Create contact with other character
mContacts.emplace_back();
Contact &contact = mContacts.back();
sFillContactProperties(mCharacter, contact, lock.GetBody(), mUp, mBaseOffset, *this, inResult);
sFillCharacterContactProperties(contact, mOtherCharacter, mBaseOffset, inResult);
contact.mFraction = 0.0f;
}
else
{
// Create contact with other body
BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2);
if (lock.SucceededAndIsInBroadPhase())
{
mContacts.emplace_back();
Contact &contact = mContacts.back();
sFillContactProperties(mCharacter, contact, lock.GetBody(), mUp, mBaseOffset, *this, inResult);
contact.mFraction = 0.0f;
}
}
}
void CharacterVirtual::ContactCastCollector::AddHit(const ShapeCastResult &inResult)
{
// Should not have gotten here without a lower fraction
JPH_ASSERT(inResult.mFraction < mContact.mFraction);
if (inResult.mFraction > 0.0f // Ignore collisions at fraction = 0
if (inResult.mFraction < mContact.mFraction // Since we're doing checks against the world and against characters, we may get a hit with a higher fraction than the previous hit
&& inResult.mFraction > 0.0f // Ignore collisions at fraction = 0
&& inResult.mPenetrationAxis.Dot(mDisplacement) > 0.0f) // Ignore penetrations that we're moving away from
{
// Test if this contact should be ignored
@@ -185,8 +299,17 @@ void CharacterVirtual::ContactCastCollector::AddHit(const ShapeCastResult &inRes
Contact contact;
// Lock body only while we fetch contact properties
if (inResult.mBodyID2.IsInvalid())
{
// Assuming this is a hit against another character
JPH_ASSERT(mOtherCharacter != nullptr);
// Create contact with other character
sFillCharacterContactProperties(contact, mOtherCharacter, mBaseOffset, inResult);
}
else
{
// Lock body only while we fetch contact properties
BodyLockRead lock(mSystem->GetBodyLockInterface(), inResult.mBodyID2);
if (!lock.SucceededAndIsInBroadPhase())
return;
@@ -219,15 +342,18 @@ void CharacterVirtual::CheckCollision(RVec3Arg inPosition, QuatArg inRotation, V
// Settings for collide shape
CollideShapeSettings settings;
settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive;
settings.mBackFaceMode = mBackFaceMode;
settings.mActiveEdgeMovementDirection = inMovementDirection;
settings.mMaxSeparationDistance = mCharacterPadding + inMaxSeparationDistance;
// Body filter
IgnoreSingleBodyFilterChained body_filter(mInnerBodyID, inBodyFilter);
// Collide shape
if (mEnhancedInternalEdgeRemoval)
{
// Version that does additional work to remove internal edges
settings.mActiveEdgeMode = EActiveEdgeMode::CollideWithAll;
settings.mCollectFacesMode = ECollectFacesMode::CollectFaces;
// This is a copy of NarrowPhaseQuery::CollideShape with additional logic to wrap the collector in an InternalEdgeRemovingCollector and flushing that collector after every body
@@ -287,11 +413,23 @@ void CharacterVirtual::CheckCollision(RVec3Arg inPosition, QuatArg inRotation, V
bounds.ExpandBy(Vec3::sReplicate(settings.mMaxSeparationDistance));
// Do broadphase test
MyCollector collector(inShape, transform, settings, inBaseOffset, ioCollector, mSystem->GetBodyLockInterface(), inBodyFilter, inShapeFilter);
MyCollector collector(inShape, transform, settings, inBaseOffset, ioCollector, mSystem->GetBodyLockInterface(), body_filter, inShapeFilter);
mSystem->GetBroadPhaseQuery().CollideAABox(bounds, collector, inBroadPhaseLayerFilter, inObjectLayerFilter);
}
else
mSystem->GetNarrowPhaseQuery().CollideShape(inShape, Vec3::sReplicate(1.0f), transform, settings, inBaseOffset, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
{
// Version that uses the cached active edges
settings.mActiveEdgeMode = EActiveEdgeMode::CollideOnlyWithActive;
mSystem->GetNarrowPhaseQuery().CollideShape(inShape, Vec3::sReplicate(1.0f), transform, settings, inBaseOffset, ioCollector, inBroadPhaseLayerFilter, inObjectLayerFilter, body_filter, inShapeFilter);
}
// Also collide with other characters
if (mCharacterVsCharacterCollision != nullptr)
{
ioCollector.SetContext(nullptr); // We're no longer colliding with a transformed shape, reset
mCharacterVsCharacterCollision->CollideCharacter(this, transform, settings, inBaseOffset, ioCollector);
}
}
void CharacterVirtual::GetContactsAtPosition(RVec3Arg inPosition, Vec3Arg inMovementDirection, const Shape *inShape, TempContactList &outContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const
@@ -299,9 +437,12 @@ void CharacterVirtual::GetContactsAtPosition(RVec3Arg inPosition, Vec3Arg inMove
// Remove previous results
outContacts.clear();
// Body filter
IgnoreSingleBodyFilterChained body_filter(mInnerBodyID, inBodyFilter);
// Collide shape
ContactCollector collector(mSystem, this, mMaxNumHits, mHitReductionCosMaxAngle, mUp, mPosition, outContacts);
CheckCollision(inPosition, mRotation, inMovementDirection, mPredictiveContactDistance, inShape, mPosition, collector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
CheckCollision(inPosition, mRotation, inMovementDirection, mPredictiveContactDistance, inShape, mPosition, collector, inBroadPhaseLayerFilter, inObjectLayerFilter, body_filter, inShapeFilter);
// The broadphase bounding boxes will not be deterministic, which means that the order in which the contacts are received by the collector is not deterministic.
// Therefore we need to sort the contacts to preserve determinism. Note that currently this will fail if we exceed mMaxNumHits hits.
@@ -313,7 +454,12 @@ void CharacterVirtual::GetContactsAtPosition(RVec3Arg inPosition, Vec3Arg inMove
// Reduce distance to contact by padding to ensure we stay away from the object by a little margin
// (this will make collision detection cheaper - especially for sweep tests as they won't hit the surface if we're properly sliding)
for (Contact &c : outContacts)
{
c.mDistance -= mCharacterPadding;
if (c.mCharacterB != nullptr)
c.mDistance -= c.mCharacterB->mCharacterPadding;
}
}
void CharacterVirtual::RemoveConflictingContacts(TempContactList &ioContacts, IgnoredContactList &outIgnoredContacts) const
@@ -330,7 +476,7 @@ void CharacterVirtual::RemoveConflictingContacts(TempContactList &ioContacts, Ig
for (size_t c2 = c1 + 1; c2 < ioContacts.size(); c2++)
{
Contact &contact2 = ioContacts[c2];
if (contact1.mBodyB == contact2.mBodyB // Only same body
if (contact1.IsSameBody(contact2)
&& contact2.mDistance <= -cMinRequiredPenetration // Only for penetrations
&& contact1.mContactNormal.Dot(contact2.mContactNormal) < 0.0f) // Only opposing normals
{
@@ -360,7 +506,21 @@ bool CharacterVirtual::ValidateContact(const Contact &inContact) const
if (mListener == nullptr)
return true;
return mListener->OnContactValidate(this, inContact.mBodyB, inContact.mSubShapeIDB);
if (inContact.mCharacterB != nullptr)
return mListener->OnCharacterContactValidate(this, inContact.mCharacterB, inContact.mSubShapeIDB);
else
return mListener->OnContactValidate(this, inContact.mBodyB, inContact.mSubShapeIDB);
}
void CharacterVirtual::ContactAdded(const Contact &inContact, CharacterContactSettings &ioSettings) const
{
if (mListener != nullptr)
{
if (inContact.mCharacterB != nullptr)
mListener->OnCharacterContactAdded(this, inContact.mCharacterB, inContact.mSubShapeIDB, inContact.mPosition, -inContact.mContactNormal, ioSettings);
else
mListener->OnContactAdded(this, inContact.mBodyB, inContact.mSubShapeIDB, inContact.mPosition, -inContact.mContactNormal, ioSettings);
}
}
template <class T>
@@ -410,30 +570,58 @@ bool CharacterVirtual::GetFirstContactForSweep(RVec3Arg inPosition, Vec3Arg inDi
// Calculate how much extra fraction we need to add to the cast to account for the character padding
float character_padding_fraction = mCharacterPadding / sqrt(displacement_len_sq);
// Body filter
IgnoreSingleBodyFilterChained body_filter(mInnerBodyID, inBodyFilter);
// Cast shape
Contact contact;
contact.mFraction = 1.0f + character_padding_fraction;
ContactCastCollector collector(mSystem, this, inDisplacement, mUp, inIgnoredContacts, start.GetTranslation(), contact);
RVec3 base_offset = start.GetTranslation();
ContactCastCollector collector(mSystem, this, inDisplacement, mUp, inIgnoredContacts, base_offset, contact);
collector.ResetEarlyOutFraction(contact.mFraction);
RShapeCast shape_cast(mShape, Vec3::sReplicate(1.0f), start, inDisplacement);
mSystem->GetNarrowPhaseQuery().CastShape(shape_cast, settings, start.GetTranslation(), collector, inBroadPhaseLayerFilter, inObjectLayerFilter, inBodyFilter, inShapeFilter);
if (contact.mBodyB.IsInvalid())
mSystem->GetNarrowPhaseQuery().CastShape(shape_cast, settings, base_offset, collector, inBroadPhaseLayerFilter, inObjectLayerFilter, body_filter, inShapeFilter);
// Also collide with other characters
if (mCharacterVsCharacterCollision != nullptr)
{
collector.SetContext(nullptr); // We're no longer colliding with a transformed shape, reset
mCharacterVsCharacterCollision->CastCharacter(this, start, inDisplacement, settings, base_offset, collector);
}
if (contact.mBodyB.IsInvalid() && contact.mCharacterB == nullptr)
return false;
// Store contact
outContact = contact;
TransformedShape ts;
float character_padding = mCharacterPadding;
if (outContact.mCharacterB != nullptr)
{
// Create a transformed shape for the character
RMat44 com = outContact.mCharacterB->GetCenterOfMassTransform();
ts = TransformedShape(com.GetTranslation(), com.GetQuaternion(), outContact.mCharacterB->GetShape(), BodyID(), SubShapeIDCreator());
// We need to take the other character's padding into account as well
character_padding += outContact.mCharacterB->mCharacterPadding;
}
else
{
// Create a transformed shape for the body
ts = mSystem->GetBodyInterface().GetTransformedShape(outContact.mBodyB);
}
// Fetch the face we're colliding with
TransformedShape ts = mSystem->GetBodyInterface().GetTransformedShape(outContact.mBodyB);
Shape::SupportingFace face;
ts.GetSupportingFace(outContact.mSubShapeIDB, -outContact.mContactNormal, start.GetTranslation(), face);
ts.GetSupportingFace(outContact.mSubShapeIDB, -outContact.mContactNormal, base_offset, face);
bool corrected = false;
if (face.size() >= 2)
{
// Inflate the colliding face by the character padding
PolygonConvexSupport polygon(face);
AddConvexRadius add_cvx(polygon, mCharacterPadding);
AddConvexRadius add_cvx(polygon, character_padding);
// Correct fraction to hit this inflated face instead of the inner shape
corrected = sCorrectFractionForCharacterPadding(mShape, start.GetRotation(), inDisplacement, add_cvx, outContact.mFraction);
@@ -503,8 +691,7 @@ bool CharacterVirtual::HandleContact(Vec3Arg inVelocity, Constraint &ioConstrain
// Send contact added event
CharacterContactSettings settings;
if (mListener != nullptr)
mListener->OnContactAdded(this, contact.mBodyB, contact.mSubShapeIDB, contact.mPosition, -contact.mContactNormal, settings);
ContactAdded(contact, settings);
contact.mCanPushCharacter = settings.mCanPushCharacter;
// We don't have any further interaction with sensors beyond an OnContactAdded notification
@@ -776,7 +963,12 @@ void CharacterVirtual::SolveConstraints(Vec3Arg inVelocity, float inDeltaTime, f
// Allow application to modify calculated velocity
if (mListener != nullptr)
mListener->OnContactSolve(this, constraint->mContact->mBodyB, constraint->mContact->mSubShapeIDB, constraint->mContact->mPosition, constraint->mContact->mContactNormal, constraint->mContact->mLinearVelocity, constraint->mContact->mMaterial, velocity, new_velocity);
{
if (constraint->mContact->mCharacterB != nullptr)
mListener->OnCharacterContactSolve(this, constraint->mContact->mCharacterB, constraint->mContact->mSubShapeIDB, constraint->mContact->mPosition, constraint->mContact->mContactNormal, constraint->mContact->mLinearVelocity, constraint->mContact->mMaterial, velocity, new_velocity);
else
mListener->OnContactSolve(this, constraint->mContact->mBodyB, constraint->mContact->mSubShapeIDB, constraint->mContact->mPosition, constraint->mContact->mContactNormal, constraint->mContact->mLinearVelocity, constraint->mContact->mMaterial, velocity, new_velocity);
}
#ifdef JPH_DEBUG_RENDERER
if (inDrawConstraints)
@@ -1088,6 +1280,14 @@ void CharacterVirtual::MoveShape(RVec3 &ioPosition, Vec3Arg inVelocity, float in
}
}
void CharacterVirtual::SetUserData(uint64 inUserData)
{
mUserData = inUserData;
if (!mInnerBodyID.IsInvalid())
mSystem->GetBodyInterface().SetUserData(mInnerBodyID, inUserData);
}
Vec3 CharacterVirtual::CancelVelocityTowardsSteepSlopes(Vec3Arg inDesiredVelocity) const
{
// If we're not pushing against a steep slope, return the desired velocity
@@ -1134,6 +1334,9 @@ void CharacterVirtual::Update(float inDeltaTime, Vec3Arg inGravity, const BroadP
// Determine the object that we're standing on
UpdateSupportingContact(false, inAllocator);
// Ensure that the rigid body ends up at the new position
UpdateInnerBodyTransform();
// If we're on the ground
if (!mGroundBodyID.IsInvalid() && mMass > 0.0f)
{
@@ -1178,6 +1381,10 @@ void CharacterVirtual::MoveToContact(RVec3Arg inPosition, const Contact &inConta
// Set the new position
SetPosition(inPosition);
// Trigger contact added callback
CharacterContactSettings dummy;
ContactAdded(inContact, dummy);
// Determine the contacts
TempContactList contacts(inAllocator);
contacts.reserve(mMaxNumHits + 1); // +1 because we can add one extra below
@@ -1202,6 +1409,9 @@ void CharacterVirtual::MoveToContact(RVec3Arg inPosition, const Contact &inConta
StoreActiveContacts(contacts, inAllocator);
JPH_ASSERT(mGroundState != EGroundState::InAir);
// Ensure that the rigid body ends up at the new position
UpdateInnerBodyTransform();
}
bool CharacterVirtual::SetShape(const Shape *inShape, float inMaxPenetrationDepth, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator)
@@ -1238,6 +1448,11 @@ bool CharacterVirtual::SetShape(const Shape *inShape, float inMaxPenetrationDept
return mShape == inShape;
}
void CharacterVirtual::SetInnerBodyShape(const Shape *inShape)
{
mSystem->GetBodyInterface().SetShape(mInnerBodyID, inShape, false, EActivation::DontActivate);
}
bool CharacterVirtual::CanWalkStairs(Vec3Arg inLinearVelocity) const
{
// We can only walk stairs if we're supported
@@ -9,11 +9,13 @@
#include <Jolt/Physics/Body/BodyFilter.h>
#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
#include <Jolt/Physics/Collision/ObjectLayer.h>
#include <Jolt/Physics/Collision/TransformedShape.h>
#include <Jolt/Core/STLTempAllocator.h>
JPH_NAMESPACE_BEGIN
class CharacterVirtual;
class CollideShapeSettings;
/// Contains the configuration of a character
class JPH_EXPORT CharacterVirtualSettings : public CharacterBaseSettings
@@ -41,14 +43,28 @@ public:
uint mMaxNumHits = 256; ///< Max num hits to collect in order to avoid excess of contact points collection
float mHitReductionCosMaxAngle = 0.999f; ///< Cos(angle) where angle is the maximum angle between two hits contact normals that are allowed to be merged during hit reduction. Default is around 2.5 degrees. Set to -1 to turn off.
float mPenetrationRecoverySpeed = 1.0f; ///< This value governs how fast a penetration will be resolved, 0 = nothing is resolved, 1 = everything in one update
/// This character can optionally have an inner rigid body. This rigid body can be used to give the character presence in the world. When set it means that:
/// - Regular collision checks (e.g. NarrowPhaseQuery::CastRay) will collide with the rigid body (they cannot collide with CharacterVirtual since it is not added to the broad phase)
/// - Regular contact callbacks will be called through the ContactListener (next to the ones that will be passed to the CharacterContactListener)
/// - Fast moving objects of motion quality LinearCast will not be able to pass through the CharacterVirtual in 1 time step
RefConst<Shape> mInnerBodyShape;
/// Layer that the inner rigid body will be added to
ObjectLayer mInnerBodyLayer = 0;
};
/// This class contains settings that allow you to override the behavior of a character's collision response
class CharacterContactSettings
{
public:
bool mCanPushCharacter = true; ///< True when the object can push the virtual character
bool mCanReceiveImpulses = true; ///< True when the virtual character can apply impulses (push) the body
/// True when the object can push the virtual character.
bool mCanPushCharacter = true;
/// True when the virtual character can apply impulses (push) the body.
/// Note that this only works against rigid bodies. Other CharacterVirtual objects can only be moved in their own update,
/// so you must ensure that in their OnCharacterContactAdded mCanPushCharacter is true.
bool mCanReceiveImpulses = true;
};
/// This class receives callbacks when a virtual character hits something.
@@ -65,6 +81,9 @@ public:
/// Checks if a character can collide with specified body. Return true if the contact is valid.
virtual bool OnContactValidate(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2) { return true; }
/// Same as OnContactValidate but when colliding with a CharacterVirtual
virtual bool OnCharacterContactValidate(const CharacterVirtual *inCharacter, const CharacterVirtual *inOtherCharacter, const SubShapeID &inSubShapeID2) { return true; }
/// Called whenever the character collides with a body.
/// @param inCharacter Character that is being solved
/// @param inBodyID2 Body ID of body that is being hit
@@ -74,6 +93,9 @@ public:
/// @param ioSettings Settings returned by the contact callback to indicate how the character should behave
virtual void OnContactAdded(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) { /* Default do nothing */ }
/// Same as OnContactAdded but when colliding with a CharacterVirtual
virtual void OnCharacterContactAdded(const CharacterVirtual *inCharacter, const CharacterVirtual *inOtherCharacter, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, CharacterContactSettings &ioSettings) { /* Default do nothing */ }
/// Called whenever a contact is being used by the solver. Allows the listener to override the resulting character velocity (e.g. by preventing sliding along certain surfaces).
/// @param inCharacter Character that is being solved
/// @param inBodyID2 Body ID of body that is being hit
@@ -85,6 +107,53 @@ public:
/// @param inCharacterVelocity World space velocity of the character prior to hitting this contact
/// @param ioNewCharacterVelocity Contains the calculated world space velocity of the character after hitting this contact, this velocity slides along the surface of the contact. Can be modified by the listener to provide an alternative velocity.
virtual void OnContactSolve(const CharacterVirtual *inCharacter, const BodyID &inBodyID2, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, Vec3Arg inContactVelocity, const PhysicsMaterial *inContactMaterial, Vec3Arg inCharacterVelocity, Vec3 &ioNewCharacterVelocity) { /* Default do nothing */ }
/// Same as OnContactSolve but when colliding with a CharacterVirtual
virtual void OnCharacterContactSolve(const CharacterVirtual *inCharacter, const CharacterVirtual *inOtherCharacter, const SubShapeID &inSubShapeID2, RVec3Arg inContactPosition, Vec3Arg inContactNormal, Vec3Arg inContactVelocity, const PhysicsMaterial *inContactMaterial, Vec3Arg inCharacterVelocity, Vec3 &ioNewCharacterVelocity) { /* Default do nothing */ }
};
/// Interface class that allows a CharacterVirtual to check collision with other CharacterVirtual instances.
/// Since CharacterVirtual instances are not registered anywhere, it is up to the application to test collision against relevant characters.
/// The characters could be stored in a tree structure to make this more efficient.
class JPH_EXPORT CharacterVsCharacterCollision : public NonCopyable
{
public:
virtual ~CharacterVsCharacterCollision() = default;
/// Collide a character against other CharacterVirtuals.
/// @param inCharacter The character to collide.
/// @param inCenterOfMassTransform Center of mass transform for this character.
/// @param inCollideShapeSettings Settings for the collision check.
/// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. GetPosition() since floats are most accurate near the origin
/// @param ioCollector Collision collector that receives the collision results.
virtual void CollideCharacter(const CharacterVirtual *inCharacter, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector) const = 0;
/// Cast a character against other CharacterVirtuals.
/// @param inCharacter The character to cast.
/// @param inCenterOfMassTransform Center of mass transform for this character.
/// @param inDirection Direction and length to cast in.
/// @param inShapeCastSettings Settings for the shape cast.
/// @param inBaseOffset All hit results will be returned relative to this offset, can be zero to get results in world position, but when you're testing far from the origin you get better precision by picking a position that's closer e.g. GetPosition() since floats are most accurate near the origin
/// @param ioCollector Collision collector that receives the collision results.
virtual void CastCharacter(const CharacterVirtual *inCharacter, RMat44Arg inCenterOfMassTransform, Vec3Arg inDirection, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector) const = 0;
};
/// Simple collision checker that loops over all registered characters.
/// Note that this is not thread safe, so make sure that only one CharacterVirtual is checking collision at a time.
class JPH_EXPORT CharacterVsCharacterCollisionSimple : public CharacterVsCharacterCollision
{
public:
/// Add a character to the list of characters to check collision against.
void Add(CharacterVirtual *inCharacter) { mCharacters.push_back(inCharacter); }
/// Remove a character from the list of characters to check collision against.
void Remove(const CharacterVirtual *inCharacter);
// See: CharacterVsCharacterCollision
virtual void CollideCharacter(const CharacterVirtual *inCharacter, RMat44Arg inCenterOfMassTransform, const CollideShapeSettings &inCollideShapeSettings, RVec3Arg inBaseOffset, CollideShapeCollector &ioCollector) const override;
virtual void CastCharacter(const CharacterVirtual *inCharacter, RMat44Arg inCenterOfMassTransform, Vec3Arg inDirection, const ShapeCastSettings &inShapeCastSettings, RVec3Arg inBaseOffset, CastShapeCollector &ioCollector) const override;
Array<CharacterVirtual *> mCharacters; ///< The list of characters to check collision against
};
/// Runtime character object.
@@ -102,18 +171,24 @@ public:
/// @param inPosition Initial position for the character
/// @param inRotation Initial rotation for the character (usually only around the up-axis)
/// @param inUserData Application specific value
/// @param inSystem Physics system that this character will be added to later
/// @param inSystem Physics system that this character will be added to
CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, uint64 inUserData, PhysicsSystem *inSystem);
/// Constructor without user data
CharacterVirtual(const CharacterVirtualSettings *inSettings, RVec3Arg inPosition, QuatArg inRotation, PhysicsSystem *inSystem) : CharacterVirtual(inSettings, inPosition, inRotation, 0, inSystem) { }
/// Destructor
virtual ~CharacterVirtual() override;
/// Set the contact listener
void SetListener(CharacterContactListener *inListener) { mListener = inListener; }
/// Get the current contact listener
CharacterContactListener * GetListener() const { return mListener; }
/// Set the character vs character collision interface
void SetCharacterVsCharacterCollision(CharacterVsCharacterCollision *inCharacterVsCharacterCollision) { mCharacterVsCharacterCollision = inCharacterVsCharacterCollision; }
/// Get the linear velocity of the character (m / s)
Vec3 GetLinearVelocity() const { return mLinearVelocity; }
@@ -124,13 +199,16 @@ public:
RVec3 GetPosition() const { return mPosition; }
/// Set the position of the character
void SetPosition(RVec3Arg inPosition) { mPosition = inPosition; }
void SetPosition(RVec3Arg inPosition) { mPosition = inPosition; UpdateInnerBodyTransform(); }
/// Get the rotation of the character
Quat GetRotation() const { return mRotation; }
/// Set the rotation of the character
void SetRotation(QuatArg inRotation) { mRotation = inRotation; }
void SetRotation(QuatArg inRotation) { mRotation = inRotation; UpdateInnerBodyTransform(); }
// Get the center of mass position of the shape
inline RVec3 GetCenterOfMassPosition() const { return mPosition + (mRotation * (mShapeOffset + mShape->GetCenterOfMass()) + mCharacterPadding * mUp); }
/// Calculate the world transform of the character
RMat44 GetWorldTransform() const { return RMat44::sRotationTranslation(mRotation, mPosition); }
@@ -173,11 +251,14 @@ public:
/// An extra offset applied to the shape in local space. This allows applying an extra offset to the shape in local space. Note that setting it on the fly can cause the shape to teleport into collision.
Vec3 GetShapeOffset() const { return mShapeOffset; }
void SetShapeOffset(Vec3Arg inShapeOffset) { mShapeOffset = inShapeOffset; }
void SetShapeOffset(Vec3Arg inShapeOffset) { mShapeOffset = inShapeOffset; UpdateInnerBodyTransform(); }
/// Access to the user data, can be used for anything by the application
uint64 GetUserData() const { return mUserData; }
void SetUserData(uint64 inUserData) { mUserData = inUserData; }
void SetUserData(uint64 inUserData);
/// Optional inner rigid body that proxies the character in the world. Can be used to update body properties.
BodyID GetInnerBodyID() const { return mInnerBodyID; }
/// This function can be called prior to calling Update() to convert a desired velocity into a velocity that won't make the character move further onto steep slopes.
/// This velocity can then be set on the character using SetLinearVelocity()
@@ -271,7 +352,14 @@ public:
/// @return Returns true if the switch succeeded.
bool SetShape(const Shape *inShape, float inMaxPenetrationDepth, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator);
/// @brief Get all contacts for the character at a particular location
/// Updates the shape of the inner rigid body. Should be called after a successful call to SetShape.
void SetInnerBodyShape(const Shape *inShape);
/// Get the transformed shape that represents the volume of the character, can be used for collision checks.
TransformedShape GetTransformedShape() const { return TransformedShape(GetCenterOfMassPosition(), mRotation, mShape, mInnerBodyID); }
/// @brief Get all contacts for the character at a particular location.
/// When colliding with another character virtual, this pointer will be provided through CollideShapeCollector::SetUserContext before adding a hit.
/// @param inPosition Position to test, note that this position will be corrected for the character padding.
/// @param inRotation Rotation at which to test the shape.
/// @param inMovementDirection A hint in which direction the character is moving, will be used to calculate a proper normal.
@@ -302,13 +390,17 @@ public:
void SaveState(StateRecorder &inStream) const;
void RestoreState(StateRecorder &inStream);
// Checks if two contacts refer to the same body (or virtual character)
inline bool IsSameBody(const Contact &inOther) const { return mBodyB == inOther.mBodyB && mCharacterB == inOther.mCharacterB; }
RVec3 mPosition; ///< Position where the character makes contact
Vec3 mLinearVelocity; ///< Velocity of the contact point
Vec3 mContactNormal; ///< Contact normal, pointing towards the character
Vec3 mSurfaceNormal; ///< Surface normal of the contact
float mDistance; ///< Distance to the contact <= 0 means that it is an actual contact, > 0 means predictive
float mFraction; ///< Fraction along the path where this contact takes place
BodyID mBodyB; ///< ID of body we're colliding with
BodyID mBodyB; ///< ID of body we're colliding with (if not invalid)
CharacterVirtual * mCharacterB = nullptr; ///< Character we're colliding with (if not null)
SubShapeID mSubShapeIDB; ///< Sub shape ID of body we're colliding with
EMotionType mMotionTypeB; ///< Motion type of B, used to determine the priority of the contact
bool mIsSensorB; ///< If B is a sensor
@@ -325,6 +417,24 @@ public:
/// Access to the internal list of contacts that the character has found.
const ContactList & GetActiveContacts() const { return mActiveContacts; }
/// Check if the character is currently in contact with or has collided with another body in the last time step
bool HasCollidedWith(const BodyID &inBody) const
{
for (const CharacterVirtual::Contact &c : mActiveContacts)
if (c.mHadCollision && c.mBodyB == inBody)
return true;
return false;
}
/// Check if the character is currently in contact with or has collided with another character in the last time step
bool HasCollidedWith(const CharacterVirtual *inCharacter) const
{
for (const CharacterVirtual::Contact &c : mActiveContacts)
if (c.mHadCollision && c.mCharacterB == inCharacter)
return true;
return false;
}
private:
// Sorting predicate for making contact order deterministic
struct ContactOrderingPredicate
@@ -369,12 +479,15 @@ private:
public:
ContactCollector(PhysicsSystem *inSystem, const CharacterVirtual *inCharacter, uint inMaxHits, float inHitReductionCosMaxAngle, Vec3Arg inUp, RVec3Arg inBaseOffset, TempContactList &outContacts) : mBaseOffset(inBaseOffset), mUp(inUp), mSystem(inSystem), mCharacter(inCharacter), mContacts(outContacts), mMaxHits(inMaxHits), mHitReductionCosMaxAngle(inHitReductionCosMaxAngle) { }
virtual void SetUserData(uint64 inUserData) override { mOtherCharacter = reinterpret_cast<CharacterVirtual *>(inUserData); }
virtual void AddHit(const CollideShapeResult &inResult) override;
RVec3 mBaseOffset;
Vec3 mUp;
PhysicsSystem * mSystem;
const CharacterVirtual * mCharacter;
CharacterVirtual * mOtherCharacter = nullptr;
TempContactList & mContacts;
uint mMaxHits;
float mHitReductionCosMaxAngle;
@@ -387,6 +500,8 @@ private:
public:
ContactCastCollector(PhysicsSystem *inSystem, const CharacterVirtual *inCharacter, Vec3Arg inDisplacement, Vec3Arg inUp, const IgnoredContactList &inIgnoredContacts, RVec3Arg inBaseOffset, Contact &outContact) : mBaseOffset(inBaseOffset), mDisplacement(inDisplacement), mUp(inUp), mSystem(inSystem), mCharacter(inCharacter), mIgnoredContacts(inIgnoredContacts), mContact(outContact) { }
virtual void SetUserData(uint64 inUserData) override { mOtherCharacter = reinterpret_cast<CharacterVirtual *>(inUserData); }
virtual void AddHit(const ShapeCastResult &inResult) override;
RVec3 mBaseOffset;
@@ -394,6 +509,7 @@ private:
Vec3 mUp;
PhysicsSystem * mSystem;
const CharacterVirtual * mCharacter;
CharacterVirtual * mOtherCharacter = nullptr;
const IgnoredContactList & mIgnoredContacts;
Contact & mContact;
};
@@ -401,6 +517,7 @@ private:
// Helper function to convert a Jolt collision result into a contact
template <class taCollector>
inline static void sFillContactProperties(const CharacterVirtual *inCharacter, Contact &outContact, const Body &inBody, Vec3Arg inUp, RVec3Arg inBaseOffset, const taCollector &inCollector, const CollideShapeResult &inResult);
inline static void sFillCharacterContactProperties(Contact &outContact, CharacterVirtual *inOtherCharacter, RVec3Arg inBaseOffset, const CollideShapeResult &inResult);
// Move the shape from ioPosition and try to displace it by inVelocity * inDeltaTime, this will try to slide the shape along the world geometry
void MoveShape(RVec3 &ioPosition, Vec3Arg inVelocity, float inDeltaTime, ContactList *outActiveContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter, TempAllocator &inAllocator
@@ -412,6 +529,9 @@ private:
// Ask the callback if inContact is a valid contact point
bool ValidateContact(const Contact &inContact) const;
// Trigger the contact callback for inContact and get the contact settings
void ContactAdded(const Contact &inContact, CharacterContactSettings &ioSettings) const;
// Tests the shape for collision around inPosition
void GetContactsAtPosition(RVec3Arg inPosition, Vec3Arg inMovementDirection, const Shape *inShape, TempContactList &outContacts, const BroadPhaseLayerFilter &inBroadPhaseLayerFilter, const ObjectLayerFilter &inObjectLayerFilter, const BodyFilter &inBodyFilter, const ShapeFilter &inShapeFilter) const;
@@ -457,9 +577,21 @@ private:
return RMat44::sRotationTranslation(inRotation, inPosition).PreTranslated(mShapeOffset + inShape->GetCenterOfMass()).PostTranslated(mCharacterPadding * mUp);
}
// This function returns the position of the inner rigid body
inline RVec3 GetInnerBodyPosition() const
{
return mPosition + (mRotation * mShapeOffset + mCharacterPadding * mUp);
}
// Move the inner rigid body to the current position
void UpdateInnerBodyTransform();
// Our main listener for contacts
CharacterContactListener * mListener = nullptr;
// Interface to detect collision between characters
CharacterVsCharacterCollision * mCharacterVsCharacterCollision = nullptr;
// Movement settings
EBackFaceMode mBackFaceMode; // When colliding with back faces, the character will not be able to move through back facing triangles. Use this if you have triangles that need to collide on both sides.
float mPredictiveContactDistance; // How far to scan outside of the shape for predictive contacts. A value of 0 will most likely cause the character to get stuck as it cannot properly calculate a sliding direction anymore. A value that's too high will cause ghost collisions.
@@ -502,6 +634,9 @@ private:
// User data, can be used for anything by the application
uint64 mUserData = 0;
// The inner rigid body that proxies the character in the world
BodyID mInnerBodyID;
};
JPH_NAMESPACE_END
@@ -19,7 +19,7 @@ class BroadPhaseLayer
public:
using Type = uint8;
JPH_INLINE BroadPhaseLayer() = default;
JPH_INLINE BroadPhaseLayer() = default;
JPH_INLINE explicit constexpr BroadPhaseLayer(Type inValue) : mValue(inValue) { }
JPH_INLINE constexpr BroadPhaseLayer(const BroadPhaseLayer &) = default;
JPH_INLINE BroadPhaseLayer & operator = (const BroadPhaseLayer &) = default;
@@ -57,7 +57,7 @@ private:
static constexpr BroadPhaseLayer cBroadPhaseLayerInvalid(0xff);
/// Interface that the application should implement to allow mapping object layers to broadphase layers
class BroadPhaseLayerInterface : public NonCopyable
class JPH_EXPORT BroadPhaseLayerInterface : public NonCopyable
{
public:
/// Destructor
@@ -76,7 +76,7 @@ public:
};
/// Class to test if an object can collide with a broadphase layer. Used while finding collision pairs.
class ObjectVsBroadPhaseLayerFilter : public NonCopyable
class JPH_EXPORT ObjectVsBroadPhaseLayerFilter : public NonCopyable
{
public:
/// Destructor
@@ -90,7 +90,7 @@ public:
};
/// Filter class for broadphase layers
class BroadPhaseLayerFilter : public NonCopyable
class JPH_EXPORT BroadPhaseLayerFilter : public NonCopyable
{
public:
/// Destructor
@@ -104,7 +104,7 @@ public:
};
/// Default filter class that uses the pair filter in combination with a specified layer to filter layers
class DefaultBroadPhaseLayerFilter : public BroadPhaseLayerFilter
class JPH_EXPORT DefaultBroadPhaseLayerFilter : public BroadPhaseLayerFilter
{
public:
/// Constructor
@@ -126,7 +126,7 @@ private:
};
/// Allows objects from a specific broad phase layer only
class SpecifiedBroadPhaseLayerFilter : public BroadPhaseLayerFilter
class JPH_EXPORT SpecifiedBroadPhaseLayerFilter : public BroadPhaseLayerFilter
{
public:
/// Constructor
@@ -33,11 +33,11 @@ private:
JPH_OVERRIDE_NEW_DELETE
/// Default constructor does not initialize
inline NodeID() = default;
inline NodeID() = default;
/// Construct a node ID
static inline NodeID sInvalid() { return NodeID(cInvalidNodeIndex); }
static inline NodeID sFromBodyID(BodyID inID) { NodeID node_id(inID.GetIndexAndSequenceNumber()); JPH_ASSERT(node_id.IsBody()); return node_id; }
static inline NodeID sFromBodyID(BodyID inID) { NodeID node_id(inID.GetIndexAndSequenceNumber()); JPH_ASSERT(node_id.IsBody()); return node_id; }
static inline NodeID sFromNodeIndex(uint32 inIdx) { NodeID node_id(inIdx | cIsNode); JPH_ASSERT(node_id.IsNode()); return node_id; }
/// Check what type of ID it is
@@ -70,6 +70,9 @@ public:
void SetContext(const TransformedShape *inContext) { mContext = inContext; }
const TransformedShape *GetContext() const { return mContext; }
/// This function can be used to set some user data on the collision collector
virtual void SetUserData(uint64 inUserData) { /* Does nothing by default */ }
/// This function will be called for every hit found, it's up to the application to decide how to store the hit
virtual void AddHit(const ResultType &inResult) = 0;
@@ -24,7 +24,7 @@ JPH_NAMESPACE_BEGIN
static constexpr ObjectLayer cObjectLayerInvalid = ObjectLayer(~ObjectLayer(0U));
/// Filter class for object layers
class ObjectLayerFilter : public NonCopyable
class JPH_EXPORT ObjectLayerFilter : public NonCopyable
{
public:
/// Destructor
@@ -46,7 +46,7 @@ public:
};
/// Filter class to test if two objects can collide based on their object layer. Used while finding collision pairs.
class ObjectLayerPairFilter : public NonCopyable
class JPH_EXPORT ObjectLayerPairFilter : public NonCopyable
{
public:
/// Destructor
@@ -60,7 +60,7 @@ public:
};
/// Default filter class that uses the pair filter in combination with a specified layer to filter layers
class DefaultObjectLayerFilter : public ObjectLayerFilter
class JPH_EXPORT DefaultObjectLayerFilter : public ObjectLayerFilter
{
public:
/// Constructor
@@ -89,7 +89,7 @@ private:
};
/// Allows objects from a specific layer only
class SpecifiedObjectLayerFilter : public ObjectLayerFilter
class JPH_EXPORT SpecifiedObjectLayerFilter : public ObjectLayerFilter
{
public:
/// Constructor
@@ -44,7 +44,7 @@ public:
BoxShape(Vec3Arg inHalfExtent, float inConvexRadius = cDefaultConvexRadius, const PhysicsMaterial *inMaterial = nullptr) : ConvexShape(EShapeSubType::Box, inMaterial), mHalfExtent(inHalfExtent), mConvexRadius(inConvexRadius) { JPH_ASSERT(inConvexRadius >= 0.0f); JPH_ASSERT(inHalfExtent.ReduceMin() >= inConvexRadius); }
/// Get half extent of box
Vec3 GetHalfExtent() const { return mHalfExtent; }
Vec3 GetHalfExtent() const { return mHalfExtent; }
// See Shape::GetLocalBounds
virtual AABox GetLocalBounds() const override { return AABox(-mHalfExtent, mHalfExtent); }
@@ -380,15 +380,6 @@ void CapsuleShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec
}
}
void CapsuleShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const
{
Vec3 scale;
Mat44 transform = inCenterOfMassTransform.Decompose(scale);
TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator());
ts.SetShapeScale(ScaleHelpers::MakeUniformScale(scale.Abs()));
ioCollector.AddHit(ts);
}
void CapsuleShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const
{
JPH_ASSERT(IsValidScale(inScale));
@@ -436,6 +427,13 @@ bool CapsuleShape::IsValidScale(Vec3Arg inScale) const
return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs());
}
Vec3 CapsuleShape::MakeScaleValid(Vec3Arg inScale) const
{
Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);
return scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs());
}
void CapsuleShape::sRegister()
{
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Capsule);
@@ -88,9 +88,6 @@ public:
// See: Shape::CollideSoftBodyVertices
virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override;
// See Shape::TransformShape
virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override;
// See Shape::GetTrianglesStart
virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override;
@@ -109,6 +106,9 @@ public:
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override;
// See Shape::MakeScaleValid
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;
// Register shape functions with the registry
static void sRegister();
@@ -388,6 +388,20 @@ bool CompoundShape::IsValidScale(Vec3Arg inScale) const
return true;
}
Vec3 CompoundShape::MakeScaleValid(Vec3Arg inScale) const
{
Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);
if (CompoundShape::IsValidScale(scale))
return scale;
Vec3 abs_uniform_scale = ScaleHelpers::MakeUniformScale(scale.Abs());
Vec3 uniform_scale = scale.GetSign() * abs_uniform_scale;
if (CompoundShape::IsValidScale(uniform_scale))
return uniform_scale;
return Sign(scale.GetX()) * abs_uniform_scale;
}
void CompoundShape::sRegister()
{
for (EShapeSubType s1 : sCompoundSubShapeTypes)
@@ -298,6 +298,9 @@ public:
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override;
// See Shape::MakeScaleValid
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;
// Register shape functions with the registry
static void sRegister();
@@ -19,7 +19,7 @@ JPH_NAMESPACE_BEGIN
struct CompoundShape::CastRayVisitor
{
JPH_INLINE CastRayVisitor(const RayCast &inRay, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) :
JPH_INLINE CastRayVisitor(const RayCast &inRay, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, RayCastResult &ioHit) :
mRay(inRay),
mHit(ioHit),
mSubShapeIDCreator(inSubShapeIDCreator),
@@ -64,7 +64,7 @@ struct CompoundShape::CastRayVisitor
struct CompoundShape::CastRayVisitorCollector
{
JPH_INLINE CastRayVisitorCollector(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) :
JPH_INLINE CastRayVisitorCollector(const RayCast &inRay, const RayCastSettings &inRayCastSettings, const CompoundShape *inShape, const SubShapeIDCreator &inSubShapeIDCreator, CastRayCollector &ioCollector, const ShapeFilter &inShapeFilter) :
mRay(inRay),
mCollector(ioCollector),
mSubShapeIDCreator(inSubShapeIDCreator),
@@ -225,7 +225,7 @@ struct CompoundShape::CastShapeVisitor
struct CompoundShape::CollectTransformedShapesVisitor
{
JPH_INLINE CollectTransformedShapesVisitor(const AABox &inBox, const CompoundShape *inShape, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) :
JPH_INLINE CollectTransformedShapesVisitor(const AABox &inBox, const CompoundShape *inShape, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale, const SubShapeIDCreator &inSubShapeIDCreator, TransformedShapeCollector &ioCollector, const ShapeFilter &inShapeFilter) :
mBox(inBox),
mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox),
mPositionCOM(inPositionCOM),
@@ -271,8 +271,8 @@ struct CompoundShape::CollectTransformedShapesVisitor
inSubShape.mShape->CollectTransformedShapes(mBox, position, rotation, inSubShape.TransformScale(mScale), sub_shape_id, mCollector, mShapeFilter);
}
AABox mBox;
OrientedBox mLocalBox;
AABox mBox;
OrientedBox mLocalBox;
Vec3 mPositionCOM;
Quat mRotation;
Vec3 mScale;
@@ -360,17 +360,6 @@ void CylinderShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Ve
}
}
void CylinderShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const
{
Vec3 scale;
Mat44 transform = inCenterOfMassTransform.Decompose(scale);
TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator());
Vec3 abs_scale = scale.Abs();
float xz = 0.5f * (abs_scale.GetX() + abs_scale.GetZ());
ts.SetShapeScale(Vec3(xz, abs_scale.GetY(), xz));
ioCollector.AddHit(ts);
}
void CylinderShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const
{
Mat44 unit_cylinder_transform(Vec4(mRadius, 0, 0, 0), Vec4(0, mHalfHeight, 0, 0), Vec4(0, 0, mRadius, 0), Vec4(0, 0, 0, 1));
@@ -407,6 +396,15 @@ bool CylinderShape::IsValidScale(Vec3Arg inScale) const
return ConvexShape::IsValidScale(inScale) && abs_scale.Swizzle<SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>().IsClose(abs_scale, ScaleHelpers::cScaleToleranceSq);
}
Vec3 CylinderShape::MakeScaleValid(Vec3Arg inScale) const
{
Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);
// Average X and Z
Vec3 abs_scale = scale.Abs();
return 0.5f * scale.GetSign() * (abs_scale + abs_scale.Swizzle<SWIZZLE_Z, SWIZZLE_Y, SWIZZLE_X>());
}
void CylinderShape::sRegister()
{
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Cylinder);
@@ -83,9 +83,6 @@ public:
// See: Shape::CollideSoftBodyVertices
virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override;
// See Shape::TransformShape
virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override;
// See Shape::GetTrianglesStart
virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override;
@@ -107,6 +104,9 @@ public:
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override;
// See Shape::MakeScaleValid
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;
// Register shape functions with the registry
static void sRegister();
@@ -63,6 +63,12 @@ public:
// See Shape::GetStatsRecursive
virtual Stats GetStatsRecursive(VisitedShapes &ioVisitedShapes) const override;
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override { return mInnerShape->IsValidScale(inScale); }
// See Shape::MakeScaleValid
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override { return mInnerShape->MakeScaleValid(inScale); }
protected:
RefConst<Shape> mInnerShape;
};
@@ -392,7 +392,7 @@ Vec3 MeshShape::GetSurfaceNormal(const SubShapeID &inSubShapeID, Vec3Arg inLocal
const TriangleCodec::DecodingContext triangle_ctx(sGetTriangleHeader(mTree));
triangle_ctx.GetTriangle(block_start, triangle_idx, v1, v2, v3);
// Calculate normal
// Calculate normal
return (v3 - v2).Cross(v1 - v2).Normalized();
}
@@ -588,7 +588,7 @@ void MeshShape::Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransfor
{
struct Visitor
{
JPH_INLINE Visitor(DebugRenderer *inRenderer, RMat44Arg inTransform) :
JPH_INLINE Visitor(DebugRenderer *inRenderer, RMat44Arg inTransform) :
mRenderer(inRenderer),
mTransform(inTransform)
{
@@ -938,7 +938,7 @@ void MeshShape::sCastSphereVsMesh(const ShapeCast &inShapeCast, const ShapeCastS
struct MeshShape::MSGetTrianglesContext
{
JPH_INLINE MSGetTrianglesContext(const MeshShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) :
JPH_INLINE MSGetTrianglesContext(const MeshShape *inShape, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) :
mDecodeCtx(sGetNodeHeader(inShape->mTree)),
mShape(inShape),
mLocalBox(Mat44::sInverseRotationTranslation(inRotation, inPositionCOM), inBox),
@@ -87,6 +87,21 @@ void MutableCompoundShape::AdjustCenterOfMass()
for (CompoundShape::SubShape &sub_shape : mSubShapes)
sub_shape.SetPositionCOM(sub_shape.GetPositionCOM() - center_of_mass);
// Update bounding boxes
for (Bounds &bounds : mSubShapeBounds)
{
Vec4 xxxx = center_of_mass.SplatX();
Vec4 yyyy = center_of_mass.SplatY();
Vec4 zzzz = center_of_mass.SplatZ();
bounds.mMinX -= xxxx;
bounds.mMinY -= yyyy;
bounds.mMinZ -= zzzz;
bounds.mMaxX -= xxxx;
bounds.mMaxY -= yyyy;
bounds.mMaxZ -= zzzz;
}
mLocalBounds.Translate(-center_of_mass);
// And adjust the center of mass for this shape in the opposite direction
mCenterOfMass += center_of_mass;
}
@@ -162,7 +177,7 @@ void MutableCompoundShape::CalculateSubShapeBounds(uint inStartIdx, uint inNumbe
{
const SubShape &sub_shape = mSubShapes[sub_shape_idx];
// Tranform the shape's bounds into our local space
// Transform the shape's bounds into our local space
Mat44 transform = Mat44::sRotationTranslation(sub_shape.GetRotation(), sub_shape.GetPositionCOM());
// Get the bounding box
@@ -174,7 +189,7 @@ void MutableCompoundShape::CalculateSubShapeBounds(uint inStartIdx, uint inNumbe
bounds_max.SetColumn3(col, sub_shape_bounds.mMax);
}
// Transpose to go to strucucture of arrays format
// Transpose to go to structure of arrays format
Mat44 bounds_min_t = bounds_min.Transposed();
Mat44 bounds_max_t = bounds_max.Transposed();
@@ -120,9 +120,6 @@ public:
// See Shape::GetVolume
virtual float GetVolume() const override { return mInnerShape->GetVolume(); }
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override { return mInnerShape->IsValidScale(inScale); }
// Register shape functions with the registry
static void sRegister();
@@ -282,7 +282,7 @@ void RotatedTranslatedShape::RestoreBinaryState(StreamIn &inStream)
bool RotatedTranslatedShape::IsValidScale(Vec3Arg inScale) const
{
if (!DecoratedShape::IsValidScale(inScale))
if (!Shape::IsValidScale(inScale))
return false;
if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(inScale))
@@ -294,6 +294,24 @@ bool RotatedTranslatedShape::IsValidScale(Vec3Arg inScale) const
return mInnerShape->IsValidScale(ScaleHelpers::RotateScale(mRotation, inScale));
}
Vec3 RotatedTranslatedShape::MakeScaleValid(Vec3Arg inScale) const
{
Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);
if (mIsRotationIdentity || ScaleHelpers::IsUniformScale(scale))
return mInnerShape->MakeScaleValid(scale);
if (ScaleHelpers::CanScaleBeRotated(mRotation, scale))
return ScaleHelpers::RotateScale(mRotation.Conjugated(), mInnerShape->MakeScaleValid(ScaleHelpers::RotateScale(mRotation, scale)));
Vec3 abs_uniform_scale = ScaleHelpers::MakeUniformScale(scale.Abs());
Vec3 uniform_scale = scale.GetSign() * abs_uniform_scale;
if (ScaleHelpers::CanScaleBeRotated(mRotation, uniform_scale))
return uniform_scale;
return Sign(scale.GetX()) * abs_uniform_scale;
}
void RotatedTranslatedShape::sRegister()
{
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::RotatedTranslated);
@@ -124,6 +124,9 @@ public:
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override;
// See Shape::MakeScaleValid
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;
// Register shape functions with the registry
static void sRegister();
@@ -11,6 +11,9 @@ JPH_NAMESPACE_BEGIN
/// Helper functions to get properties of a scaling vector
namespace ScaleHelpers
{
/// Minimum valid scale value. This is used to prevent division by zero when scaling a shape with a zero scale.
static constexpr float cMinScale = 1.0e-6f;
/// The tolerance used to check if components of the scale vector are the same
static constexpr float cScaleToleranceSq = 1.0e-8f;
@@ -26,6 +29,12 @@ namespace ScaleHelpers
/// Test if a scale flips an object inside out (which requires flipping all normals and polygon windings)
inline bool IsInsideOut(Vec3Arg inScale) { return (CountBits(Vec3::sLess(inScale, Vec3::sZero()).GetTrues() & 0x7) & 1) != 0; }
/// Test if any of the components of the scale have a value below cMinScale
inline bool IsZeroScale(Vec3Arg inScale) { return Vec3::sLess(inScale.Abs(), Vec3::sReplicate(cMinScale)).TestAnyXYZTrue(); }
/// Ensure that the scale for each component is at least cMinScale
inline Vec3 MakeNonZeroScale(Vec3Arg inScale) { return inScale.GetSign() * Vec3::sMax(inScale.Abs(), Vec3::sReplicate(cMinScale)); }
/// Get the average scale if inScale, used to make the scale uniform when a shape doesn't support non-uniform scale
inline Vec3 MakeUniformScale(Vec3Arg inScale) { return Vec3::sReplicate((inScale.GetX() + inScale.GetY() + inScale.GetZ()) / 3.0f); }
@@ -5,6 +5,7 @@
#include <Jolt/Jolt.h>
#include <Jolt/Physics/Collision/Shape/ScaledShape.h>
#include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>
#include <Jolt/Physics/Collision/RayCast.h>
#include <Jolt/Physics/Collision/ShapeCast.h>
#include <Jolt/Physics/Collision/TransformedShape.h>
@@ -36,6 +37,12 @@ ScaledShape::ScaledShape(const ScaledShapeSettings &inSettings, ShapeResult &out
if (outResult.HasError())
return;
if (ScaleHelpers::IsZeroScale(inSettings.mScale))
{
outResult.SetError("Can't use zero scale!");
return;
}
outResult.Set(this);
}
@@ -175,6 +182,11 @@ bool ScaledShape::IsValidScale(Vec3Arg inScale) const
return mInnerShape->IsValidScale(inScale * mScale);
}
Vec3 ScaledShape::MakeScaleValid(Vec3Arg inScale) const
{
return mInnerShape->MakeScaleValid(mScale * inScale) / mScale;
}
void ScaledShape::sCollideScaledVsShape(const Shape *inShape1, const Shape *inShape2, Vec3Arg inScale1, Vec3Arg inScale2, Mat44Arg inCenterOfMassTransform1, Mat44Arg inCenterOfMassTransform2, const SubShapeIDCreator &inSubShapeIDCreator1, const SubShapeIDCreator &inSubShapeIDCreator2, const CollideShapeSettings &inCollideShapeSettings, CollideShapeCollector &ioCollector, const ShapeFilter &inShapeFilter)
{
JPH_ASSERT(inShape1->GetSubType() == EShapeSubType::Scaled);
@@ -5,6 +5,7 @@
#pragma once
#include <Jolt/Physics/Collision/Shape/DecoratedShape.h>
#include <Jolt/Physics/Collision/Shape/ScaleHelpers.h>
JPH_NAMESPACE_BEGIN
@@ -42,10 +43,10 @@ public:
ScaledShape(const ScaledShapeSettings &inSettings, ShapeResult &outResult);
/// Constructor that decorates another shape with a scale
ScaledShape(const Shape *inShape, Vec3Arg inScale) : DecoratedShape(EShapeSubType::Scaled, inShape), mScale(inScale) { }
ScaledShape(const Shape *inShape, Vec3Arg inScale) : DecoratedShape(EShapeSubType::Scaled, inShape), mScale(inScale) { JPH_ASSERT(!ScaleHelpers::IsZeroScale(mScale)); }
/// Get the scale
Vec3 GetScale() const { return mScale; }
Vec3 GetScale() const { return mScale; }
// See Shape::GetCenterOfMass
virtual Vec3 GetCenterOfMass() const override { return mScale * mInnerShape->GetCenterOfMass(); }
@@ -120,6 +121,9 @@ public:
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override;
// See Shape::MakeScaleValid
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;
// Register shape functions with the registry
static void sRegister();
@@ -59,7 +59,7 @@ void Shape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCol
Vec3 scale;
Mat44 transform = inCenterOfMassTransform.Decompose(scale);
TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator());
ts.SetShapeScale(scale);
ts.SetShapeScale(MakeScaleValid(scale));
ioCollector.AddHit(ts);
}
@@ -117,7 +117,7 @@ void Shape::SaveWithChildren(StreamOut &inStream, ShapeToIDMap &ioShapeMap, Mate
// Write the ID's of all sub shapes
ShapeList sub_shapes;
SaveSubShapeState(sub_shapes);
inStream.Write(sub_shapes.size());
inStream.Write(uint32(sub_shapes.size()));
for (const Shape *shape : sub_shapes)
{
if (shape == nullptr)
@@ -173,7 +173,7 @@ Shape::ShapeResult Shape::sRestoreWithChildren(StreamIn &inStream, IDToShapeMap
ioShapeMap.push_back(result.Get());
// Read the sub shapes
size_t len;
uint32 len;
inStream.Read(len);
if (inStream.IsEOF() || inStream.IsFailed())
{
@@ -215,6 +215,16 @@ Shape::Stats Shape::GetStatsRecursive(VisitedShapes &ioVisitedShapes) const
return stats;
}
bool Shape::IsValidScale(Vec3Arg inScale) const
{
return !ScaleHelpers::IsZeroScale(inScale);
}
Vec3 Shape::MakeScaleValid(Vec3Arg inScale) const
{
return ScaleHelpers::MakeNonZeroScale(inScale);
}
Shape::ShapeResult Shape::ScaleShape(Vec3Arg inScale) const
{
const Vec3 unit_scale = Vec3::sReplicate(1.0f);
@@ -170,7 +170,7 @@ public:
static inline ShapeFunctions & sGet(EShapeSubType inSubType) { return sRegistry[int(inSubType)]; }
private:
static ShapeFunctions sRegistry[NumSubShapeTypes];
static ShapeFunctions sRegistry[NumSubShapeTypes];
};
/// Base class for all shapes (collision volume of a body). Defines a virtual interface for collision detection.
@@ -424,7 +424,14 @@ public:
/// * CylinderShape: Scale must be uniform in XZ plane, Y can scale independently (signs of scale are ignored).
/// * RotatedTranslatedShape: Scale must not cause shear in the child shape.
/// * CompoundShape: Scale must not cause shear in any of the child shapes.
virtual bool IsValidScale(Vec3Arg inScale) const { return !inScale.IsNearZero(); }
virtual bool IsValidScale(Vec3Arg inScale) const;
/// This function will make sure that if you wrap this shape in a ScaledShape that the scale is valid.
/// Note that this involves discarding components of the scale that are invalid, so the resulting scaled shape may be different than the requested scale.
/// Compare the return value of this function with the scale you passed in to detect major inconsistencies and possibly warn the user.
/// @param inScale Local space scale for this shape.
/// @return Scale that can be used to wrap this shape in a ScaledShape. IsValidScale will return true for this scale.
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const;
#ifdef JPH_DEBUG_RENDERER
/// Debug helper which draws the intersection between water and the shapes, the center of buoyancy and the submerged volume
@@ -303,15 +303,6 @@ void SphereShape::CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3
}
}
void SphereShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const
{
Vec3 scale;
Mat44 transform = inCenterOfMassTransform.Decompose(scale);
TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator());
ts.SetShapeScale(ScaleHelpers::MakeUniformScale(scale.Abs()));
ioCollector.AddHit(ts);
}
void SphereShape::GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const
{
float scaled_radius = GetScaledRadius(inScale);
@@ -342,6 +333,13 @@ bool SphereShape::IsValidScale(Vec3Arg inScale) const
return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs());
}
Vec3 SphereShape::MakeScaleValid(Vec3Arg inScale) const
{
Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);
return scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs());
}
void SphereShape::sRegister()
{
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Sphere);
@@ -83,9 +83,6 @@ public:
// See: Shape::CollideSoftBodyVertices
virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override;
// See Shape::TransformShape
virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override;
// See Shape::GetTrianglesStart
virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override;
@@ -104,6 +101,9 @@ public:
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override;
// See Shape::MakeScaleValid
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;
// Register shape functions with the registry
static void sRegister();
@@ -116,8 +116,8 @@ private:
inline float GetScaledRadius(Vec3Arg inScale) const;
// Classes for GetSupportFunction
class SphereNoConvex;
class SphereWithConvex;
class SphereNoConvex;
class SphereWithConvex;
float mRadius = 0.0f;
};
@@ -406,15 +406,6 @@ AABox TaperedCapsuleShape::GetInertiaApproximation() const
return AABox(Vec3(-avg_radius, mBottomCenter - mBottomRadius, -avg_radius), Vec3(avg_radius, mTopCenter + mTopRadius, avg_radius));
}
void TaperedCapsuleShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const
{
Vec3 scale;
Mat44 transform = inCenterOfMassTransform.Decompose(scale);
TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator());
ts.SetShapeScale(scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs()));
ioCollector.AddHit(ts);
}
void TaperedCapsuleShape::SaveBinaryState(StreamOut &inStream) const
{
ConvexShape::SaveBinaryState(inStream);
@@ -448,6 +439,13 @@ bool TaperedCapsuleShape::IsValidScale(Vec3Arg inScale) const
return ConvexShape::IsValidScale(inScale) && ScaleHelpers::IsUniformScale(inScale.Abs());
}
Vec3 TaperedCapsuleShape::MakeScaleValid(Vec3Arg inScale) const
{
Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);
return scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs());
}
void TaperedCapsuleShape::sRegister()
{
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::TaperedCapsule);
@@ -79,9 +79,6 @@ public:
virtual void Draw(DebugRenderer *inRenderer, RMat44Arg inCenterOfMassTransform, Vec3Arg inScale, ColorArg inColor, bool inUseMaterialColors, bool inDrawWireframe) const override;
#endif // JPH_DEBUG_RENDERER
// See Shape::TransformShape
virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override;
// See Shape
virtual void SaveBinaryState(StreamOut &inStream) const override;
@@ -94,6 +91,9 @@ public:
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override;
// See Shape::MakeScaleValid
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;
// Register shape functions with the registry
static void sRegister();
@@ -313,15 +313,6 @@ void TriangleShape::sCastSphereVsTriangle(const ShapeCast &inShapeCast, const Sh
caster.Cast(shape->mV1, shape->mV2, shape->mV3, 0b111, inSubShapeIDCreator2.GetID());
}
void TriangleShape::TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const
{
Vec3 scale;
Mat44 transform = inCenterOfMassTransform.Decompose(scale);
TransformedShape ts(RVec3(transform.GetTranslation()), transform.GetQuaternion(), this, BodyID(), SubShapeIDCreator());
ts.SetShapeScale(mConvexRadius == 0.0f? scale : scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs()));
ioCollector.AddHit(ts);
}
class TriangleShape::TSGetTrianglesContext
{
public:
@@ -393,6 +384,16 @@ bool TriangleShape::IsValidScale(Vec3Arg inScale) const
return ConvexShape::IsValidScale(inScale) && (mConvexRadius == 0.0f || ScaleHelpers::IsUniformScale(inScale.Abs()));
}
Vec3 TriangleShape::MakeScaleValid(Vec3Arg inScale) const
{
Vec3 scale = ScaleHelpers::MakeNonZeroScale(inScale);
if (mConvexRadius == 0.0f)
return scale;
return scale.GetSign() * ScaleHelpers::MakeUniformScale(scale.Abs());
}
void TriangleShape::sRegister()
{
ShapeFunctions &f = ShapeFunctions::sGet(EShapeSubType::Triangle);
@@ -87,9 +87,6 @@ public:
// See: Shape::CollideSoftBodyVertices
virtual void CollideSoftBodyVertices(Mat44Arg inCenterOfMassTransform, Vec3Arg inScale, SoftBodyVertex *ioVertices, uint inNumVertices, float inDeltaTime, Vec3Arg inDisplacementDueToGravity, int inCollidingShapeIndex) const override;
// See Shape::TransformShape
virtual void TransformShape(Mat44Arg inCenterOfMassTransform, TransformedShapeCollector &ioCollector) const override;
// See Shape::GetTrianglesStart
virtual void GetTrianglesStart(GetTrianglesContext &ioContext, const AABox &inBox, Vec3Arg inPositionCOM, QuatArg inRotation, Vec3Arg inScale) const override;
@@ -108,6 +105,9 @@ public:
// See Shape::IsValidScale
virtual bool IsValidScale(Vec3Arg inScale) const override;
// See Shape::MakeScaleValid
virtual Vec3 MakeScaleValid(Vec3Arg inScale) const override;
// Register shape functions with the registry
static void sRegister();
@@ -126,7 +126,7 @@ private:
class TSGetTrianglesContext;
// Classes for GetSupportFunction
class TriangleNoConvex;
class TriangleNoConvex;
class TriangleWithConvex;
Vec3 mV1;
@@ -99,7 +99,7 @@ public:
float GetCosHalfConeAngle() const { return mCosHalfConeAngle; }
///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint)
inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); }
inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); }
inline float GetTotalLambdaRotation() const { return mAngleConstraintPart.GetTotalLambda(); }
private:
@@ -210,7 +210,7 @@ public:
}
/// Return lagrange multiplier
Vec3 GetTotalLambda() const
Vec3 GetTotalLambda() const
{
return mTotalLambda;
}
@@ -243,7 +243,7 @@ public:
}
/// Return lagrange multiplier
Vec3 GetTotalLambda() const
Vec3 GetTotalLambda() const
{
return mTotalLambda;
}
@@ -218,7 +218,7 @@ public:
}
/// Return lagrange multiplier
Vec3 GetTotalLambda() const
Vec3 GetTotalLambda() const
{
return mTotalLambda;
}
@@ -408,7 +408,7 @@ void ContactConstraintManager::ManifoldCache::SaveState(StateRecorder &inStream,
}
// Write body pairs
size_t num_body_pairs = selected_bp.size();
uint32 num_body_pairs = uint32(selected_bp.size());
inStream.Write(num_body_pairs);
for (const BPKeyValue *bp_kv : selected_bp)
{
@@ -424,7 +424,7 @@ void ContactConstraintManager::ManifoldCache::SaveState(StateRecorder &inStream,
GetAllManifoldsSorted(bp, all_m);
// Write num manifolds
size_t num_manifolds = all_m.size();
uint32 num_manifolds = uint32(all_m.size());
inStream.Write(num_manifolds);
// Write all manifolds
@@ -464,7 +464,7 @@ void ContactConstraintManager::ManifoldCache::SaveState(StateRecorder &inStream,
}
// Write all CCD manifold keys
size_t num_manifolds = selected_m.size();
uint32 num_manifolds = uint32(selected_m.size());
inStream.Write(num_manifolds);
for (const MKeyValue *m_kv : selected_m)
inStream.Write(m_kv->GetKey());
@@ -485,13 +485,13 @@ bool ContactConstraintManager::ManifoldCache::RestoreState(const ManifoldCache &
inReadCache.GetAllBodyPairsSorted(all_bp);
// Read amount of body pairs
size_t num_body_pairs;
uint32 num_body_pairs;
if (inStream.IsValidating())
num_body_pairs = all_bp.size();
num_body_pairs = uint32(all_bp.size());
inStream.Read(num_body_pairs);
// Read entire cache
for (size_t i = 0; i < num_body_pairs; ++i)
for (uint32 i = 0; i < num_body_pairs; ++i)
{
// Read key
BodyPair body_pair_key;
@@ -521,13 +521,13 @@ bool ContactConstraintManager::ManifoldCache::RestoreState(const ManifoldCache &
inReadCache.GetAllManifoldsSorted(all_bp[i]->GetValue(), all_m);
// Read amount of manifolds
size_t num_manifolds;
uint32 num_manifolds;
if (inStream.IsValidating())
num_manifolds = all_m.size();
num_manifolds = uint32(all_m.size());
inStream.Read(num_manifolds);
uint32 handle = ManifoldMap::cInvalidHandle;
for (size_t j = 0; j < num_manifolds; ++j)
for (uint32 j = 0; j < num_manifolds; ++j)
{
// Read key
SubShapeIDPair sub_shape_key;
@@ -573,12 +573,12 @@ bool ContactConstraintManager::ManifoldCache::RestoreState(const ManifoldCache &
inReadCache.GetAllCCDManifoldsSorted(all_m);
// Read amount of CCD manifolds
size_t num_manifolds;
uint32 num_manifolds;
if (inStream.IsValidating())
num_manifolds = all_m.size();
num_manifolds = uint32(all_m.size());
inStream.Read(num_manifolds);
for (size_t j = 0; j < num_manifolds; ++j)
for (uint32 j = 0; j < num_manifolds; ++j)
{
// Read key
SubShapeIDPair sub_shape_key;
@@ -83,7 +83,7 @@ public:
void SetLimitsSpringSettings(const SpringSettings &inLimitsSpringSettings) { mLimitsSpringSettings = inLimitsSpringSettings; }
///@name Get Lagrange multiplier from last physics update (the linear impulse applied to satisfy the constraint)
inline float GetTotalLambdaPosition() const { return mAxisConstraint.GetTotalLambda(); }
inline float GetTotalLambdaPosition() const { return mAxisConstraint.GetTotalLambda(); }
private:
// Internal helper function to calculate the values below
@@ -28,7 +28,11 @@ public:
EConstraintSpace mSpace = EConstraintSpace::WorldSpace;
/// Body 1 constraint reference frame (space determined by mSpace).
/// Hinge axis is the axis where rotation is allowed, normal axis defines the 0 angle of the hinge.
/// Hinge axis is the axis where rotation is allowed.
/// When the normal axis of both bodies align in world space, the hinge angle is defined to be 0.
/// mHingeAxis1 and mNormalAxis1 should be perpendicular. mHingeAxis2 and mNormalAxis2 should also be perpendicular.
/// If you configure the joint in world space and create both bodies with a relative rotation you want to be defined as zero,
/// you can simply set mHingeAxis1 = mHingeAxis2 and mNormalAxis1 = mNormalAxis2.
RVec3 mPoint1 = RVec3::sZero();
Vec3 mHingeAxis1 = Vec3::sAxisY();
Vec3 mNormalAxis1 = Vec3::sAxisX();
@@ -38,7 +42,7 @@ public:
Vec3 mHingeAxis2 = Vec3::sAxisY();
Vec3 mNormalAxis2 = Vec3::sAxisX();
/// Bodies are assumed to be placed so that the hinge angle = 0, movement will be limited between [mLimitsMin, mLimitsMax] where mLimitsMin e [-pi, 0] and mLimitsMax e [0, pi].
/// Rotation around the hinge axis will be limited between [mLimitsMin, mLimitsMax] where mLimitsMin e [-pi, 0] and mLimitsMax e [0, pi].
/// Both angles are in radians.
float mLimitsMin = -JPH_PI;
float mLimitsMax = JPH_PI;
@@ -86,6 +90,20 @@ public:
virtual Mat44 GetConstraintToBody1Matrix() const override;
virtual Mat44 GetConstraintToBody2Matrix() const override;
/// Get the attachment point for body 1 relative to body 1 COM (transform by Body::GetCenterOfMassTransform to take to world space)
inline Vec3 GetLocalSpacePoint1() const { return mLocalSpacePosition1; }
/// Get the attachment point for body 2 relative to body 2 COM (transform by Body::GetCenterOfMassTransform to take to world space)
inline Vec3 GetLocalSpacePoint2() const { return mLocalSpacePosition2; }
// Local space hinge directions (transform direction by Body::GetCenterOfMassTransform to take to world space)
Vec3 GetLocalSpaceHingeAxis1() const { return mLocalSpaceHingeAxis1; }
Vec3 GetLocalSpaceHingeAxis2() const { return mLocalSpaceHingeAxis2; }
// Local space normal directions (transform direction by Body::GetCenterOfMassTransform to take to world space)
Vec3 GetLocalSpaceNormalAxis1() const { return mLocalSpaceNormalAxis1; }
Vec3 GetLocalSpaceNormalAxis2() const { return mLocalSpaceNormalAxis2; }
/// Get the current rotation angle from the rest position
float GetCurrentAngle() const;
@@ -117,7 +135,7 @@ public:
void SetLimitsSpringSettings(const SpringSettings &inLimitsSpringSettings) { mLimitsSpringSettings = inLimitsSpringSettings; }
///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint)
inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); }
inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); }
inline Vector<2> GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); }
inline float GetTotalLambdaRotationLimits() const { return mRotationLimitsConstraintPart.GetTotalLambda(); }
inline float GetTotalLambdaMotor() const { return mMotorConstraintPart.GetTotalLambda(); }
@@ -139,7 +139,7 @@ static inline float sCalculateClosestPointThroughNewtonRaphson(Vec3Arg inP1, Vec
float d2dt_h01 = -d2dt_h00;
float d2dt_h11 = 6.0f * t - 2.0f;
Vec3 ddt_tangent = d2dt_h00 * inP1 + d2dt_h10 * inM1 + d2dt_h01 * inP2 + d2dt_h11 * inM2;
float d2dt = tangent.Dot(tangent) + position.Dot(ddt_tangent); // Leaving out factor 2, because we left it out above too
float d2dt = tangent.Dot(tangent) + position.Dot(ddt_tangent); // Leaving out factor 2, because we left it out above too
// If d2dt is zero, the curve is flat and there are multiple t's for which we are closest to the origin, stop now
if (d2dt == 0.0f)
@@ -66,10 +66,10 @@ public:
/// Update the attachment point for body 2
void SetPoint2(EConstraintSpace inSpace, RVec3Arg inPoint2);
/// Get the attachment point for body 1 relative to body 1 COM
/// Get the attachment point for body 1 relative to body 1 COM (transform by Body::GetCenterOfMassTransform to take to world space)
inline Vec3 GetLocalSpacePoint1() const { return mLocalSpacePosition1; }
/// Get the attachment point for body 2 relative to body 2 COM
/// Get the attachment point for body 2 relative to body 2 COM (transform by Body::GetCenterOfMassTransform to take to world space)
inline Vec3 GetLocalSpacePoint2() const { return mLocalSpacePosition2; }
// See: TwoBodyConstraint
@@ -77,7 +77,7 @@ public:
virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sTranslation(mLocalSpacePosition2); } // Note: Incorrect rotation as we don't track the original rotation difference, should not matter though as the constraint is not limiting rotation.
///@name Get Lagrange multiplier from last physics update (the linear impulse applied to satisfy the constraint)
inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); }
inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); }
private:
// Internal helper function to calculate the values below
@@ -92,7 +92,7 @@ public:
float GetCurrentLength() const { return Vec3(mWorldSpacePosition1 - mFixedPosition1).Length() + mRatio * Vec3(mWorldSpacePosition2 - mFixedPosition2).Length(); }
///@name Get Lagrange multiplier from last physics update (the linear impulse applied to satisfy the constraint)
inline float GetTotalLambdaPosition() const { return mIndependentAxisConstraintPart.GetTotalLambda(); }
inline float GetTotalLambdaPosition() const { return mIndependentAxisConstraintPart.GetTotalLambda(); }
private:
// Calculates world positions and normals and returns current length
@@ -173,15 +173,15 @@ public:
EMotorState GetMotorState(EAxis inAxis) const { return mMotorState[inAxis]; }
/// Set the target velocity in body 1 constraint space
Vec3 GetTargetVelocityCS() const { return mTargetVelocity; }
Vec3 GetTargetVelocityCS() const { return mTargetVelocity; }
void SetTargetVelocityCS(Vec3Arg inVelocity) { mTargetVelocity = inVelocity; }
/// Set the target angular velocity in body 2 constraint space (!)
void SetTargetAngularVelocityCS(Vec3Arg inAngularVelocity) { mTargetAngularVelocity = inAngularVelocity; }
Vec3 GetTargetAngularVelocityCS() const { return mTargetAngularVelocity; }
Vec3 GetTargetAngularVelocityCS() const { return mTargetAngularVelocity; }
/// Set the target position in body 1 constraint space
Vec3 GetTargetPositionCS() const { return mTargetPosition; }
Vec3 GetTargetPositionCS() const { return mTargetPosition; }
void SetTargetPositionCS(Vec3Arg inPosition) { mTargetPosition = inPosition; }
/// Set the target orientation in body 1 constraint space
@@ -193,7 +193,7 @@ public:
void SetTargetOrientationBS(QuatArg inOrientation) { SetTargetOrientationCS(mConstraintToBody1.Conjugated() * inOrientation * mConstraintToBody2); }
///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint)
inline Vec3 GetTotalLambdaPosition() const { return IsTranslationFullyConstrained()? mPointConstraintPart.GetTotalLambda() : Vec3(mTranslationConstraintPart[0].GetTotalLambda(), mTranslationConstraintPart[1].GetTotalLambda(), mTranslationConstraintPart[2].GetTotalLambda()); }
inline Vec3 GetTotalLambdaPosition() const { return IsTranslationFullyConstrained()? mPointConstraintPart.GetTotalLambda() : Vec3(mTranslationConstraintPart[0].GetTotalLambda(), mTranslationConstraintPart[1].GetTotalLambda(), mTranslationConstraintPart[2].GetTotalLambda()); }
inline Vec3 GetTotalLambdaRotation() const { return IsRotationFullyConstrained()? mRotationConstraintPart.GetTotalLambda() : Vec3(mSwingTwistConstraintPart.GetTotalTwistLambda(), mSwingTwistConstraintPart.GetTotalSwingYLambda(), mSwingTwistConstraintPart.GetTotalSwingZLambda()); }
inline Vec3 GetTotalLambdaMotorTranslation() const { return Vec3(mMotorTranslationConstraintPart[0].GetTotalLambda(), mMotorTranslationConstraintPart[1].GetTotalLambda(), mMotorTranslationConstraintPart[2].GetTotalLambda()); }
inline Vec3 GetTotalLambdaMotorRotation() const { return Vec3(mMotorRotationConstraintPart[0].GetTotalLambda(), mMotorRotationConstraintPart[1].GetTotalLambda(), mMotorRotationConstraintPart[2].GetTotalLambda()); }
@@ -123,7 +123,7 @@ public:
void SetLimitsSpringSettings(const SpringSettings &inLimitsSpringSettings) { mLimitsSpringSettings = inLimitsSpringSettings; }
///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint)
inline Vector<2> GetTotalLambdaPosition() const { return mPositionConstraintPart.GetTotalLambda(); }
inline Vector<2> GetTotalLambdaPosition() const { return mPositionConstraintPart.GetTotalLambda(); }
inline float GetTotalLambdaPositionLimits() const { return mPositionLimitsConstraintPart.GetTotalLambda(); }
inline Vec3 GetTotalLambdaRotation() const { return mRotationConstraintPart.GetTotalLambda(); }
inline float GetTotalLambdaMotor() const { return mMotorConstraintPart.GetTotalLambda(); }
@@ -41,7 +41,7 @@ public:
/// Selects the way in which the spring is defined
/// If the mode is StiffnessAndDamping then mFrequency becomes the stiffness (k) and mDamping becomes the damping ratio (c) in the spring equation F = -k * x - c * v. Otherwise the properties are as documented.
ESpringMode mMode = ESpringMode::FrequencyAndDamping;
ESpringMode mMode = ESpringMode::FrequencyAndDamping;
union
{
@@ -96,10 +96,10 @@ public:
virtual Mat44 GetConstraintToBody2Matrix() const override { return Mat44::sRotationTranslation(mConstraintToBody2, mLocalSpacePosition2); }
///@name Constraint reference frame
inline Vec3 GetLocalSpacePosition1() const { return mLocalSpacePosition1; }
inline Vec3 GetLocalSpacePosition2() const { return mLocalSpacePosition2; }
inline Quat GetConstraintToBody1() const { return mConstraintToBody1; }
inline Quat GetConstraintToBody2() const { return mConstraintToBody2; }
inline Vec3 GetLocalSpacePosition1() const { return mLocalSpacePosition1; }
inline Vec3 GetLocalSpacePosition2() const { return mLocalSpacePosition2; }
inline Quat GetConstraintToBody1() const { return mConstraintToBody1; }
inline Quat GetConstraintToBody2() const { return mConstraintToBody2; }
///@name Constraint limits
inline float GetNormalHalfConeAngle() const { return mNormalHalfConeAngle; }
@@ -131,11 +131,11 @@ public:
/// Set the target angular velocity of body 2 in constraint space of body 2
void SetTargetAngularVelocityCS(Vec3Arg inAngularVelocity) { mTargetAngularVelocity = inAngularVelocity; }
Vec3 GetTargetAngularVelocityCS() const { return mTargetAngularVelocity; }
Vec3 GetTargetAngularVelocityCS() const { return mTargetAngularVelocity; }
/// Set the target orientation in constraint space (drives constraint to: GetRotationInConstraintSpace() == inOrientation)
void SetTargetOrientationCS(QuatArg inOrientation);
Quat GetTargetOrientationCS() const { return mTargetOrientation; }
Quat GetTargetOrientationCS() const { return mTargetOrientation; }
/// Set the target orientation in body space (R2 = R1 * inOrientation, where R1 and R2 are the world space rotations for body 1 and 2).
/// Solve: R2 * ConstraintToBody2 = R1 * ConstraintToBody1 * q (see SwingTwistConstraint::GetSwingTwist) and R2 = R1 * inOrientation for q.
@@ -146,7 +146,7 @@ public:
Quat GetRotationInConstraintSpace() const;
///@name Get Lagrange multiplier from last physics update (the linear/angular impulse applied to satisfy the constraint)
inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); }
inline Vec3 GetTotalLambdaPosition() const { return mPointConstraintPart.GetTotalLambda(); }
inline float GetTotalLambdaTwist() const { return mSwingTwistConstraintPart.GetTotalTwistLambda(); }
inline float GetTotalLambdaSwingY() const { return mSwingTwistConstraintPart.GetTotalSwingYLambda(); }
inline float GetTotalLambdaSwingZ() const { return mSwingTwistConstraintPart.GetTotalSwingZLambda(); }

Some files were not shown because too many files have changed in this diff Show More