// // 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-2018 NVIDIA Corporation. All rights reserved. // Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved. // Copyright (c) 2001-2004 NovodeX AG. All rights reserved. #ifndef PX_PHYSICS_SCB_RIGID_OBJECT #define PX_PHYSICS_SCB_RIGID_OBJECT #include "../../SimulationController/include/ScRigidCore.h" #include "ScbScene.h" #include "ScbActor.h" #include "ScbShape.h" #include "PsInlineArray.h" namespace physx { // base class for dynamic and static rigid objects, so that shapes can have something to refer to namespace Scb { struct RemovedShape { RemovedShape() : mShape(NULL), mWakeTouching(0) {} RemovedShape(Scb::Shape* s, PxU8 wakeTouching) : mShape(s), mWakeTouching(wakeTouching) {} PX_FORCE_INLINE bool operator == (const RemovedShape& other) const { return (mShape == other.mShape); } PX_FORCE_INLINE bool operator != (const RemovedShape& other) const { return (mShape != other.mShape); } Scb::Shape* mShape; PxU8 mWakeTouching; }; struct RigidObjectBuffer : public ActorBuffer //once RigidObject has its own buffered elements, derive from that instead { RigidObjectBuffer(): mResetFilterShape(0), mResetFilterShapeCount(0) {} // TODO(dsequeira): ideally we would use an allocator that allocates from the buffered memory stream Ps::InlineArray mAddedShapes; Ps::InlineArray mRemovedShapes; union { PxU32 mResetFilterShapesIdx; Scb::Shape* mResetFilterShape; }; PxU32 mResetFilterShapeCount; enum { BF_Base = ActorBuffer::AttrCount }; enum { BF_Shapes = 1<mRemovedShapes.size();i++) { RemovedShape& rs = b->mRemovedShapes[i]; Shape& shape = *rs.mShape; shape.setControlStateIfExclusive(NULL, Scb::ControlState::eNOT_IN_SCENE); Sc::RigidCore& rc = getScRigidCore(); Scb::Scene* scene = getScbScene(); #if PX_SUPPORT_PVD scene->getScenePvdClient().releasePvdInstance(&shape, pxActor); #endif if(!isSimDisabledInternally()) { rc.removeShapeFromScene(shape.getScShape(), (rs.mWakeTouching != 0)); shape.checkUpdateOnRemove(scene); NpShapeDecRefCount(shape); } } } // The array of removed shapes must be reset to avoid memory leaks. b->mRemovedShapes.reset(); } } PX_INLINE void syncState() { const PxU32 bufferFlags = getBufferFlags(); if(bufferFlags & Buf::BF_ResetFiltering) { PX_ASSERT(getControlState() != ControlState::eREMOVE_PENDING); // removing the actor should have cleared BF_ResetFiltering Scb::Scene* scene = getScbScene(); Sc::RigidCore& scCore = getScRigidCore(); RigidObjectBuffer* b = getBuffer(); Scb::Shape* const* shapes = (b->mResetFilterShapeCount == 1) ? &b->mResetFilterShape : scene->getShapeBuffer(b->mResetFilterShapesIdx); for(PxU32 i=0; imResetFilterShapeCount; i++) { Sc::ShapeCore& scShape = shapes[i]->getScShape(); // do not process the call if the shape will not be a broadphase shape any longer if(shapes[i]->getFlags() & (PxShapeFlag::eSIMULATION_SHAPE | PxShapeFlag::eTRIGGER_SHAPE)) scCore.onShapeChange(scShape, Sc::ShapeChangeNotifyFlag::eRESET_FILTERING, PxShapeFlags()); } } if(bufferFlags & Buf::BF_Shapes) { RigidObjectBuffer* b = getBuffer(); ControlState::Enum cs = getControlState(); #if PX_SUPPORT_PVD PxActor& pxActor = *getScRigidCore().getPxActor(); #endif for(PxU32 i=0;imAddedShapes.size();i++) { Shape& shape = *b->mAddedShapes[i]; // it can happen that a shape gets attached while the sim is running but then the actor is removed from the scene, // so we need to distinguish those two cases if(cs != ControlState::eREMOVE_PENDING) { shape.setControlStateIfExclusive(getScbScene(), Scb::ControlState::eIN_SCENE); if(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) // important to use the buffered flags since we want the new state. { getScRigidCore().addShapeToScene(shape.getScShape()); NpShapeIncRefCount(shape); } #if PX_SUPPORT_PVD getScbScene()->getScenePvdClient().createPvdInstance(&shape, pxActor); #endif } else shape.setControlStateIfExclusive(getScbScene(), Scb::ControlState::eNOT_IN_SCENE); } // reset the arrays, because destructors don't run on buffers b->mAddedShapes.reset(); } Actor::syncState(); } PX_FORCE_INLINE void scheduleForWakeTouching() { PX_ASSERT(getScbScene() && getScbScene()->isPhysicsBuffering()); setBufferFlag(RigidObjectBuffer::BF_WakeTouching); } //--------------------------------------------------------------------------------- // Miscellaneous //--------------------------------------------------------------------------------- public: PX_INLINE const Sc::RigidCore& getScRigidCore() const { return static_cast(getActorCore()); } // Only use if you know what you're doing! PX_INLINE Sc::RigidCore& getScRigidCore() { return static_cast(getActorCore()); } // Only use if you know what you're doing! PX_INLINE void onShapeAttach(Scb::Shape& shape) { // there are two things to do here: add the shape to the sim (if unbuffered) or set it up for // * if unbuffered, add the shape to the sim and PVD and increment its refcount, else set it up for buffered insertion, // * if the shape is exclusive, set its Scb control state appropriately. ControlState::Enum cs = getControlState(); if(cs==ControlState::eNOT_IN_SCENE) return; Scene* scbScene = getScbScene(); if(!scbScene->isPhysicsBuffering()) { if(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) { NpShapeIncRefCount(shape); getScRigidCore().addShapeToScene(shape.getScShape()); } #if PX_SUPPORT_PVD scbScene->getScenePvdClient().createPvdInstance(&shape, *getScRigidCore().getPxActor()); #endif shape.setControlStateIfExclusive(scbScene, ControlState::eIN_SCENE); return; } else if(cs == ControlState::eINSERT_PENDING) { shape.setControlStateIfExclusive(scbScene, ControlState::eINSERT_PENDING); return; } RigidObjectBuffer* b = getBuffer(); if(!b->mRemovedShapes.findAndReplaceWithLast(RemovedShape(&shape, 0))) b->mAddedShapes.pushBack(&shape); markUpdated(Buf::BF_Shapes); shape.setControlStateIfExclusive(scbScene, ControlState::eINSERT_PENDING); } PX_INLINE void onShapeDetach(Scb::Shape& shape, bool wakeOnLostTouch, bool toBeReleased) { // see comments in onShapeAttach ControlState::Enum cs = getControlState(); if(cs==ControlState::eNOT_IN_SCENE) return; Scene* scbScene = getScbScene(); if(!scbScene->isPhysicsBuffering()) { #if PX_SUPPORT_PVD scbScene->getScenePvdClient().releasePvdInstance(&shape, *getScRigidCore().getPxActor()); #endif if(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)) { getScRigidCore().removeShapeFromScene(shape.getScShape(), wakeOnLostTouch); NpShapeDecRefCount(shape); } shape.setControlStateIfExclusive(NULL, ControlState::eNOT_IN_SCENE); return; } else if(cs == ControlState::eINSERT_PENDING) { shape.setControlStateIfExclusive(NULL, ControlState::eNOT_IN_SCENE); return; } RigidObjectBuffer* b = getBuffer(); // remove from the resetFiltering list const PxU32 bufferFlags = getBufferFlags(); if(bufferFlags & Buf::BF_ResetFiltering) { if(b->mResetFilterShapeCount == 1) { if(b->mResetFilterShape == &shape) { b->mResetFilterShapeCount = 0; b->mResetFilterShape = 0; resetBufferFlag(Buf::BF_ResetFiltering); } } else { Scb::Shape** shapes = scbScene->getShapeBuffer(b->mResetFilterShapesIdx); PxU32 idx = 0; PxU32 lastIdx = b->mResetFilterShapeCount; for(PxU32 k=0; k < b->mResetFilterShapeCount; k++) // need to iterate over whole list, same shape can be in there multiple times { if(shapes[idx] != &shape) idx++; else { lastIdx--; shapes[idx] = shapes[lastIdx]; } } b->mResetFilterShapeCount = idx; if(idx == 0) { b->mResetFilterShape = 0; resetBufferFlag(Buf::BF_ResetFiltering); } else if(idx == 1) b->mResetFilterShape = shapes[0]; } } if(b->mAddedShapes.findAndReplaceWithLast(&shape)) shape.setControlStateIfExclusive(scbScene, ControlState::eIN_SCENE); else { if(!isSimDisabledInternally()) { b->mRemovedShapes.pushBack(RemovedShape(&shape, PxU8(wakeOnLostTouch ? 1 : 0))); } else { PX_ASSERT(scbScene); PX_ASSERT(scbScene->isPhysicsBuffering()); if(toBeReleased) { shape.checkUpdateOnRemove(scbScene); #if PX_SUPPORT_PVD scbScene->getScenePvdClient().releasePvdInstance(&shape, *getScRigidCore().getPxActor()); #endif } else b->mRemovedShapes.pushBack(RemovedShape(&shape, 0)); } shape.setControlStateIfExclusive(scbScene, ControlState::eREMOVE_PENDING); } markUpdated(Buf::BF_Shapes); } PX_INLINE bool isAddedShape(Scb::Shape&); // check whether the specified shape is pending for insertion. Only call this method if you know that there are pending shape adds/removes. PX_FORCE_INLINE void switchToNoSim(bool isDynamic); PX_FORCE_INLINE void switchFromNoSim(bool isDynamic); PX_FORCE_INLINE void syncNoSimSwitch(const Buf& buf, Sc::RigidCore& rc, bool isDynamic); // IMPORTANT: This is the non-buffered state, for the case where it is important to know what the current internal state is. // Reading is fine even if the sim is running because actor flags are read-only internally. PX_FORCE_INLINE bool isSimDisabledInternally() const { return getScRigidCore().getActorFlags().isSet(PxActorFlag::eDISABLE_SIMULATION); } PX_FORCE_INLINE void clearBufferedState() { resetBufferFlag(Buf::BF_ResetFiltering); } PX_FORCE_INLINE static const RigidObject& fromSc(const Sc::RigidCore& a) { return static_cast(Actor::fromSc(a)); } PX_FORCE_INLINE static RigidObject& fromSc(Sc::RigidCore &a) { return static_cast(Actor::fromSc(a)); } protected: ~RigidObject() {} private: Buf* getBuffer() { return reinterpret_cast(getStream()); } PX_FORCE_INLINE void copyResetFilterShapes(Scb::Shape** shapePtrs, Scb::Shape*const* oldShapes, PxU32 oldShapeCount, Scb::Shape*const* newShapes, PxU32 newShapeCount); }; PX_INLINE void RigidObject::resetFiltering(Scb::Shape*const* shapes, PxU32 shapeCount) { PX_ASSERT(!(getActorFlags() & PxActorFlag::eDISABLE_SIMULATION)); if(!isBuffering()) { for(PxU32 i=0; i < shapeCount; i++) getScRigidCore().onShapeChange(shapes[i]->getScShape(), Sc::ShapeChangeNotifyFlag::eRESET_FILTERING, PxShapeFlags()); } else { RigidObjectBuffer* b = getBuffer(); if(b->mResetFilterShapeCount == 0) { if(shapeCount == 1) { b->mResetFilterShape = shapes[0]; b->mResetFilterShapeCount = 1; markUpdated(Buf::BF_ResetFiltering); } else { PxU32 bufferIdx; Scb::Shape** shapePtrs = getScbScene()->allocShapeBuffer(shapeCount, bufferIdx); if(shapePtrs) { for(PxU32 i=0; i < shapeCount; i++) shapePtrs[i] = shapes[i]; b->mResetFilterShapesIdx = bufferIdx; b->mResetFilterShapeCount = shapeCount; markUpdated(Buf::BF_ResetFiltering); } } } else { PxU32 newCount = b->mResetFilterShapeCount + shapeCount; PxU32 bufferIdx; Scb::Shape** shapePtrs = getScbScene()->allocShapeBuffer(newCount, bufferIdx); if(shapePtrs) { if(b->mResetFilterShapeCount == 1) copyResetFilterShapes(shapePtrs, &b->mResetFilterShape, 1, shapes, shapeCount); else copyResetFilterShapes(shapePtrs, getScbScene()->getShapeBuffer(b->mResetFilterShapesIdx), b->mResetFilterShapeCount, shapes, shapeCount); b->mResetFilterShapesIdx = bufferIdx; b->mResetFilterShapeCount = newCount; markUpdated(Buf::BF_ResetFiltering); } } } } PX_INLINE bool RigidObject::isAddedShape(Scb::Shape& shape) { PX_ASSERT(isBuffered(Buf::BF_Shapes)); if(shape.isExclusive()) { return (shape.getControlState() == Scb::ControlState::eINSERT_PENDING); } else { // For shared shapes it is not clear from the shape alone whether it has been added while the simulation was running. RigidObjectBuffer* buf = getBuffer(); PX_ASSERT(buf); const PxU32 addedShapeCount = buf->mAddedShapes.size(); for(PxU32 k=0; k < addedShapeCount; k++) { if(&shape == buf->mAddedShapes[k]) return true; } return false; } } PX_FORCE_INLINE void RigidObject::switchToNoSim(bool isDynamic) { Scb::Scene* scene = getScbScene(); if(scene && (!scene->isPhysicsBuffering())) scene->switchRigidToNoSim(*this, isDynamic); } PX_FORCE_INLINE void RigidObject::switchFromNoSim(bool isDynamic) { Scb::Scene* scene = getScbScene(); if(scene && (!scene->isPhysicsBuffering())) scene->switchRigidFromNoSim(*this, isDynamic); } PX_FORCE_INLINE void RigidObject::syncNoSimSwitch(const Buf& buf, Sc::RigidCore& rc, bool isDynamic) { const PxActorFlags oldFlags = rc.getActorFlags(); const bool oldNoSim = oldFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); const bool newNoSim = buf.mActorFlags.isSet(PxActorFlag::eDISABLE_SIMULATION); if(oldNoSim && (!newNoSim)) getScbScene()->switchRigidFromNoSim(*this, isDynamic); else if((!oldNoSim) && newNoSim) getScbScene()->switchRigidToNoSim(*this, isDynamic); } PX_FORCE_INLINE void RigidObject::copyResetFilterShapes(Scb::Shape** shapePtrs, Scb::Shape*const* oldShapes, PxU32 oldShapeCount, Scb::Shape*const* newShapes, PxU32 newShapeCount) { for(PxU32 i=0; i < oldShapeCount; i++) shapePtrs[i] = oldShapes[i]; for(PxU32 i=0; i < newShapeCount; i++) shapePtrs[i+oldShapeCount] = newShapes[i]; } } // namespace Scb } #endif