text: //
  // 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 "NpCast.h"
  #include "GuConvexMesh.h"
  #include "GuTriangleMesh.h"
  #include "ScbNpDeps.h"
  #include "GuBounds.h"
  
  using namespace physx;
  using namespace Sq;
  
  namespace physx
  {
  	extern bool gUnifiedHeightfieldCollision;
  }
  
  static PX_FORCE_INLINE void updatePvdProperties(const Scb::Shape& shape)
  {
  #if PX_SUPPORT_PVD
  	Scb::Scene* scbScene = shape.getScbSceneForAPI();
  	if(scbScene)
  		scbScene->getScenePvdClient().updatePvdProperties(&shape);
  #else
  	PX_UNUSED(shape);
  #endif
  }
  
  NpShape::NpShape(const PxGeometry& geometry, PxShapeFlags shapeFlags, const PxU16* materialIndices, PxU16 materialCount, bool isExclusive)
  :	PxShape				(PxConcreteType::eSHAPE, PxBaseFlag::eOWNS_MEMORY | PxBaseFlag::eIS_RELEASABLE)
  ,	mActor				(NULL)
  ,	mShape				(geometry, shapeFlags, materialIndices, materialCount, isExclusive)
  ,	mName				(NULL)
  ,	mExclusiveAndActorCount (isExclusive ? EXCLUSIVE_MASK : 0)
  {
  	PX_ASSERT(mShape.getScShape().getPxShape() == static_cast<PxShape*>(this));
  
  	PxShape::userData = NULL;
  
  	incMeshRefCount();
  }
  
  NpShape::~NpShape()
  {
  	decMeshRefCount();
  
  	PxU32 nbMaterials = mShape.getNbMaterials();
  	for (PxU32 i=0; i < nbMaterials; i++)
  	{
  		NpMaterial* mat = static_cast<NpMaterial*>(mShape.getMaterial(i));
  		mat->decRefCount();
  	}
  }
  
  void NpShape::onRefCountZero()
  {
  	NpFactory::getInstance().onShapeRelease(this);
  	// see NpShape.h for ref counting semantics for shapes
  	NpDestroy(getScbShape());
  }
  
  // PX_SERIALIZATION
  
  NpShape::NpShape(PxBaseFlags baseFlags) : PxShape(baseFlags), mShape(PxEmpty) {}
  
  void NpShape::preExportDataReset()
  {
  	Cm::RefCountable::preExportDataReset();
  	mExclusiveAndActorCount &= EXCLUSIVE_MASK;
  }
  
  void NpShape::exportExtraData(PxSerializationContext& context)
  {	
  	getScbShape().getScShape().exportExtraData(context);
  	context.writeName(mName);
  }
  
  void NpShape::importExtraData(PxDeserializationContext& context)
  {
  	getScbShape().getScShape().importExtraData(context);
  	context.readName(mName);
  }
  
  void NpShape::requiresObjects(PxProcessPxBaseCallback& c)
  {
  	//meshes
  	PxBase* mesh = NULL;
  	switch(mShape.getGeometryType())
  	{
  	case PxGeometryType::eCONVEXMESH:
  		mesh = static_cast<const PxConvexMeshGeometry&>(mShape.getGeometry()).convexMesh;
  		break;
  	case PxGeometryType::eHEIGHTFIELD:
  		mesh = static_cast<const PxHeightFieldGeometry&>(mShape.getGeometry()).heightField;
  		break;
  	case PxGeometryType::eTRIANGLEMESH:
  		mesh = static_cast<const PxTriangleMeshGeometry&>(mShape.getGeometry()).triangleMesh;
  		break;
  	case PxGeometryType::eSPHERE:
  	case PxGeometryType::ePLANE:
  	case PxGeometryType::eCAPSULE:
  	case PxGeometryType::eBOX:
  	case PxGeometryType::eGEOMETRY_COUNT:
  	case PxGeometryType::eINVALID:
  		break;
  	}
  	
  	if(mesh)
  		c.process(*mesh);
  
  	//material
  	PxU32 nbMaterials = mShape.getNbMaterials();
  	for (PxU32 i=0; i < nbMaterials; i++)
  	{
  		NpMaterial* mat = static_cast<NpMaterial*>(mShape.getMaterial(i));
  		c.process(*mat);
  	}
  }
  
  void NpShape::resolveReferences(PxDeserializationContext& context)
  {	
  	// getMaterials() only works after material indices have been patched. 
  	// in order to get to the new material indices, we need access to the new materials.
  	// this only leaves us with the option of acquiring the material through the context given an old material index (we do have the mapping)
  	{
  		PxU32 nbIndices = mShape.getScShape().getNbMaterialIndices();
  		const PxU16* indices = mShape.getScShape().getMaterialIndices();
  
  		for (PxU32 i=0; i < nbIndices; i++)
  		{
  			PxBase* base = context.resolveReference(PX_SERIAL_REF_KIND_MATERIAL_IDX, size_t(indices[i]));
  			PX_ASSERT(base && base->is<PxMaterial>());
  
  			NpMaterial& material = *static_cast<NpMaterial*>(base);
  			getScbShape().getScShape().resolveMaterialReference(i, material.getHandle());
  		}
  	}
  
  	context.translatePxBase(mActor);
  
  	getScbShape().getScShape().resolveReferences(context);	
  
  
  	incMeshRefCount();
  
  	// Increment materials' refcounts in a second pass. Works better in case of failure above.
  	PxU32 nbMaterials = mShape.getNbMaterials();
  	for (PxU32 i=0; i < nbMaterials; i++)
  	{
  		NpMaterial* mat = static_cast<NpMaterial*>(mShape.getMaterial(i));
  		mat->incRefCount();
  	}	
  }
  
  NpShape* NpShape::createObject(PxU8*& address, PxDeserializationContext& context)
  {
  	NpShape* obj = new (address) NpShape(PxBaseFlag::eIS_RELEASABLE);
  	address += sizeof(NpShape);	
  	obj->importExtraData(context);
  	obj->resolveReferences(context);
  	return obj;
  }
  //~PX_SERIALIZATION
  
  void NpShape::acquireReference()
  {
  	incRefCount();
  }
  
  void NpShape::release()
  {
  	PX_CHECK_AND_RETURN(getRefCount() > 1 || getActorCount() == 0, "PxShape::release: last reference to a shape released while still attached to an actor!");
  	NP_WRITE_CHECK(getOwnerScene());
  	releaseInternal();
  }
  
  void NpShape::releaseInternal()
  {
  	decRefCount();
  }
  
  Sc::RigidCore& NpShape::getScRigidObjectExclusive() const
  {
  	const PxType actorType = mActor->getConcreteType();
  
  	if (actorType == PxConcreteType::eRIGID_DYNAMIC)
  		return static_cast<NpRigidDynamic&>(*mActor).getScbBodyFast().getScBody();
  	else if (actorType == PxConcreteType::eARTICULATION_LINK)
  		return static_cast<NpArticulationLink&>(*mActor).getScbBodyFast().getScBody();
  	else
  		return static_cast<NpRigidStatic&>(*mActor).getScbRigidStaticFast().getScStatic();
  }
  
  void NpShape::updateSQ(const char* errorMessage)
  {
  	if(mActor && (mShape.getFlags() & PxShapeFlag::eSCENE_QUERY_SHAPE))
  	{
  		NpScene* scene = NpActor::getAPIScene(*mActor);
  		NpShapeManager* shapeManager = NpActor::getShapeManager(*mActor);
  		if(scene)
  		{
  			const PrunerData sqData = shapeManager->findSceneQueryData(*this);
  			scene->getSceneQueryManagerFast().markForUpdate(sqData);
  		}
  
  		// invalidate the pruning structure if the actor bounds changed
  		if(shapeManager->getPruningStructure())
  		{
  			Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, errorMessage);
  			shapeManager->getPruningStructure()->invalidate(mActor);
  		}
  	}
  }
  
  PxGeometryType::Enum NpShape::getGeometryType() const
  {
  	NP_READ_CHECK(getOwnerScene());
  
  	return mShape.getGeometryType();
  }
  
  void NpShape::setGeometry(const PxGeometry& g)
  {
  	NP_WRITE_CHECK(getOwnerScene());
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setGeometry: shared shapes attached to actors are not writable.");
  	PX_SIMD_GUARD;
  
  	// PT: fixes US2117
  	if(g.getType() != getGeometryTypeFast())
  	{
  		Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxShape::setGeometry(): Invalid geometry type. Changing the type of the shape is not supported.");
  		return;
  	}
  
  #if PX_CHECKED
  	bool isValid = false;
  	switch(g.getType())
  	{
  		case PxGeometryType::eSPHERE:
  			isValid = static_cast<const PxSphereGeometry&>(g).isValid();
  		break;
  
  		case PxGeometryType::ePLANE:
  			isValid = static_cast<const PxPlaneGeometry&>(g).isValid();
  		break;
  
  		case PxGeometryType::eCAPSULE:
  			isValid = static_cast<const PxCapsuleGeometry&>(g).isValid();
  		break;
  
  		case PxGeometryType::eBOX:
  			isValid = static_cast<const PxBoxGeometry&>(g).isValid();
  		break;
  
  		case PxGeometryType::eCONVEXMESH:
  			isValid = static_cast<const PxConvexMeshGeometry&>(g).isValid();
  		break;
  
  		case PxGeometryType::eTRIANGLEMESH:
  			isValid = static_cast<const PxTriangleMeshGeometry&>(g).isValid();
  		break;
  
  		case PxGeometryType::eHEIGHTFIELD:
  			isValid = static_cast<const PxHeightFieldGeometry&>(g).isValid();
  		break;
  		
  		case PxGeometryType::eGEOMETRY_COUNT:
  		case PxGeometryType::eINVALID:
  			break;
  	}
  
  	if(!isValid)
  	{
  		Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, "PxShape::setGeometry(): Invalid geometry!");
  		return;
  	}
  #endif
  
  	decMeshRefCount();
  
  	mShape.setGeometry(g);
  
  	incMeshRefCount();
  
  	updateSQ("PxShape::setGeometry: Shape is a part of pruning structure, pruning structure is now invalid!");
  }
  
  PxGeometryHolder NpShape::getGeometry() const
  {
  	PX_COMPILE_TIME_ASSERT(sizeof(Gu::GeometryUnion)>=sizeof(PxGeometryHolder));
  	return reinterpret_cast<const PxGeometryHolder&>(mShape.getGeometry());
  }
  
  template<class T>
  static PX_FORCE_INLINE bool getGeometryT(const NpShape* npShape, PxGeometryType::Enum type, T& geom)
  {
  	NP_READ_CHECK(npShape->getOwnerScene());
  
  	if(npShape->getGeometryTypeFast() != type)
  		return false;
  
  	geom = static_cast<const T&>(npShape->getScbShape().getGeometry());
  	return true;
  }
  
  bool NpShape::getBoxGeometry(PxBoxGeometry& g)						const	{ return getGeometryT(this, PxGeometryType::eBOX, g);			}
  bool NpShape::getSphereGeometry(PxSphereGeometry& g)				const	{ return getGeometryT(this, PxGeometryType::eSPHERE, g);		}
  bool NpShape::getCapsuleGeometry(PxCapsuleGeometry& g)				const	{ return getGeometryT(this, PxGeometryType::eCAPSULE, g);		}
  bool NpShape::getPlaneGeometry(PxPlaneGeometry& g)					const	{ return getGeometryT(this, PxGeometryType::ePLANE, g);		}
  bool NpShape::getConvexMeshGeometry(PxConvexMeshGeometry& g)		const	{ return getGeometryT(this, PxGeometryType::eCONVEXMESH, g);	}
  bool NpShape::getTriangleMeshGeometry(PxTriangleMeshGeometry& g)	const	{ return getGeometryT(this, PxGeometryType::eTRIANGLEMESH, g);	}
  bool NpShape::getHeightFieldGeometry(PxHeightFieldGeometry& g)		const	{ return getGeometryT(this, PxGeometryType::eHEIGHTFIELD, g);	}
  
  PxRigidActor* NpShape::getActor() const
  {
  	NP_READ_CHECK(getOwnerScene());
  	return mActor;
  }
  
  void NpShape::setLocalPose(const PxTransform& newShape2Actor)
  {
  	PX_CHECK_AND_RETURN(newShape2Actor.isSane(), "PxShape::setLocalPose: pose is not valid.");
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setLocalPose: shared shapes attached to actors are not writable.");
  	NP_WRITE_CHECK(getOwnerScene());
  
  	mShape.setShape2Actor(newShape2Actor.getNormalized());
  
  	updateSQ("PxShape::setLocalPose: Shape is a part of pruning structure, pruning structure is now invalid!");
  }
  
  PxTransform NpShape::getLocalPose() const
  {
  	NP_READ_CHECK(getOwnerScene());
  
  	return mShape.getShape2Actor();
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  
  void NpShape::setSimulationFilterData(const PxFilterData& data)
  {
  	NP_WRITE_CHECK(getOwnerScene());
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setSimulationFilterData: shared shapes attached to actors are not writable.");
  	mShape.setSimulationFilterData(data);
  }
  
  PxFilterData NpShape::getSimulationFilterData() const
  {
  	NP_READ_CHECK(getOwnerScene());
  	return mShape.getSimulationFilterData();
  }
  
  void NpShape::setQueryFilterData(const PxFilterData& data)
  {
  	NP_WRITE_CHECK(getOwnerScene());
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setQueryFilterData: shared shapes attached to actors are not writable.");
  
  	mShape.getScShape().setQueryFilterData(data);	// PT: this one doesn't need double-buffering
  
  	updatePvdProperties(mShape);
  }
  
  PxFilterData NpShape::getQueryFilterData() const
  {
  	NP_READ_CHECK(getOwnerScene());
  
  	return getQueryFilterDataFast();
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  
  void NpShape::setMaterials(PxMaterial*const* materials, PxU16 materialCount)
  {
  	NP_WRITE_CHECK(getOwnerScene());
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setMaterials: shared shapes attached to actors are not writable.");
  
  #if PX_CHECKED
  	if (!NpShape::checkMaterialSetup(mShape.getGeometry(), "PxShape::setMaterials()", materials,  materialCount))
  		return;
  #endif
  
  	PxU32 oldMaterialCount = mShape.getNbMaterials();
  	PX_ALLOCA(oldMaterials, PxMaterial*, oldMaterialCount);
  	PxU32 tmp = mShape.getMaterials(oldMaterials, oldMaterialCount);
  	PX_ASSERT(tmp == oldMaterialCount);
  	PX_UNUSED(tmp);
  
  	if (mShape.setMaterials(materials, materialCount))
  	{
  		for(PxU32 i=0; i < materialCount; i++)
  			static_cast<NpMaterial*>(materials[i])->incRefCount();
  
  		for(PxU32 i=0; i < oldMaterialCount; i++)
  			static_cast<NpMaterial*>(oldMaterials[i])->decRefCount();
  	}
  }
  
  PxU16 NpShape::getNbMaterials() const
  {
  	NP_READ_CHECK(getOwnerScene());
  
  	return mShape.getNbMaterials();
  }
  
  PxU32 NpShape::getMaterials(PxMaterial** userBuffer, PxU32 bufferSize, PxU32 startIndex) const
  {
  	NP_READ_CHECK(getOwnerScene());
  
  	return mShape.getMaterials(userBuffer, bufferSize, startIndex);
  }
  
  PxMaterial* NpShape::getMaterialFromInternalFaceIndex(PxU32 faceIndex) const
  {
  	NP_READ_CHECK(getOwnerScene());
  
  	bool isHf = (getGeometryType() == PxGeometryType::eHEIGHTFIELD);
  	bool isMesh = (getGeometryType() == PxGeometryType::eTRIANGLEMESH);
  	if( faceIndex == 0xFFFFffff && (isHf || isMesh) )
  	{
  		Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__,
  			"PxShape::getMaterialFromInternalFaceIndex received 0xFFFFffff as input - returning NULL.");
  		return NULL;
  	}
  
  	PxMaterialTableIndex hitMatTableId = 0;
  
  	if(isHf)
  	{
  		PxHeightFieldGeometry hfGeom;
  		getHeightFieldGeometry(hfGeom);
  
  		hitMatTableId = hfGeom.heightField->getTriangleMaterialIndex(faceIndex);
  	}
  	else if(isMesh)
  	{
  		PxTriangleMeshGeometry triGeo;
  		getTriangleMeshGeometry(triGeo);
  
  		Gu::TriangleMesh* tm = static_cast<Gu::TriangleMesh*>(triGeo.triangleMesh);
  		if(tm->hasPerTriangleMaterials())
  			hitMatTableId = triGeo.triangleMesh->getTriangleMaterialIndex(faceIndex);
  	}
  
  	return getMaterial(hitMatTableId);
  }
  
  void NpShape::setContactOffset(PxReal contactOffset)
  {
  	NP_WRITE_CHECK(getOwnerScene());
  
  	PX_CHECK_AND_RETURN(PxIsFinite(contactOffset), "PxShape::setContactOffset: invalid float");
  	PX_CHECK_AND_RETURN((contactOffset >= 0.0f && contactOffset > mShape.getRestOffset()), "PxShape::setContactOffset: contactOffset should be positive, and greater than restOffset!");
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setContactOffset: shared shapes attached to actors are not writable.");
  
  	mShape.setContactOffset(contactOffset);
  }
  
  PxReal NpShape::getContactOffset() const
  {
  	NP_READ_CHECK(getOwnerScene());
  
  	return mShape.getContactOffset();
  }
  
  void NpShape::setRestOffset(PxReal restOffset)
  {
  	NP_WRITE_CHECK(getOwnerScene());
  	PX_CHECK_AND_RETURN(PxIsFinite(restOffset), "PxShape::setRestOffset: invalid float");
  	PX_CHECK_AND_RETURN((restOffset < mShape.getContactOffset()), "PxShape::setRestOffset: restOffset should be less than contactOffset!");
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setRestOffset: shared shapes attached to actors are not writable.");
  
  	mShape.setRestOffset(restOffset);
  }
  
  PxReal NpShape::getRestOffset() const
  {
  	NP_READ_CHECK(getOwnerScene());
  
  	return mShape.getRestOffset();
  }
  
  void NpShape::setFlagsInternal(PxShapeFlags inFlags)
  {
  	const bool hasMeshTypeGeom = mShape.getGeometryType() == PxGeometryType::eTRIANGLEMESH || mShape.getGeometryType() == PxGeometryType::eHEIGHTFIELD;
  
  	if(hasMeshTypeGeom && (inFlags & PxShapeFlag::eTRIGGER_SHAPE))
  	{
  		Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, 
  			"PxShape::setFlag(s): triangle mesh and heightfield triggers are not supported!");
  		return;
  	}
  
  	if((inFlags & PxShapeFlag::eSIMULATION_SHAPE) && (inFlags & PxShapeFlag::eTRIGGER_SHAPE))
  	{
  		Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, 
  			"PxShape::setFlag(s): shapes cannot simultaneously be trigger shapes and simulation shapes.");
  		return;
  	}
  
  	const PxShapeFlags oldFlags = mShape.getFlags();
  
  	const bool oldIsSimShape = oldFlags & PxShapeFlag::eSIMULATION_SHAPE;
  	const bool isSimShape = inFlags & PxShapeFlag::eSIMULATION_SHAPE;
  
  	if(mActor)
  	{
  		const PxType type = mActor->getConcreteType();
  
  		// PT: US5732 - support kinematic meshes
  		bool isKinematic = false;
  		if(type==PxConcreteType::eRIGID_DYNAMIC)
  		{
  			PxRigidDynamic* rigidDynamic = static_cast<PxRigidDynamic*>(mActor);
  			isKinematic = rigidDynamic->getRigidBodyFlags() & PxRigidBodyFlag::eKINEMATIC;
  		}
  
  		if((type != PxConcreteType::eRIGID_STATIC) && !isKinematic && isSimShape && !oldIsSimShape && (hasMeshTypeGeom || mShape.getGeometryType() == PxGeometryType::ePLANE))
  		{
  			Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, 
  				"PxShape::setFlag(s): triangle mesh, heightfield and plane shapes can only be simulation shapes if part of a PxRigidStatic!");
  			return;
  		}
  	}
  
  	const bool oldHasSceneQuery = oldFlags & PxShapeFlag::eSCENE_QUERY_SHAPE;
  	const bool hasSceneQuery = inFlags & PxShapeFlag::eSCENE_QUERY_SHAPE;
  
  	mShape.setFlags(inFlags);
  
  	if(oldHasSceneQuery != hasSceneQuery && mActor)
  	{
  		NpScene* npScene = getAPIScene();
  		NpShapeManager* shapeManager = NpActor::getShapeManager(*mActor);
  		if(npScene)
  		{
  			if(hasSceneQuery)
  				shapeManager->setupSceneQuery(npScene->getSceneQueryManagerFast(), *mActor, *this);
  			else
  				shapeManager->teardownSceneQuery(npScene->getSceneQueryManagerFast(), *this);
  		}
  
  		// invalidate the pruning structure if the actor bounds changed
  		if(shapeManager->getPruningStructure())
  		{
  			Ps::getFoundation().error(PxErrorCode::eINVALID_OPERATION, __FILE__, __LINE__, "PxShape::setFlag: Shape is a part of pruning structure, pruning structure is now invalid!");
  			shapeManager->getPruningStructure()->invalidate(mActor);
  		}
  	}
  }
  
  void NpShape::setFlag(PxShapeFlag::Enum flag, bool value)
  {
  	NP_WRITE_CHECK(getOwnerScene());
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setFlag: shared shapes attached to actors are not writable.");
  	PX_SIMD_GUARD;
  
  	PxShapeFlags shapeFlags = mShape.getFlags();
  	shapeFlags = value ? shapeFlags | flag : shapeFlags & ~flag;
  	
  	setFlagsInternal(shapeFlags);
  }
  
  void NpShape::setFlags(PxShapeFlags inFlags)
  {
  	NP_WRITE_CHECK(getOwnerScene());
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setFlags: shared shapes attached to actors are not writable.");
  	PX_SIMD_GUARD;
  
  	setFlagsInternal(inFlags);
  }
  
  PxShapeFlags NpShape::getFlags() const
  {
  	NP_READ_CHECK(getOwnerScene());
  	return mShape.getFlags();
  }
  
  bool NpShape::isExclusive() const
  {
  	NP_READ_CHECK(getOwnerScene());
  	return (mExclusiveAndActorCount & EXCLUSIVE_MASK) != 0;
  }
  
  void NpShape::onActorAttach(PxRigidActor& actor)
  {
  	incRefCount();
  	if(isExclusiveFast())
  		mActor = &actor;
  	Ps::atomicIncrement(&mExclusiveAndActorCount);
  }
  
  void NpShape::onActorDetach()
  {
  	PX_ASSERT(getActorCount() > 0);
  	Ps::atomicDecrement(&mExclusiveAndActorCount);
  	if(isExclusiveFast())
  		mActor = NULL;
  	decRefCount();
  }
  
  void NpShape::setName(const char* debugName)		
  {
  	NP_WRITE_CHECK(getOwnerScene());
  	PX_CHECK_AND_RETURN(isWritable(), "PxShape::setName: shared shapes attached to actors are not writable.");
  
  	mName = debugName;
  
  	updatePvdProperties(mShape);
  }
  
  const char* NpShape::getName() const
  {
  	NP_READ_CHECK(getOwnerScene());
  
  	return mName;
  }
  
  NpScene* NpShape::getOwnerScene()	const 
  {	
  	return mActor ? NpActor::getOwnerScene(*mActor) : NULL; 
  }
  
  NpScene* NpShape::getAPIScene()	const 
  {	
  	// gets called when we update SQ structures due to a write - in which case there must be an actor 
  	PX_ASSERT(mActor);
  	return NpActor::getAPIScene(*mActor);
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  
  namespace physx
  {
  Sc::RigidCore* NpShapeGetScRigidObjectFromScbSLOW(const Scb::Shape& scb)
  {
  	const NpShape* np = getNpShape(&scb);
  	return np->NpShape::getActor() ? &np->getScRigidObjectExclusive() : NULL;
  }
  
  size_t NpShapeGetScPtrOffset()
  {
  	const size_t offset = size_t(&(reinterpret_cast<NpShape*>(0)->getScbShape().getScShape()));	
  	return offset;
  }
  
  void NpShapeIncRefCount(Scb::Shape& scb)
  {
  	NpShape* np = const_cast<NpShape*>(getNpShape(&scb));
  	np->incRefCount();
  }
  
  void NpShapeDecRefCount(Scb::Shape& scb)
  {
  	NpShape* np = const_cast<NpShape*>(getNpShape(&scb));
  	np->decRefCount();
  }
  }
  
  // see NpConvexMesh.h, NpHeightField.h, NpTriangleMesh.h for details on how ref counting works for meshes
  Cm::RefCountable* NpShape::getMeshRefCountable()
  {
  	switch(mShape.getGeometryType())
  	{
  		case PxGeometryType::eCONVEXMESH:
  			return static_cast<Gu::ConvexMesh*>(
  				static_cast<const PxConvexMeshGeometry&>(mShape.getGeometry()).convexMesh);
  
  		case PxGeometryType::eHEIGHTFIELD:
  			return static_cast<Gu::HeightField*>(
  				static_cast<const PxHeightFieldGeometry&>(mShape.getGeometry()).heightField);
  
  		case PxGeometryType::eTRIANGLEMESH:
  			return static_cast<Gu::TriangleMesh*>(
  				static_cast<const PxTriangleMeshGeometry&>(mShape.getGeometry()).triangleMesh);
  		
  		case PxGeometryType::eSPHERE:
  		case PxGeometryType::ePLANE:
  		case PxGeometryType::eCAPSULE:
  		case PxGeometryType::eBOX:
  		case PxGeometryType::eGEOMETRY_COUNT:
  		case PxGeometryType::eINVALID:
  			break;
  	}
  	return NULL;
  }
  
  bool NpShape::isWritable()
  {
  	// a shape is writable if it's exclusive, or it's not connected to any actors (which is true if the ref count is 1 and the user ref is not released.)
  	return isExclusiveFast() || (getRefCount()==1 && (mBaseFlags & PxBaseFlag::eIS_RELEASABLE));
  }
  
  void NpShape::incMeshRefCount()
  {
  	Cm::RefCountable* npMesh = getMeshRefCountable();
  	if(npMesh)
  		npMesh->incRefCount();
  }
  
  void NpShape::decMeshRefCount()
  {
  	Cm::RefCountable* npMesh = getMeshRefCountable();
  	if(npMesh)
  		npMesh->decRefCount();
  }
  
  bool NpShape::checkMaterialSetup(const PxGeometry& geom, const char* errorMsgPrefix, PxMaterial*const* materials, PxU16 materialCount)
  {
  	for(PxU32 i=0; i<materialCount; ++i)
  	{
  		if(!materials[i])
  		{
  			Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, 
  					"material pointer %d is NULL!", i);
  			return false;
  		}
  	}
  
  	// check that simple shapes don't get assigned multiple materials
  	if (materialCount > 1 && (geom.getType() != PxGeometryType::eHEIGHTFIELD) && (geom.getType() != PxGeometryType::eTRIANGLEMESH))
  	{
  		Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, 
  			"%s: multiple materials defined for single material geometry!", errorMsgPrefix);
  		return false;
  	}
  
  	//  verify we provide all materials required
  	if (materialCount > 1 && (geom.getType() == PxGeometryType::eTRIANGLEMESH))
  	{
  		const PxTriangleMeshGeometry& meshGeom = static_cast<const PxTriangleMeshGeometry&>(geom);
  		const PxTriangleMesh& mesh = *meshGeom.triangleMesh;
  		if(mesh.getTriangleMaterialIndex(0) != 0xffff)
  		{
  			for(PxU32 i = 0; i < mesh.getNbTriangles(); i++)
  			{
  				const PxMaterialTableIndex meshMaterialIndex = mesh.getTriangleMaterialIndex(i);
  				if(meshMaterialIndex >= materialCount)
  				{
  					Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, 
  						"%s: PxTriangleMesh material indices reference more materials than provided!", errorMsgPrefix);
  					break;
  				}
  			}
  		}
  	}
  	if (materialCount > 1 && (geom.getType() == PxGeometryType::eHEIGHTFIELD))
  	{
  		const PxHeightFieldGeometry& meshGeom = static_cast<const PxHeightFieldGeometry&>(geom);
  		const PxHeightField& mesh = *meshGeom.heightField;
  		if(mesh.getTriangleMaterialIndex(0) != 0xffff)
  		{
  			const PxU32 nbTris = mesh.getNbColumns()*mesh.getNbRows()*2;
  			for(PxU32 i = 0; i < nbTris; i++)
  			{
  				const PxMaterialTableIndex meshMaterialIndex = mesh.getTriangleMaterialIndex(i);
  				if(meshMaterialIndex != PxHeightFieldMaterial::eHOLE && meshMaterialIndex >= materialCount)
  				{
  					Ps::getFoundation().error(PxErrorCode::eINVALID_PARAMETER, __FILE__, __LINE__, 
  						"%s: PxHeightField material indices reference more materials than provided!", errorMsgPrefix);
  					break;
  				}
  			}
  		}
  	}
  
  	return true;
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  