1039 lines
30 KiB
C++
1039 lines
30 KiB
C++
//
|
|
// 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-2021 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 <PsString.h>
|
|
#include "SampleLargeWorld.h"
|
|
#include "RenderMeshActor.h"
|
|
#include "RenderMaterial.h"
|
|
#include "RenderPhysX3Debug.h"
|
|
|
|
#include <SampleBaseInputEventIds.h>
|
|
#include <SamplePlatform.h>
|
|
#include <SampleUserInput.h>
|
|
#include <SampleUserInputIds.h>
|
|
#include <SampleUserInputDefines.h>
|
|
#include "SampleLargeWorldInputEventIds.h"
|
|
|
|
using namespace SampleRenderer;
|
|
using namespace SampleFramework;
|
|
|
|
#include "ChunkLoader.h"
|
|
#include "wavefront.h"
|
|
#include "characterkinematic/PxControllerManager.h"
|
|
#include "SampleCCTActor.h"
|
|
#include "SampleCCTCameraController.h"
|
|
|
|
#include "extensions/PxDefaultSimulationFilterShader.h"
|
|
|
|
static const PxF32 gContactOffset = 0.01f;
|
|
static const PxF32 gStepOffset = 0.05f;
|
|
static const PxF32 gSlopeLimit = 0.0f;
|
|
static const PxF32 gInvisibleWallsHeight = 0.0f;
|
|
static const PxF32 gMaxJumpHeight = 0.0f;
|
|
|
|
static const PxF32 gScaleFactor = 1.5f;
|
|
static const PxF32 gStandingSize = 1.0f * gScaleFactor;
|
|
static const PxF32 gCrouchingSize = 0.25f * gScaleFactor;
|
|
static const PxF32 gControllerRadius = 0.3f * gScaleFactor;
|
|
|
|
static const PxF32 CAMERA_MAX_SPEED = 120.0f;
|
|
|
|
const char* gDynamic = "Dynamic";
|
|
|
|
REGISTER_SAMPLE(SampleLargeWorld, "SampleLargeWorld")
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
SampleLargeWorld::SampleLargeWorld(PhysXSampleApplication& app)
|
|
: PhysXSample(app)
|
|
, mCCTCamera(NULL)
|
|
, mActor(NULL)
|
|
, mControllerManager(NULL)
|
|
, mDiskIOTime(0)
|
|
, mPhysxStreaming(0)
|
|
, mGraphicStreaming(0)
|
|
, mReadyToSyncCCT(false)
|
|
, mAddRenderActor(false)
|
|
, mPick(false)
|
|
, mKeyShiftDown(false)
|
|
, mStringTable(NULL)
|
|
{
|
|
mCreateGroundPlane = false;
|
|
mControllerInitialPosition = PxExtendedVec3(1035.0f, 49.0f, 989.0f);
|
|
mLastCCTPosition = mControllerInitialPosition;
|
|
mDefaultCameraSpeed = mCameraController.getCameraSpeed();
|
|
|
|
PxMemZero(mSkybox, sizeof(mSkybox));
|
|
}
|
|
|
|
SampleLargeWorld::~SampleLargeWorld()
|
|
{
|
|
DELETESINGLE(mActor);
|
|
for(ObjMeshMap::Iterator iter = mRenderMeshCache.getIterator(); !iter.done(); ++iter)
|
|
{
|
|
RAWMesh& mesh = iter->second;
|
|
|
|
PxVec3 *verts = const_cast<PxVec3*>(mesh.mVerts);
|
|
SAMPLE_FREE(verts);
|
|
mesh.mVerts = NULL;
|
|
|
|
PxVec3 *normals = const_cast<PxVec3*>(mesh.mVertexNormals);
|
|
SAMPLE_FREE(normals);
|
|
mesh.mVertexNormals = NULL;
|
|
|
|
PxVec3 *colors = const_cast<PxVec3*>(mesh.mVertexColors);
|
|
SAMPLE_FREE(colors);
|
|
mesh.mVertexColors = NULL;
|
|
|
|
PxReal *uvs = const_cast<PxReal*>(mesh.mUVs);
|
|
SAMPLE_FREE(uvs);
|
|
mesh.mUVs = NULL;
|
|
|
|
PxU32 *indices = const_cast<PxU32*>(mesh.mIndices);
|
|
SAMPLE_FREE(indices);
|
|
mesh.mIndices = NULL;
|
|
}
|
|
}
|
|
|
|
void SampleLargeWorld::setCCDActive(PxShape& shape)
|
|
{
|
|
PxFilterData fd = shape.getSimulationFilterData();
|
|
fd.word3 |= CCD_FLAG;
|
|
shape.setSimulationFilterData(fd);
|
|
}
|
|
|
|
bool SampleLargeWorld::isCCDActive(PxFilterData& filterData)
|
|
{
|
|
return filterData.word3 & CCD_FLAG ? true : false;
|
|
}
|
|
|
|
PxFilterFlags SampleLargeWorld::filter( PxFilterObjectAttributes attributes0,
|
|
PxFilterData filterData0,
|
|
PxFilterObjectAttributes attributes1,
|
|
PxFilterData filterData1,
|
|
PxPairFlags& pairFlags,
|
|
const void* constantBlock,
|
|
PxU32 constantBlockSize)
|
|
{
|
|
PxFilterFlags filterFlags = PxDefaultSimulationFilterShader(attributes0,
|
|
filterData0, attributes1, filterData1, pairFlags, constantBlock, constantBlockSize);
|
|
|
|
if (isCCDActive(filterData0) && isCCDActive(filterData1))
|
|
{
|
|
pairFlags |= PxPairFlag::eSOLVE_CONTACT;
|
|
pairFlags |= PxPairFlag::eDETECT_CCD_CONTACT;
|
|
}
|
|
|
|
return filterFlags;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if PX_INTEL_FAMILY
|
|
static const bool gFlip = false;
|
|
#elif PX_PPC
|
|
static const bool gFlip = true;
|
|
#elif PX_ARM_FAMILY
|
|
static const bool gFlip = false;
|
|
#else
|
|
#error Unknown platform!
|
|
#endif
|
|
|
|
static PX_INLINE void Flip(PxU32& v)
|
|
{
|
|
PxU8* b = (PxU8*)&v;
|
|
|
|
PxU8 temp = b[0];
|
|
b[0] = b[3];
|
|
b[3] = temp;
|
|
temp = b[1];
|
|
b[1] = b[2];
|
|
b[2] = temp;
|
|
}
|
|
|
|
static PX_INLINE void Flip(PxF32& v)
|
|
{
|
|
Flip((PxU32&)v);
|
|
}
|
|
|
|
class PxBinFile: public PxDefaultFileInputData
|
|
{
|
|
public:
|
|
PxBinFile(const char* name):PxDefaultFileInputData(name){}
|
|
~PxBinFile(){}
|
|
|
|
PX_FORCE_INLINE PxU32 LoadDword(){ PxU32 t; read(&t, sizeof(PxU32)); if(gFlip) Flip(t); return t;}
|
|
PX_FORCE_INLINE PxF32 LoadFloat(){ PxF32 t; read(&t, sizeof(PxF32)); if(gFlip) Flip(t); return t;}
|
|
};
|
|
|
|
void SampleLargeWorld::BinData::serialize( SampleLargeWorld* parent, const char* terrainFile)
|
|
{
|
|
PxBinFile binFile(terrainFile);
|
|
|
|
if( !binFile.isValid())
|
|
{
|
|
char errMsg[256];
|
|
Ps::snprintf(errMsg, 256, "Couldn't load %s\n", terrainFile);
|
|
parent->fatalError(errMsg);
|
|
|
|
return;
|
|
}
|
|
|
|
PxU32 nbMeshes = binFile.LoadDword();
|
|
|
|
//Suppose the terrain is N*N
|
|
mDim = (CoordType)PxSqrt((PxF32)nbMeshes);
|
|
|
|
PxU32 totalNbTris = 0;
|
|
PxU32 totalNbVerts = 0;
|
|
|
|
mTerrainVertices.resize( nbMeshes );
|
|
mTerrainIndices.resize( nbMeshes );
|
|
|
|
for(PxU32 i = 0; i < nbMeshes; ++i)
|
|
{
|
|
binFile.LoadDword();
|
|
binFile.LoadDword();
|
|
|
|
const PxU32 nbVerts = binFile.LoadDword();
|
|
const PxU32 nbFaces = binFile.LoadDword();
|
|
|
|
totalNbTris += nbFaces;
|
|
totalNbVerts += nbVerts;
|
|
|
|
SampleArray<PxVec3>& vertices = mTerrainVertices[i];
|
|
vertices.resize(nbVerts);
|
|
|
|
for(PxU32 j = 0; j < nbVerts; ++j)
|
|
{
|
|
vertices[j].x = binFile.LoadFloat();
|
|
vertices[j].y = binFile.LoadFloat();
|
|
vertices[j].z = binFile.LoadFloat();
|
|
}
|
|
|
|
SampleArray<PxU32>& indices = mTerrainIndices[i];
|
|
indices.resize(nbFaces * 3);
|
|
|
|
for(PxU32 j = 0; j < nbFaces * 3; ++j)
|
|
{
|
|
indices[j] = binFile.LoadDword();
|
|
}
|
|
}
|
|
}
|
|
|
|
//Hardcoded terrain.bin terrain.
|
|
void SampleLargeWorld::onInit()
|
|
{
|
|
PhysXSample::onInit();
|
|
mCameraController.setCameraSpeed( 2.0f );
|
|
|
|
mStringTable = &PxStringTableExt::createStringTable( Ps::getAllocator() );
|
|
|
|
mApplication.setMouseCursorHiding(true);
|
|
mApplication.setMouseCursorRecentering(true);
|
|
|
|
//Disable collision between differnt groups
|
|
PxSetGroupCollisionFlag(DEFAULT_COLLISION_GROUP,FAN_COLLISION_GROUP,false);
|
|
PxSetGroupCollisionFlag(DEFAULT_COLLISION_GROUP,PICKING_COLLISION_GROUP,false);
|
|
PxSetGroupCollisionFlag(FAN_COLLISION_GROUP,PICKING_COLLISION_GROUP,false);
|
|
|
|
mBGLoader = SAMPLE_NEW(BackgroundLoader)(*this, N1, N2-1, CHUNK_WIDTH);
|
|
|
|
const char* terrainFile = getSampleMediaFilename("Terrain.bin");
|
|
binData.serialize(this, terrainFile);
|
|
|
|
//Load the skydome.
|
|
importRAWFile("sky_mission_race1.raw", 2.0f);
|
|
|
|
//Load the terrain material
|
|
{
|
|
RAWTexture data;
|
|
data.mName = "SampleLargeWorld_simplegrass.bmp";
|
|
RenderTexture* grassTexture = createRenderTextureFromRawTexture(data);
|
|
RenderMaterial* roadGravelMaterial = SAMPLE_NEW(RenderMaterial)(*getRenderer(),
|
|
PxVec3(1.0f, 1.0f, 1.0f), 1.0f, false, MATERIAL_ROAD_GRASS, grassTexture);
|
|
mRenderMaterials.push_back(roadGravelMaterial);
|
|
}
|
|
|
|
{
|
|
RAWTexture data;
|
|
data.mName = "SampleLargeWorld_simplecity.bmp";
|
|
RenderTexture* bricksTexture = createRenderTextureFromRawTexture(data);
|
|
RenderMaterial* bricksMaterial = SAMPLE_NEW(RenderMaterial)(*getRenderer(),
|
|
PxVec3(1.0f, 1.0f, 1.0f), 1.0f, false, MATERIAL_BUILDING, bricksTexture);
|
|
mRenderMaterials.push_back(bricksMaterial);
|
|
}
|
|
{
|
|
RAWTexture data;
|
|
data.mName = "SampleLargeWorld_simplefence.bmp";
|
|
RenderTexture* fencingTexture = createRenderTextureFromRawTexture(data);
|
|
RenderMaterial* fencingMaterial = SAMPLE_NEW(RenderMaterial)(*getRenderer(),
|
|
PxVec3(1.0f, 1.0f, 1.0f), 1.0f, false, MATERIAL_FARM, fencingTexture);
|
|
mRenderMaterials.push_back(fencingMaterial);
|
|
}
|
|
{
|
|
RAWTexture data;
|
|
data.mName = "SampleLargeWorld_simpletree.bmp";
|
|
RenderTexture* treeTexture = createRenderTextureFromRawTexture(data);
|
|
RenderMaterial* treeMaterial = SAMPLE_NEW(RenderMaterial)(*getRenderer(),
|
|
PxVec3(1.0f, 1.0f, 1.0f), 1.0f, false, MATERIAL_TREE, treeTexture);
|
|
mRenderMaterials.push_back(treeMaterial);
|
|
}
|
|
//Set up the fog.
|
|
getRenderer()->setFog(SampleRenderer::RendererColor(40,40,40), 225.0f);
|
|
|
|
mControllerManager = PxCreateControllerManager(getActiveScene());
|
|
mControllerManager->setOverlapRecoveryModule(false);
|
|
|
|
ControlledActorDesc desc;
|
|
desc.mType = PxControllerShapeType::eCAPSULE;
|
|
desc.mPosition = mControllerInitialPosition;
|
|
desc.mSlopeLimit = gSlopeLimit;
|
|
desc.mContactOffset = gContactOffset;
|
|
desc.mStepOffset = gStepOffset;
|
|
desc.mInvisibleWallHeight = gInvisibleWallsHeight;
|
|
desc.mMaxJumpHeight = gMaxJumpHeight;
|
|
desc.mRadius = gControllerRadius;
|
|
desc.mHeight = gStandingSize;
|
|
desc.mCrouchHeight = gCrouchingSize;
|
|
desc.mReportCallback = this;
|
|
desc.mBehaviorCallback = this;
|
|
{
|
|
mActor = SAMPLE_NEW(ControlledActor)(*this);
|
|
|
|
{
|
|
PxSceneWriteLock scopedLock(*mScene);
|
|
mActor->init(desc, mControllerManager);
|
|
}
|
|
|
|
mCCTCamera = SAMPLE_NEW(SampleCCTCameraController)(*this);
|
|
mCCTCamera->setControlled(&mActor, 0, 1);
|
|
mCCTCamera->setFilterCallback(this);
|
|
mCCTCamera->setGravity(-20.0f);
|
|
|
|
setCameraController(mCCTCamera);
|
|
|
|
mCCTCamera->setView(0,0);
|
|
mCCTCamera->setCameraMaxSpeed( CAMERA_MAX_SPEED );
|
|
mCCTCamera->enableCCT(false);
|
|
}
|
|
|
|
mWorldBound.minimum = PxVec3(-128.0f,-256.0f, -128.0f);
|
|
mWorldBound.maximum = PxVec3(-128.0f + 256.0f*N2,256.0f, -128.0f + 256.0f*N2);
|
|
}
|
|
|
|
void SampleLargeWorld::onShutdown()
|
|
{
|
|
mCameraController.setCameraSpeed(mDefaultCameraSpeed);
|
|
|
|
if(isPaused())
|
|
{
|
|
togglePause();
|
|
}
|
|
|
|
mScene->fetchResults(true);
|
|
DELETESINGLE(mBGLoader);
|
|
|
|
{
|
|
PxSceneWriteLock scopedLock(*mScene);
|
|
|
|
if( !mAddRenderActor )
|
|
{
|
|
RenderBaseActor* actor = mActor->getRenderActorStanding();
|
|
DELETESINGLE( actor );
|
|
|
|
actor = mActor->getRenderActorCrouching();
|
|
DELETESINGLE( actor );
|
|
}
|
|
|
|
DELETESINGLE(mCCTCamera);
|
|
mControllerManager->release();
|
|
|
|
releaseJoints();
|
|
}
|
|
|
|
if(mStringTable)
|
|
{
|
|
mStringTable->release();
|
|
mStringTable = NULL;
|
|
}
|
|
|
|
PhysXSample::onShutdown();
|
|
}
|
|
|
|
void SampleLargeWorld::onTickPreRender(PxF32 dtime)
|
|
{
|
|
if(!mPause)
|
|
{
|
|
mBGLoader->onTick();
|
|
|
|
//add render actor here, otherwise it has issues on render
|
|
if(!mReadyToSyncCCT)
|
|
{
|
|
mReadyToSyncCCT = readyToSyncCCT();
|
|
}
|
|
|
|
if( !mAddRenderActor && mReadyToSyncCCT )
|
|
{
|
|
RenderBaseActor* actor0 = mActor->getRenderActorStanding();
|
|
RenderBaseActor* actor1 = mActor->getRenderActorCrouching();
|
|
|
|
if(actor0)
|
|
{
|
|
mRenderActors.push_back(actor0);
|
|
}
|
|
|
|
if(actor1)
|
|
{
|
|
mRenderActors.push_back(actor1);
|
|
}
|
|
|
|
mAddRenderActor = true;
|
|
}
|
|
|
|
if( mReadyToSyncCCT && !mCCTCamera->getCCTState() )
|
|
{
|
|
mCCTCamera->enableCCT(true);
|
|
}
|
|
}
|
|
|
|
for( PxU32 i = 0; i < 2; i++)
|
|
{
|
|
if(mSkybox[i])
|
|
mSkybox[i]->setTransform(PxTransform(getCamera().getPos()));
|
|
}
|
|
|
|
PhysXSample::onTickPreRender(dtime);
|
|
|
|
if(mIsFlyCamera)
|
|
{
|
|
getDebugRenderer()->addAABB(mWorldBound, RendererColor(0,255,0), RENDER_DEBUG_WIREFRAME);
|
|
}
|
|
|
|
#if ENABLE_PROGRESS_BAR
|
|
mBGLoader->mLoaderStatusLock.lockReader(true);
|
|
mProgressBarRatio = mBGLoader->mQueryProgress;
|
|
mDiskIOTime = mBGLoader->mDiskIOTime;
|
|
mPhysxStreaming = mBGLoader->mPhyXStreamTime;
|
|
mGraphicStreaming = mBGLoader->mGraphicStreamTime;
|
|
mBGLoader->mLoaderStatusLock.unlockReader();
|
|
#endif
|
|
|
|
mProgressBarRatio = PxClamp( mProgressBarRatio, 0.0f, 1.0f);
|
|
|
|
if(!mPause && mReadyToSyncCCT)
|
|
{
|
|
mActor->sync();
|
|
}
|
|
}
|
|
|
|
void SampleLargeWorld::onDigitalInputEvent(const SampleFramework::InputEvent& ie, bool val)
|
|
{
|
|
if(val)
|
|
{
|
|
switch(ie.m_Id)
|
|
{
|
|
case RETRY:
|
|
{
|
|
PxSceneWriteLock scopedLock(*mScene);
|
|
mActor->reset();
|
|
mCCTCamera->setView(0,0);
|
|
}
|
|
break;
|
|
|
|
case FLY_CAMERA:
|
|
{
|
|
toggleFlyCamera();
|
|
mReadyToSyncCCT = false;
|
|
mPause = !mPause;
|
|
if( mPause )
|
|
{
|
|
mCCTCamera->enableCCT(false);
|
|
}
|
|
|
|
}
|
|
break;
|
|
case THROW_IMPORTANTOBJECT:
|
|
case THROW_UNIMPORTANCTOBJECT:
|
|
{
|
|
const PxVec3 pos = getCamera().getPos();
|
|
const PxVec3 vel = getCamera().getViewDir() * getDebugObjectsVelocity();
|
|
|
|
bool isImportant = ie.m_Id == THROW_IMPORTANTOBJECT;
|
|
|
|
PxSceneWriteLock scopedLock(*mScene);
|
|
|
|
PxRigidDynamic* actor = createBox(pos, getDebugBoxObjectExtents(), &vel,
|
|
isImportant ? mManagedMaterials[MATERIAL_RED] : mManagedMaterials[MATERIAL_GREEN], mDefaultDensity);
|
|
|
|
actor->setName(gDynamic);
|
|
|
|
mBGLoader->addDynamicObject(actor, isImportant);
|
|
}
|
|
break;
|
|
|
|
case PICK_NEARSETOBJECT:
|
|
{
|
|
mPick = !mPick;
|
|
if(mPick)
|
|
{
|
|
attachNearestObjectsToCCT();
|
|
}
|
|
else
|
|
{
|
|
releaseJoints();
|
|
}
|
|
}
|
|
break;
|
|
case CAMERA_SHIFT_SPEED:
|
|
{
|
|
mKeyShiftDown = true;
|
|
break;
|
|
}
|
|
case CAMERA_SPEED_INCREASE:
|
|
{
|
|
PxReal speed = getCurrentCameraController()->getCameraSpeed();
|
|
if(speed * 2 >= CAMERA_MAX_SPEED)
|
|
{
|
|
if(mKeyShiftDown)
|
|
shdfnd::printFormatted("running speed exceeds the maximum speed\n");
|
|
else
|
|
shdfnd::printFormatted("walking speed exceeds the maximum speed\n");
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(ie.m_Id == CAMERA_SHIFT_SPEED) mKeyShiftDown = false;
|
|
}
|
|
|
|
PhysXSample::onDigitalInputEvent(ie,val);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void SampleLargeWorld::customizeSceneDesc(PxSceneDesc& desc)
|
|
{
|
|
desc.flags |= PxSceneFlag::eENABLE_CCD;
|
|
desc.flags |= PxSceneFlag::eREQUIRE_RW_LOCK;
|
|
desc.filterShader = filter;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void SampleLargeWorld::customizeSample(SampleSetup& setup)
|
|
{
|
|
setup.mName = "SampleLargeWorld";
|
|
}
|
|
|
|
void SampleLargeWorld::onSubstep(PxF32 dtime)
|
|
{
|
|
PhysXSample::onSubstep(dtime);
|
|
|
|
mBGLoader->updateChunk(getCamera().getPos());
|
|
|
|
PxExtendedVec3 curCCTPosition = mActor->getController()->getPosition();
|
|
if(mWorldBound.contains(toVec3(curCCTPosition)))
|
|
{
|
|
mLastCCTPosition = curCCTPosition;
|
|
}
|
|
else
|
|
{
|
|
PxSceneWriteLock scopedLock(*mScene);
|
|
mActor->getController()->setPosition(mLastCCTPosition);
|
|
}
|
|
}
|
|
|
|
void SampleLargeWorld::collectInputEvents(std::vector<const SampleFramework::InputEvent*>& inputEvents)
|
|
{
|
|
PhysXSample::collectInputEvents(inputEvents);
|
|
|
|
getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(STEP_ONE_FRAME);
|
|
getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(PAUSE_SAMPLE);
|
|
getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(SPAWN_DEBUG_OBJECT);
|
|
getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(CAMERA_JUMP);
|
|
getApplication().getPlatform()->getSampleUserInput()->unregisterInputEvent(CAMERA_CONTROLLER_INCREASE);
|
|
|
|
DIGITAL_INPUT_EVENT_DEF(CAMERA_SPEED_INCREASE, WKEY_ADD, OSXKEY_ADD, LINUXKEY_ADD );
|
|
DIGITAL_INPUT_EVENT_DEF(CAMERA_SPEED_DECREASE, WKEY_SUBTRACT, OSXKEY_SUBTRACT, LINUXKEY_SUBTRACT );
|
|
|
|
DIGITAL_INPUT_EVENT_DEF(THROW_IMPORTANTOBJECT, WKEY_I, OSXKEY_I, LINUXKEY_I );
|
|
DIGITAL_INPUT_EVENT_DEF(THROW_UNIMPORTANCTOBJECT, WKEY_U, OSXKEY_U, LINUXKEY_U );
|
|
DIGITAL_INPUT_EVENT_DEF(PICK_NEARSETOBJECT, WKEY_E, OSXKEY_E, LINUXKEY_E );
|
|
|
|
DIGITAL_INPUT_EVENT_DEF(FLY_CAMERA, WKEY_M, OSXKEY_M, LINUXKEY_M );
|
|
DIGITAL_INPUT_EVENT_DEF(FLY_CAMERA, GAMEPAD_WEST, OSXKEY_UNKNOWN, LINUXKEY_UNKNOWN );
|
|
|
|
DIGITAL_INPUT_EVENT_DEF(RETRY, WKEY_R, OSXKEY_R, LINUXKEY_R );
|
|
|
|
DIGITAL_INPUT_EVENT_DEF(CAMERA_SPEED_INCREASE, GAMEPAD_RIGHT_SHOULDER_TOP, GAMEPAD_RIGHT_SHOULDER_TOP, LINUXKEY_UNKNOWN );
|
|
DIGITAL_INPUT_EVENT_DEF(CAMERA_SPEED_DECREASE, GAMEPAD_LEFT_SHOULDER_TOP, GAMEPAD_LEFT_SHOULDER_TOP, LINUXKEY_UNKNOWN );
|
|
}
|
|
|
|
void SampleLargeWorld::helpRender(PxU32 x, PxU32 y, PxU8 textAlpha)
|
|
{
|
|
Renderer* renderer = getRenderer();
|
|
const PxU32 yInc = 18;
|
|
const PxReal scale = 0.5f;
|
|
const PxReal shadowOffset = 6.0f;
|
|
const RendererColor textColor(255, 255, 255, textAlpha);
|
|
const bool isMouseSupported = getApplication().getPlatform()->getSampleUserInput()->mouseSupported();
|
|
const bool isPadSupported = getApplication().getPlatform()->getSampleUserInput()->gamepadSupported();
|
|
const char* msg;
|
|
|
|
if (isMouseSupported && isPadSupported)
|
|
renderer->print(x, y += yInc, "Use mouse or right stick to rotate", scale, shadowOffset, textColor);
|
|
else if (isMouseSupported)
|
|
renderer->print(x, y += yInc, "Use mouse to rotate", scale, shadowOffset, textColor);
|
|
else if (isPadSupported)
|
|
renderer->print(x, y += yInc, "Use right stick to rotate", scale, shadowOffset, textColor);
|
|
if (isPadSupported)
|
|
renderer->print(x, y += yInc, "Use left stick to move",scale, shadowOffset, textColor);
|
|
msg = mApplication.inputMoveInfoMsg("Press "," to move", CAMERA_MOVE_FORWARD,CAMERA_MOVE_BACKWARD, CAMERA_MOVE_LEFT, CAMERA_MOVE_RIGHT);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
|
|
msg = mApplication.inputInfoMsg("Press "," to move fast", CAMERA_SHIFT_SPEED, -1);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg, scale, shadowOffset, textColor);
|
|
msg = mApplication.inputInfoMsg("Press "," to throw an object", SPAWN_DEBUG_OBJECT, -1);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
|
|
|
|
msg = mApplication.inputInfoMsg("Press "," to toggle fly camera", FLY_CAMERA, -1);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
|
|
|
|
msg = mApplication.inputInfoMsg("Press "," to increase fly camera speed", CAMERA_SPEED_INCREASE, -1);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
|
|
|
|
msg = mApplication.inputInfoMsg("Press "," to decrease fly camera speed", CAMERA_SPEED_DECREASE, -1);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
|
|
|
|
msg = mApplication.inputInfoMsg("Press "," to reset CCT position", RETRY, -1);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
|
|
|
|
msg = mApplication.inputInfoMsg("Press "," to throw an important object", THROW_IMPORTANTOBJECT, -1);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
|
|
|
|
msg = mApplication.inputInfoMsg("Press "," to throw an unimportant object", THROW_UNIMPORTANCTOBJECT, -1);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
|
|
|
|
msg = mApplication.inputInfoMsg("Press "," to pick up the nearest objects maximum=5, or release objects", PICK_NEARSETOBJECT, -1);
|
|
if(msg)
|
|
renderer->print(x, y += yInc, msg,scale, shadowOffset, textColor);
|
|
}
|
|
|
|
void SampleLargeWorld::descriptionRender(PxU32 x, PxU32 y, PxU8 textAlpha)
|
|
{
|
|
bool print=(textAlpha!=0.0f);
|
|
|
|
if(print)
|
|
{
|
|
Renderer* renderer = getRenderer();
|
|
const PxU32 yInc = 24;
|
|
const PxReal scale = 0.5f;
|
|
const PxReal shadowOffset = 6.0f;
|
|
const RendererColor textColor(255, 255, 255, textAlpha);
|
|
|
|
char line0[256]="This sample demonstrates the PhysX streaming feature. It can load";
|
|
char line1[256]="and unload parts of map procedurally, treats it as an infinite map.";
|
|
char line2[256]="We serialize/deserialize files in the background thread, to avoid";
|
|
char line3[256]="spikes in the framerate when your HD is lagging or something. We add";
|
|
char line4[256]="one farm, one city, some trees and one fortress for gameplay activity.";
|
|
|
|
renderer->print(x, y+=yInc, line0, scale, shadowOffset, textColor);
|
|
renderer->print(x, y+=yInc, line1, scale, shadowOffset, textColor);
|
|
renderer->print(x, y+=yInc, line2, scale, shadowOffset, textColor);
|
|
renderer->print(x, y+=yInc, line3, scale, shadowOffset, textColor);
|
|
renderer->print(x, y+=yInc, line4, scale, shadowOffset, textColor);
|
|
}
|
|
}
|
|
|
|
void SampleLargeWorld::customizeRender()
|
|
{
|
|
PhysXSample::customizeRender();
|
|
|
|
renderProgressBar( mProgressBarRatio, false );
|
|
Renderer* renderer = getRenderer();
|
|
if(!renderer) return;
|
|
|
|
//ScopedRender renderSection(*renderer);
|
|
//if(renderSection)
|
|
{
|
|
const PxU32 yInc = 18;
|
|
char textBuf[256];
|
|
|
|
Camera& camera = getCamera();
|
|
|
|
Ps::snprintf(textBuf, 256, "Disk IO\t\t\t%0.1f ms", mDiskIOTime*1000.0f);
|
|
renderer->print(10, camera.getScreenHeight() - yInc*6, textBuf);
|
|
|
|
Ps::snprintf(textBuf, 256, "PhysX Streaming %0.1f ms", mPhysxStreaming*1000.0f);
|
|
renderer->print(10, camera.getScreenHeight() - yInc*5, textBuf);
|
|
|
|
Ps::snprintf(textBuf, 256, "Graphic Streaming %0.1f ms", mGraphicStreaming*1000.0f);
|
|
renderer->print(10, camera.getScreenHeight() - yInc*4, textBuf);
|
|
|
|
PxVec3 cameraPos = camera.getPos();
|
|
Ps::snprintf(textBuf, 256, "Position x=%0.1f,z=%0.1f", cameraPos.x, cameraPos.z);
|
|
renderer->print(camera.getScreenWidth() - 300, 10, textBuf);
|
|
}
|
|
}
|
|
|
|
//Sky box should not be culled
|
|
void SampleLargeWorld::newMesh(const RAWMesh& data)
|
|
{
|
|
static PxU32 submeshCount = 0;
|
|
if(submeshCount + 1<= (sizeof(mSkybox)/sizeof(mSkybox[0])))
|
|
{
|
|
mSkybox[submeshCount] = createRenderMeshFromRawMesh(data);
|
|
submeshCount++;
|
|
}
|
|
}
|
|
|
|
RAWMesh* SampleLargeWorld::createRawMeshFromMeshGeom(const PxTriangleMeshGeometry& mesh, RAWMesh &rawMesh)
|
|
{
|
|
// Get physics geometry
|
|
PxTriangleMesh* tm = mesh.triangleMesh;
|
|
|
|
const PxU32 nbVerts = tm->getNbVertices();
|
|
const PxU32 nbTris = tm->getNbTriangles();
|
|
const void* tris = tm->getTriangles();
|
|
const bool has16BitTriangleIndices = tm->getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES ? true : false;
|
|
PX_ASSERT(has16BitTriangleIndices);
|
|
|
|
PxVec3* verts = (PxVec3*)SAMPLE_ALLOC(sizeof(PxVec3)*nbVerts);
|
|
PxMemCopy( verts, tm->getVertices(), sizeof(PxVec3)*nbVerts);
|
|
|
|
PxReal* normals = (PxReal*)SAMPLE_ALLOC(sizeof(PxReal)*nbVerts*3);
|
|
PxU32* indices32 = NULL;
|
|
|
|
if(has16BitTriangleIndices)
|
|
{
|
|
PxU16* indices16 = (PxU16*)tris;
|
|
indices32 = (PxU32*)SAMPLE_ALLOC(sizeof(PxU32)*nbTris*3);
|
|
for( PxU32 i = 0; i < nbTris; i++)
|
|
{
|
|
indices32[i*3+0] = indices16[i*3+0];
|
|
indices32[i*3+1] = indices16[i*3+1];
|
|
indices32[i*3+2] = indices16[i*3+2];
|
|
}
|
|
|
|
PxBuildSmoothNormals(nbTris, nbVerts, verts, NULL, indices16, (PxVec3*)normals, true);
|
|
}
|
|
else
|
|
{
|
|
indices32 = (PxU32*)tris;
|
|
PxBuildSmoothNormals(nbTris, nbVerts, verts, indices32, NULL, (PxVec3*)normals, true);
|
|
}
|
|
|
|
PxBounds3 bound = mesh.triangleMesh->getLocalBounds();
|
|
|
|
PxF32* uv =(PxF32*)SAMPLE_ALLOC(sizeof(PxF32)*nbVerts*2);
|
|
for(PxU32 i = 0; i < nbVerts; i++)
|
|
{
|
|
uv[i*2+0] = (verts[i].x - bound.minimum.x)/(bound.maximum.x - bound.minimum.x);
|
|
uv[i*2+1] = (verts[i].z - bound.minimum.z)/(bound.maximum.z - bound.minimum.z);
|
|
}
|
|
|
|
rawMesh.mNbVerts = nbVerts;
|
|
rawMesh.mVerts = verts;
|
|
rawMesh.mNbFaces = nbTris;
|
|
rawMesh.mIndices = indices32;
|
|
rawMesh.mVertexNormals = (PxVec3*)normals;
|
|
rawMesh.mUVs = uv;
|
|
rawMesh.mMaterialID = MATERIAL_ROAD_GRASS;
|
|
|
|
return &rawMesh;
|
|
}
|
|
|
|
RAWMesh* SampleLargeWorld::createRAWMeshFromObjMesh( const char* inObjFileName, const PxTransform& inPos, PxU32 inMaterialID, RAWMesh &outRawMesh )
|
|
{
|
|
const ObjMeshMap::Entry* entry = mRenderMeshCache.find(inObjFileName);
|
|
if( entry != NULL )
|
|
{
|
|
cloneMesh( entry->second, outRawMesh );
|
|
}
|
|
else
|
|
{
|
|
WavefrontObj wfo;
|
|
if (!wfo.loadObj(getSampleMediaFilename(inObjFileName), true))
|
|
return NULL;
|
|
|
|
PxU32 nbTris = wfo.mTriCount;
|
|
PxU32 nbVerts = wfo.mVertexCount;
|
|
|
|
PxVec3* verts = (PxVec3*)SAMPLE_ALLOC(sizeof(PxVec3)*nbVerts);
|
|
PxMemCopy( verts, wfo.mVertices, sizeof(PxVec3)*nbVerts );
|
|
|
|
PxU32* indices = (PxU32*)SAMPLE_ALLOC(sizeof(PxU32)*nbTris*3);
|
|
PxMemCopy( indices, wfo.mIndices, sizeof(PxU32)*nbTris*3 );
|
|
|
|
PxReal* uvs = (PxReal*)SAMPLE_ALLOC(sizeof(PxReal)*nbVerts*2);
|
|
PxMemCopy( uvs, wfo.mTexCoords, sizeof(PxReal)*nbVerts*2 );
|
|
|
|
PxVec3* normals = (PxVec3*)SAMPLE_ALLOC(sizeof(PxVec3)*nbVerts);
|
|
PxBuildSmoothNormals(
|
|
nbTris,
|
|
nbVerts,
|
|
verts,
|
|
indices,
|
|
NULL,
|
|
normals,
|
|
true
|
|
);
|
|
|
|
outRawMesh.mNbVerts = nbVerts;
|
|
outRawMesh.mVerts = verts;
|
|
outRawMesh.mNbFaces = nbTris;
|
|
outRawMesh.mIndices = indices;
|
|
outRawMesh.mUVs = uvs;
|
|
outRawMesh.mVertexNormals = normals;
|
|
outRawMesh.mMaterialID = inMaterialID;
|
|
outRawMesh.mTransform = inPos;
|
|
|
|
RAWMesh cacheMesh;
|
|
cloneMesh( outRawMesh, cacheMesh );
|
|
mRenderMeshCache.insert( inObjFileName, cacheMesh );
|
|
}
|
|
|
|
return &outRawMesh;
|
|
}
|
|
|
|
void SampleLargeWorld::cloneMesh(const RAWMesh& inSrc, RAWMesh& outDst)
|
|
{
|
|
PxVec3* verts = NULL;
|
|
PxU32* indices = NULL;
|
|
PxReal* uvs = NULL;
|
|
PxVec3* normals = NULL;
|
|
PxVec3* colors = NULL;
|
|
|
|
if( inSrc.mVerts )
|
|
{
|
|
verts = (PxVec3*)SAMPLE_ALLOC(sizeof(PxVec3)*inSrc.mNbVerts);
|
|
PxMemCopy( verts, inSrc.mVerts, sizeof(PxVec3)*inSrc.mNbVerts );
|
|
}
|
|
|
|
if( inSrc.mVertexNormals )
|
|
{
|
|
normals = (PxVec3*)SAMPLE_ALLOC(sizeof(PxVec3)*inSrc.mNbVerts);
|
|
PxMemCopy( normals, inSrc.mVertexNormals, sizeof(PxVec3)*inSrc.mNbVerts );
|
|
}
|
|
|
|
if( inSrc.mVertexColors )
|
|
{
|
|
colors = (PxVec3*)SAMPLE_ALLOC(sizeof(PxVec3)*inSrc.mNbVerts);
|
|
PxMemCopy( colors, inSrc.mVertexColors, sizeof(PxVec3)*inSrc.mNbVerts );
|
|
}
|
|
|
|
if( inSrc.mUVs )
|
|
{
|
|
uvs = (PxReal*)SAMPLE_ALLOC(sizeof(PxReal)*inSrc.mNbVerts*2);
|
|
PxMemCopy( uvs, inSrc.mUVs, sizeof(PxReal)*inSrc.mNbVerts*2 );
|
|
}
|
|
|
|
if( inSrc.mIndices )
|
|
{
|
|
indices = (PxU32*)SAMPLE_ALLOC(sizeof(PxU32)*inSrc.mNbFaces*3);
|
|
PxMemCopy( indices, inSrc.mIndices, sizeof(PxU32)*inSrc.mNbFaces*3 );
|
|
}
|
|
|
|
outDst.mName = inSrc.mName;
|
|
outDst.mTransform = inSrc.mTransform;
|
|
outDst.mNbVerts = inSrc.mNbVerts;
|
|
outDst.mNbFaces = inSrc.mNbFaces;
|
|
outDst.mMaterialID = inSrc.mMaterialID;
|
|
outDst.mVerts = verts;
|
|
outDst.mVertexNormals = normals;
|
|
outDst.mVertexColors = colors;
|
|
outDst.mUVs = uvs;
|
|
outDst.mIndices = indices;
|
|
}
|
|
|
|
void SampleLargeWorld::renderProgressBar( PxF32 ratio, bool needFlush )
|
|
{
|
|
const RendererColor backColor(0, 255, 39);
|
|
|
|
ScreenQuad sq;
|
|
sq.mX0 = 0.0f;
|
|
sq.mY0 = 0.8f;
|
|
sq.mX1 = ratio;
|
|
sq.mY1 = 0.85f;
|
|
sq.mLeftUpColor = backColor;
|
|
sq.mRightUpColor = backColor;
|
|
sq.mLeftDownColor = backColor;
|
|
sq.mRightDownColor = backColor;
|
|
sq.mAlpha = 1.0f;
|
|
|
|
if(needFlush)
|
|
getRenderer()->clearBuffers();
|
|
|
|
getRenderer()->drawScreenQuad(sq);
|
|
|
|
if(needFlush)
|
|
getRenderer()->swapBuffers();
|
|
}
|
|
|
|
void SampleLargeWorld::releaseJoints()
|
|
{
|
|
PxSceneWriteLock scopedLock(*mScene);
|
|
|
|
const size_t nbJoints = mFixedJoints.size();
|
|
|
|
PxRigidActor* actor0 = NULL;
|
|
PxRigidActor* actor1 = NULL;
|
|
|
|
for(PxU32 i=0;i<nbJoints;i++)
|
|
{
|
|
mFixedJoints[i]->getActors(actor0, actor1);
|
|
setCollisionGroup( actor1, DEFAULT_COLLISION_GROUP );
|
|
|
|
mFixedJoints[i]->release();
|
|
}
|
|
mFixedJoints.clear();
|
|
}
|
|
|
|
bool SampleLargeWorld::readyToSyncCCT()
|
|
{
|
|
PxSceneReadLock scopedLock(*mScene);
|
|
PxVec3 pos = toVec3(mActor->getController()->getPosition());
|
|
|
|
PxRigidDynamic* actor = mActor->getController()->getActor();
|
|
PxShape* capsuleShape = getShape( *actor );
|
|
|
|
PxCapsuleGeometry capGeom(capsuleShape->getGeometry().capsule());
|
|
|
|
PxQueryFilterData objType = PxQueryFilterData(PxQueryFlag::eSTATIC);
|
|
PxHitFlags hitFlag = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL;
|
|
PxSweepBuffer hit;
|
|
return getActiveScene().sweep(capGeom, PxTransform(pos), PxVec3(0.0f, -1.0f,0.0f), 1000.f, hit, hitFlag, objType);
|
|
}
|
|
|
|
void SampleLargeWorld::attachNearestObjectsToCCT()
|
|
{
|
|
PxSceneWriteLock scopedLock(*mScene);
|
|
|
|
PxRigidDynamic* actor = mActor->getController()->getActor();
|
|
PxSphereGeometry sg(3.f);
|
|
|
|
PxI32 attachNum = 5;
|
|
PxVec3 footPos = toVec3(mActor->getController()->getFootPosition());
|
|
|
|
PxShape* capsuleShape = getShape( *actor );
|
|
|
|
const PxCapsuleGeometry& capGeom = capsuleShape->getGeometry().capsule();
|
|
|
|
PxF32 boxExtent = getDebugBoxObjectExtents().y + 0.1f;
|
|
|
|
PxOverlapHit hitBuffer[5];
|
|
PxMemZero(&hitBuffer, sizeof(hitBuffer));
|
|
PxFilterData fd(PICKING_COLLISION_GROUP,0,0,0);
|
|
PxOverlapBuffer buf(hitBuffer, 5);
|
|
|
|
getActiveScene().overlap(sg, PxTransform(footPos), buf, PxQueryFilterData(fd, PxQueryFlag::eDYNAMIC));
|
|
PxU32 hitNum = buf.getNbAnyHits();
|
|
if( hitNum == 0 )
|
|
return;
|
|
|
|
attachNum = hitNum == (PxU32)-1 ? attachNum : hitNum;
|
|
|
|
PxTransform lastTransform = PxTransform(actor->getGlobalPose().q);
|
|
PxVec3 hookPos = PxVec3( 0.0f, capGeom.halfHeight + capGeom.radius +boxExtent*2, capGeom.radius*2.0f);
|
|
|
|
for( PxI32 i = 0; i < attachNum; ++i)
|
|
{
|
|
PxRigidDynamic* attachedActor = hitBuffer[i].actor->is<PxRigidDynamic>();
|
|
setCollisionGroup( attachedActor, PICKING_COLLISION_GROUP );
|
|
|
|
PxFixedJoint *joint = PxFixedJointCreate(getPhysics(), actor,lastTransform, attachedActor, PxTransform(hookPos));
|
|
joint->setConstraintFlag( PxConstraintFlag::eCOLLISION_ENABLED, false );
|
|
mFixedJoints.push_back( joint );
|
|
|
|
hookPos.y += (boxExtent*1.5f);
|
|
}
|
|
}
|
|
|
|
PxRigidDynamic* SampleLargeWorld::createBox(const PxVec3& pos, const PxVec3& dims, const PxVec3* linVel, RenderMaterial* material, PxReal density)
|
|
{
|
|
PxSceneWriteLock scopedLock(*mScene);
|
|
|
|
PxRigidDynamic* box = PxCreateDynamic(*mPhysics, PxTransform(pos), PxBoxGeometry(dims), *mMaterial, density);
|
|
PX_ASSERT(box);
|
|
|
|
box->setActorFlag(PxActorFlag::eVISUALIZATION, true);
|
|
box->setAngularDamping(0.5f);
|
|
|
|
//Enable ccd of dynamic generated box
|
|
PxShape* boxShape = getShape(*box);
|
|
setCCDActive(*boxShape);
|
|
|
|
boxShape->setQueryFilterData(PxFilterData(PICKING_COLLISION_GROUP,0,0,0));
|
|
|
|
getActiveScene().addActor(*box);
|
|
|
|
if(linVel)
|
|
box->setLinearVelocity(*linVel);
|
|
|
|
createRenderObjectsFromActor(box, material);
|
|
|
|
return box;
|
|
}
|
|
|
|
void SampleLargeWorld::setCollisionGroup(PxRigidActor* actor, PxU32 group)
|
|
{
|
|
PxSceneWriteLock scopedLock(*mScene);
|
|
|
|
PxU32 nbShapes = actor->getNbShapes();
|
|
if( nbShapes )
|
|
{
|
|
SampleArray<PxShape*> shapes(nbShapes);
|
|
actor->getShapes( &shapes[0], nbShapes);
|
|
for( PxU32 j = 0; j < nbShapes; j++)
|
|
{
|
|
PxFilterData fd = shapes[j]->getSimulationFilterData();
|
|
fd.word0 = group;
|
|
shapes[j]->setSimulationFilterData(fd);
|
|
}
|
|
}
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|