// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of NVIDIA CORPORATION nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Copyright (c) 2008-2019 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #include "foundation/PxMemory.h" #include "CctObstacleContext.h" #include "CctCharacterControllerManager.h" #include "PsUtilities.h" using namespace physx; using namespace Cct; //! Initial list size #define DEFAULT_HANDLEMANAGER_SIZE 2 HandleManager::HandleManager() : mCurrentNbObjects(0), mNbFreeIndices(0) { mMaxNbObjects = DEFAULT_HANDLEMANAGER_SIZE; mObjects = reinterpret_cast(PX_ALLOC(sizeof(void*)*mMaxNbObjects, "HandleManager")); mOutToIn = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); mInToOut = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); mStamps = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); PxMemSet(mOutToIn, 0xff, mMaxNbObjects*sizeof(PxU16)); PxMemSet(mInToOut, 0xff, mMaxNbObjects*sizeof(PxU16)); PxMemZero(mStamps, mMaxNbObjects*sizeof(PxU16)); } HandleManager::~HandleManager() { SetupLists(); } bool HandleManager::SetupLists(void** objects, PxU16* oti, PxU16* ito, PxU16* stamps) { // Release old data PX_FREE_AND_RESET(mStamps); PX_FREE_AND_RESET(mInToOut); PX_FREE_AND_RESET(mOutToIn); PX_FREE_AND_RESET(mObjects); // Assign new data mObjects = objects; mOutToIn = oti; mInToOut = ito; mStamps = stamps; return true; } Handle HandleManager::Add(void* object) { // Are there any free indices I should recycle? if(mNbFreeIndices) { const PxU16 FreeIndex = mInToOut[mCurrentNbObjects];// Get the recycled virtual index mObjects[mCurrentNbObjects] = object; // The physical location is always at the end of the list (it never has holes). mOutToIn[FreeIndex] = Ps::to16(mCurrentNbObjects++); // Update virtual-to-physical remapping table. mNbFreeIndices--; return Handle((mStamps[FreeIndex]<<16)|FreeIndex); // Return virtual index (handle) to the client app } else { PX_ASSERT_WITH_MESSAGE(mCurrentNbObjects<0xffff ,"Internal error - 64K objects in HandleManager!"); // Is the array large enough for another entry? if(mCurrentNbObjects==mMaxNbObjects) { // Nope! Resize all arrays (could be avoided with linked lists... one day) mMaxNbObjects<<=1; // The more you eat, the more you're given if(mMaxNbObjects>0xffff) mMaxNbObjects = 0xffff; // Clamp to 64K void** NewList = reinterpret_cast(PX_ALLOC(sizeof(void*)*mMaxNbObjects, "HandleManager")); // New physical list PxU16* NewOTI = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New remapping table PxU16* NewITO = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New remapping table PxU16* NewStamps = reinterpret_cast(PX_ALLOC(sizeof(PxU16)*mMaxNbObjects, "HandleManager")); // New stamps PxMemCopy(NewList, mObjects, mCurrentNbObjects*sizeof(void*)); // Copy old data PxMemCopy(NewOTI, mOutToIn, mCurrentNbObjects*sizeof(PxU16)); // Copy old data PxMemCopy(NewITO, mInToOut, mCurrentNbObjects*sizeof(PxU16)); // Copy old data PxMemCopy(NewStamps, mStamps, mCurrentNbObjects*sizeof(PxU16)); // Copy old data PxMemSet(NewOTI+mCurrentNbObjects, 0xff, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16)); PxMemSet(NewITO+mCurrentNbObjects, 0xff, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16)); PxMemZero(NewStamps+mCurrentNbObjects, (mMaxNbObjects-mCurrentNbObjects)*sizeof(PxU16)); SetupLists(NewList, NewOTI, NewITO, NewStamps); } mObjects[mCurrentNbObjects] = object; // Store object at mCurrentNbObjects = physical index = virtual index mOutToIn[mCurrentNbObjects] = Ps::to16(mCurrentNbObjects); // Update virtual-to-physical remapping table. mInToOut[mCurrentNbObjects] = Ps::to16(mCurrentNbObjects); // Update physical-to-virtual remapping table. PxU32 tmp = mCurrentNbObjects++; return (mStamps[tmp]<<16)|tmp; // Return virtual index (handle) to the client app } } void HandleManager::Remove(Handle handle) { const PxU16 VirtualIndex = PxU16(handle); if(VirtualIndex>=mMaxNbObjects) return; // Invalid handle const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get the physical index if(PhysicalIndex==0xffff) return; // Value has already been deleted if(PhysicalIndex>=mMaxNbObjects) return; // Invalid index // There must be at least one valid entry. if(mCurrentNbObjects) { if(mStamps[VirtualIndex]!=handle>>16) return; // Stamp mismatch => index has been recycled // Update list so that there's no hole mObjects[PhysicalIndex] = mObjects[--mCurrentNbObjects];// Move the real object so that the array has no holes. mOutToIn[mInToOut[mCurrentNbObjects]] = PhysicalIndex; // Update virtual-to-physical remapping table. mInToOut[PhysicalIndex] = mInToOut[mCurrentNbObjects]; // Update physical-to-virtual remapping table. // Keep track of the recyclable virtual index mInToOut[mCurrentNbObjects] = VirtualIndex; // Store the free virtual index/handle at the end of mInToOut mOutToIn[VirtualIndex] = 0xffff; // Invalidate the entry mNbFreeIndices++; // One more free index mStamps[VirtualIndex]++; // Update stamp } } void* HandleManager::GetObject(Handle handle) const { const PxU16 VirtualIndex = PxU16(handle); if(VirtualIndex>=mMaxNbObjects) return NULL; // Invalid handle const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get physical index if(PhysicalIndex==0xffff) return NULL; // Object has been deleted if(PhysicalIndex>=mMaxNbObjects) return NULL; // Index is invalid if(mStamps[VirtualIndex]!=handle>>16) return NULL; // Index has been recycled return mObjects[PhysicalIndex]; // Returns stored pointer } bool HandleManager::UpdateObject(Handle handle, void* object) { const PxU16 VirtualIndex = PxU16(handle); if(VirtualIndex>=mMaxNbObjects) return false; // Invalid handle const PxU16 PhysicalIndex = mOutToIn[VirtualIndex]; // Get physical index if(PhysicalIndex==0xffff) return false; // Object has been deleted if(PhysicalIndex>=mMaxNbObjects) return false; // Index is invalid if(mStamps[VirtualIndex]!=handle>>16) return false; // Index has been recycled mObjects[PhysicalIndex] = object; // Updates stored pointer return true; } // PT: please do not expose these functions outside of this class, // as we want to retain the ability to easily modify the handle format if necessary. #define NEW_ENCODING #ifdef NEW_ENCODING static PX_FORCE_INLINE void* encodeInternalHandle(PxU32 index, PxGeometryType::Enum type) { PX_ASSERT(index<=0xffff); PX_ASSERT(PxU32(type)<=0xffff); // PT: we do type+1 to ban internal handles with a 0 value, since it's reserved for NULL pointers return reinterpret_cast((size_t(index)<<16)|(size_t(type)+1)); } static PX_FORCE_INLINE PxGeometryType::Enum decodeInternalType(void* handle) { return PxGeometryType::Enum((PxU32(reinterpret_cast(handle)) & 0xffff)-1); } static PX_FORCE_INLINE PxU32 decodeInternalIndex(void* handle) { return (PxU32(size_t(handle)))>>16; } #else static PX_FORCE_INLINE ObstacleHandle encodeHandle(PxU32 index, PxGeometryType::Enum type) { PX_ASSERT(index<=0xffff); PX_ASSERT(type<=0xffff); return (PxU16(index)<<16)|PxU32(type); } static PX_FORCE_INLINE PxGeometryType::Enum decodeType(ObstacleHandle handle) { return PxGeometryType::Enum(handle & 0xffff); } static PX_FORCE_INLINE PxU32 decodeIndex(ObstacleHandle handle) { return handle>>16; } #endif ObstacleContext::ObstacleContext(CharacterControllerManager& cctMan) : mCCTManager(cctMan) { } ObstacleContext::~ObstacleContext() { } void ObstacleContext::release() { mCCTManager.releaseObstacleContext(*this); } PxControllerManager& ObstacleContext::getControllerManager() const { return mCCTManager; } ObstacleHandle ObstacleContext::addObstacle(const PxObstacle& obstacle) { const PxGeometryType::Enum type = obstacle.getType(); if(type==PxGeometryType::eBOX) { const PxU32 index = mBoxObstacles.size(); #ifdef NEW_ENCODING const ObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type)); #else const ObstacleHandle handle = encodeHandle(index, type); #endif mBoxObstacles.pushBack(InternalBoxObstacle(handle, static_cast(obstacle))); mCCTManager.onObstacleAdded(handle, this); return handle; } else if(type==PxGeometryType::eCAPSULE) { const PxU32 index = mCapsuleObstacles.size(); #ifdef NEW_ENCODING const ObstacleHandle handle = mHandleManager.Add(encodeInternalHandle(index, type)); #else const ObstacleHandle handle = encodeHandle(index, type); #endif mCapsuleObstacles.pushBack(InternalCapsuleObstacle(handle, static_cast(obstacle))); mCCTManager.onObstacleAdded(handle, this); return handle; } else return INVALID_OBSTACLE_HANDLE; } #ifdef NEW_ENCODING template static PX_FORCE_INLINE void remove(HandleManager& manager, void* object, ObstacleHandle handle, PxU32 index, PxU32 size, const Ps::Array& obstacles) { manager.Remove(handle); if(index!=size-1) { bool status = manager.UpdateObject(obstacles[size-1].mHandle, object); PX_ASSERT(status); PX_UNUSED(status); } } #endif bool ObstacleContext::removeObstacle(ObstacleHandle handle) { #ifdef NEW_ENCODING void* object = mHandleManager.GetObject(handle); if(!object) return false; const PxGeometryType::Enum type = decodeInternalType(object); const PxU32 index = decodeInternalIndex(object); #else const PxGeometryType::Enum type = decodeType(handle); const PxU32 index = decodeIndex(handle); #endif if(type==PxGeometryType::eBOX) { const PxU32 size = mBoxObstacles.size(); PX_ASSERT(index=size) return false; #ifdef NEW_ENCODING remove(mHandleManager, object, handle, index, size, mBoxObstacles); #endif mBoxObstacles.replaceWithLast(index); #ifdef NEW_ENCODING mCCTManager.onObstacleRemoved(handle); #else mCCTManager.onObstacleRemoved(handle, encodeHandle(size-1, type)); #endif return true; } else if(type==PxGeometryType::eCAPSULE) { const PxU32 size = mCapsuleObstacles.size(); PX_ASSERT(index=size) return false; #ifdef NEW_ENCODING remove(mHandleManager, object, handle, index, size, mCapsuleObstacles); #endif mCapsuleObstacles.replaceWithLast(index); #ifdef NEW_ENCODING mCCTManager.onObstacleRemoved(handle); #else mCCTManager.onObstacleRemoved(handle, encodeHandle(size-1, type)); #endif return true; } else return false; } bool ObstacleContext::updateObstacle(ObstacleHandle handle, const PxObstacle& obstacle) { #ifdef NEW_ENCODING void* object = mHandleManager.GetObject(handle); if(!object) return false; const PxGeometryType::Enum type = decodeInternalType(object); PX_ASSERT(type==obstacle.getType()); if(type!=obstacle.getType()) return false; const PxU32 index = decodeInternalIndex(object); #else const PxGeometryType::Enum type = decodeType(handle); PX_ASSERT(type==obstacle.getType()); if(type!=obstacle.getType()) return false; const PxU32 index = decodeIndex(handle); #endif if(type==PxGeometryType::eBOX) { const PxU32 size = mBoxObstacles.size(); PX_ASSERT(index=size) return false; mBoxObstacles[index].mData = static_cast(obstacle); mCCTManager.onObstacleUpdated(handle,this); return true; } else if(type==PxGeometryType::eCAPSULE) { const PxU32 size = mCapsuleObstacles.size(); PX_ASSERT(index=size) return false; mCapsuleObstacles[index].mData = static_cast(obstacle); mCCTManager.onObstacleUpdated(handle,this); return true; } else return false; } PxU32 ObstacleContext::getNbObstacles() const { return mBoxObstacles.size() + mCapsuleObstacles.size(); } const PxObstacle* ObstacleContext::getObstacle(PxU32 i) const { const PxU32 nbBoxes = mBoxObstacles.size(); if(i=size) return NULL; PX_ASSERT(mBoxObstacles[index].mHandle==handle); return &mBoxObstacles[index].mData; } else if(type==PxGeometryType::eCAPSULE) { const PxU32 size = mCapsuleObstacles.size(); if(index>=size) return NULL; PX_ASSERT(mCapsuleObstacles[index].mHandle==handle); return &mCapsuleObstacles[index].mData; } else return NULL; } #include "GuRaycastTests.h" #include "geometry/PxBoxGeometry.h" #include "geometry/PxCapsuleGeometry.h" #include "PsMathUtils.h" using namespace Gu; const PxObstacle* ObstacleContext::raycastSingle(PxRaycastHit& hit, const PxVec3& origin, const PxVec3& unitDir, const PxReal distance, ObstacleHandle& obstacleHandle) const { PxRaycastHit localHit; PxF32 t = FLT_MAX; const PxObstacle* touchedObstacle = NULL; const PxHitFlags hitFlags = PxHitFlags(0); { const RaycastFunc raycastFunc = Gu::getRaycastFuncTable()[PxGeometryType::eBOX]; PX_ASSERT(raycastFunc); const PxU32 nbExtraBoxes = mBoxObstacles.size(); for(PxU32 i=0;i