aboutsummaryrefslogtreecommitdiff
path: root/PhysX_3.4/Samples/SampleLargeWorld/ChunkLoader.cpp
diff options
context:
space:
mode:
authorgit perforce import user <a@b>2016-10-25 12:29:14 -0600
committerSheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees>2016-10-25 18:56:37 -0500
commit3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch)
treefa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Samples/SampleLargeWorld/ChunkLoader.cpp
downloadphysx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.tar.xz
physx-3.4-3dfe2108cfab31ba3ee5527e217d0d8e99a51162.zip
Initial commit:
PhysX 3.4.0 Update @ 21294896 APEX 1.4.0 Update @ 21275617 [CL 21300167]
Diffstat (limited to 'PhysX_3.4/Samples/SampleLargeWorld/ChunkLoader.cpp')
-rw-r--r--PhysX_3.4/Samples/SampleLargeWorld/ChunkLoader.cpp1232
1 files changed, 1232 insertions, 0 deletions
diff --git a/PhysX_3.4/Samples/SampleLargeWorld/ChunkLoader.cpp b/PhysX_3.4/Samples/SampleLargeWorld/ChunkLoader.cpp
new file mode 100644
index 00000000..4dcaa544
--- /dev/null
+++ b/PhysX_3.4/Samples/SampleLargeWorld/ChunkLoader.cpp
@@ -0,0 +1,1232 @@
+// This code contains NVIDIA Confidential Information and is disclosed to you
+// under a form of NVIDIA software license agreement provided separately to you.
+//
+// Notice
+// NVIDIA Corporation and its licensors retain all intellectual property and
+// proprietary rights in and to this software and related documentation and
+// any modifications thereto. Any use, reproduction, disclosure, or
+// distribution of this software and related documentation without an express
+// license agreement from NVIDIA Corporation is strictly prohibited.
+//
+// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES
+// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO
+// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT,
+// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE.
+//
+// Information and code furnished is believed to be accurate and reliable.
+// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such
+// information or for any infringement of patents or other rights of third parties that may
+// result from its use. No license is granted by implication or otherwise under any patent
+// or patent rights of NVIDIA Corporation. Details are subject to change without notice.
+// This code supersedes and replaces all information previously supplied.
+// NVIDIA Corporation products are not authorized for use as critical
+// components in life support devices or systems without express written approval of
+// NVIDIA Corporation.
+//
+// Copyright (c) 2008-2016 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 "PxTkRandom.h"
+#include "RenderMeshActor.h"
+#include "SampleDirManager.h"
+#include "ChunkLoader.h"
+#include "SampleLargeWorld.h"
+#include <algorithm>
+#include "MeshBuilder.h"
+#include "PsMathUtils.h"
+#include "PxTkFile.h"
+#include "wavefront.h"
+#include "PsString.h"
+#include "PxScene.h"
+#include "geometry/PxGeometryQuery.h"
+#include "extensions/PxCollectionExt.h"
+
+using namespace PxToolkit;
+using namespace physx;
+using namespace shdfnd;
+
+#define MATERIAL_ID 0x01
+
+#define CHUNK_DEBRIS_NUM 1
+#define ADD_TREES 1
+#define ADD_WIND_MILL 1
+
+static const ChunkID FARM_CHUNKID = {{4, 3}};
+static const ChunkID CITY_CHUNKID = {{4, 2}};
+static const ChunkID FORTRESS_CHUNKID = {{1, 2}};
+static const ChunkID FOREST_CHUNKID = {{5, 3}};
+
+static const char* gFarmName = "Farm";
+static const char* gCityName = "City";
+static const char* gTreeName = "Tree";
+
+static const float gFortrestScale = 0.05f;
+
+static const PxBounds3 gCityBounds( PxVec3(927.65833,17.403145,452.00970), PxVec3(1119.6967,256.83026,576.55121));
+static const PxBounds3 gFarmBounds( PxVec3(1001.8151,9.4852743,732.46741), PxVec3(1036.2266,16.729010,787.07898));
+static const PxBounds3 gFortressBounds( PxVec3(-584.924011,0.000122,-586.000305), PxVec3(584.924011,1875.750488,585.999939));
+
+static const PxVec3 gCityPos(1024.0000,17.414299,512.00000);
+static const PxVec3 gFarmPos(1024.0000, 9.4852762, 768.0f);
+static const PxVec3 gFortressPos(256.00000,81.219383,512.000000);
+
+static PX_INLINE ChunkID getChunkIdFromPosition(const PxVec3& position, CoordType terrainDim, PxF32 terrainSize)
+{
+ CoordType xCoor = (CoordType)((position.x + terrainSize*0.5f) / terrainSize);
+ xCoor = PxClamp((CoordType)xCoor, (CoordType)0, (CoordType)(terrainDim -1));
+
+ CoordType yCoor = (CoordType)((position.z + terrainSize*0.5f) / terrainSize);
+ yCoor = PxClamp((CoordType)yCoor, (CoordType)0, (CoordType)(terrainDim -1));
+
+ ChunkID res;
+ res.coord.x = xCoor;
+ res.coord.y = yCoor;
+
+ return res;
+}
+
+static PxI32 getFileSize(const char* name)
+{
+ if(!name)
+ return 0;
+
+ SampleFramework::File* fp = NULL;
+ PxToolkit::fopen_s(&fp, name, "rb");
+ if( !fp )
+ return 0;
+
+ fseek(fp, 0, SEEK_END);
+ PxI32 filesize = (PxI32)ftell(fp);
+ fclose(fp);
+ return filesize;
+}
+
+void SampleLargeWorld::addWindMills(const PxTriangleMeshGeometry& meshGeom, BasicRandom& random, const PxBounds3& bound, PxCollection& outCollection)
+{
+ PxPhysics& physics = getPhysics();
+ PxMaterial& material = getDefaultMaterial();
+
+ const PxReal millHalfHeight = 20.0f;
+ const PxReal millRadius =9.0f;
+ const PxReal fanRadius = 22.0f;
+ const PxReal fanWidth = 3.0f;
+ const PxReal fanThin = 0.5f;
+ const PxReal axisHalfHeight = 5.0f;
+ const PxReal axisRadius = 1.0f;
+ const PxVec3 millExtent(
+ millRadius*0.5f + axisHalfHeight + axisRadius + fanThin,
+ fanRadius*0.5f + millRadius + millHalfHeight,
+ millRadius*0.5f + axisHalfHeight + axisRadius + fanThin
+ ) ;
+
+ PxBounds3 millBornBound = bound;
+ millBornBound.fattenFast(-millRadius*2.0f);
+
+ //Store mill position to check if new mill is overlaped with old mills
+ PxVec3 millPts[5];
+
+ PxU32 mills = 0, retryNum = 1;
+ do
+ {
+ //Raycast against terrain from random pos to get mill foot point
+ PxVec3 rayStart(
+ random.rand( millBornBound.minimum.x, millBornBound.maximum.x),
+ bound.maximum.y,
+ random.rand( millBornBound.minimum.z, millBornBound.maximum.z));
+
+ PxReal maxDist = 2*(bound.maximum.y - bound.minimum.y);
+
+ PxRaycastHit hit;
+
+ PxU32 hitsNum = PxGeometryQuery::raycast(
+ rayStart, PxVec3(0.0f,-1.0f,0.0f),
+ meshGeom, PxTransform(PxIdentity), maxDist,
+ PxHitFlag::ePOSITION, 1, &hit);
+
+ if( hitsNum )
+ {
+ PxVec3 millPt = hit.position;
+ millPt.y += millHalfHeight - millRadius;
+
+ PxVec3 axisPt = millPt;
+ axisPt.y += millHalfHeight + millRadius;
+ axisPt.x += millRadius;
+
+ PxVec3 fanPt = axisPt;
+ fanPt.x += axisHalfHeight;
+
+ //Roughly check if fans of mill collided with terrain
+ PxQueryFilterData filter( PxQueryFlag::eANY_HIT);
+ PxOverlapBuffer buf;
+ PxBoxGeometry fanBox(fanThin, fanRadius, fanRadius);
+ if(PxGeometryQuery::overlap(fanBox, PxTransform(fanPt), meshGeom, PxTransform(PxIdentity)))
+ {
+ continue;
+ }
+
+
+ PxBounds3 curBounds = PxBounds3::centerExtents( millPt, millExtent);
+
+ bool needRegenerate = false;
+
+ for(PxU32 t = 0; t < mills; t++)
+ {
+ PxBounds3 otherBounds = PxBounds3::centerExtents( millPts[t], millExtent);
+ if( otherBounds.intersects(curBounds) )
+ needRegenerate = true;
+ }
+
+ if(needRegenerate)
+ {
+ continue;
+ }
+
+ millPts[mills] = millPt;
+ millPts[mills].x = millPts[mills].x + millRadius - millExtent.x;
+ ++mills;
+
+ PxCapsuleGeometry millGeom(millRadius, millHalfHeight);
+ PxCapsuleGeometry axisGeom(axisRadius, axisHalfHeight);
+ PxBoxGeometry fanGeom(fanRadius, fanWidth, fanThin);
+
+ PxQuat verticleRote( PxHalfPi, PxVec3(0.0f,0.0f,1.0f));
+ PxQuat y90Rote( PxHalfPi, PxVec3(0.0f,1.0f,0.0f));
+
+ PxRigidStatic* millBody = physics.createRigidStatic(PxTransform(millPt));
+ PxShape* millShape0 = PxRigidActorExt::createExclusiveShape(*millBody, millGeom, material);
+ millShape0->setLocalPose(PxTransform(verticleRote));
+ PxShape* millShape1 = PxRigidActorExt::createExclusiveShape(*millBody, axisGeom, material);
+ millShape1->setLocalPose(PxTransform(axisPt - millPt));
+
+ PxRigidDynamic* fan = physics.createRigidDynamic(PxTransform(fanPt));
+ PxShape* fanShape0 = PxRigidActorExt::createExclusiveShape(*fan, fanGeom, material);
+ fanShape0->setLocalPose(PxTransform(PxIdentity));
+ PxShape* fanShape1 = PxRigidActorExt::createExclusiveShape(*fan, fanGeom, material);
+ fanShape1->setLocalPose(PxTransform(verticleRote));
+
+ //Disable the collision between fans and other objects in scene
+ setCollisionGroup(fan, FAN_COLLISION_GROUP);
+
+ PxRevoluteJoint* joint = PxRevoluteJointCreate(physics,
+ millBody, PxTransform(fanPt - millPt),
+ fan, PxTransform(PxVec3(0), y90Rote));
+
+ joint->setDriveVelocity(10.0f);
+ joint->setRevoluteJointFlag(PxRevoluteJointFlag::eDRIVE_ENABLED, true);
+
+ outCollection.add(*millBody);
+ outCollection.add(*fan);
+ outCollection.add(*joint);
+ }
+
+ }while( mills < retryNum);
+}
+
+void SampleLargeWorld::addTrees(const PxTriangleMeshGeometry& meshGeom, const PxBounds3& bound, PxCollection& outCollection)
+{
+ const char* filename = getSampleMediaFilename("tree.obj");
+ const char* filenameCooked = getSampleOutputFilePath("tree.obj", "");
+
+ PxReal maxDist = 2*(bound.maximum.y - bound.minimum.y);
+ for(PxU32 i = (PxU32)bound.minimum.z; i < (PxU32)bound.maximum.z; i += 80)
+ {
+ PxVec3 rayStart(bound.minimum.x, bound.maximum.y, (PxReal)i);
+
+ PxRaycastBuffer hit;
+ PxQueryFilterData filter(PxQueryFlag::eSTATIC);
+ PxScene& scene = getActiveScene();
+ bool ret = false;
+ {
+ PxSceneReadLock scopedLock(scene);
+ ret = scene.raycast(rayStart, PxVec3(0.0f,-1.0f,0.0f), maxDist, hit, PxHitFlag::ePOSITION, filter);
+ }
+ if( ret )
+ {
+ MeshBuilder::addObjMeshToPxCollection(
+ getPhysics(), getCooking(), getDefaultMaterial(),
+ filename, filenameCooked,
+ PxTransform(hit.block.position, PxQuat(-PxHalfPi, PxVec3(1,0,0))), PxVec3(200, 200, 200),
+ outCollection, gTreeName);
+
+ }
+ }
+
+}
+
+static void flattenChunk(SampleArray<PxVec3>& terrainVertices, const PxBounds3& bound, PxReal dataY)
+{
+ PxBounds3 bound1 = bound;
+ PxReal deltaX = bound1.maximum.x - bound1.minimum.x;
+ PxReal deltaZ = bound1.maximum.z- bound1.minimum.z;
+ PxReal delta = deltaX < deltaZ ? deltaX : deltaZ;
+ bound1.fattenFast(delta/32);
+
+ const PxU32 size = terrainVertices.size();
+
+ for(PxU32 i = 0; i < size; ++i)
+ {
+ if((terrainVertices[i].x <= bound1.maximum.x && terrainVertices[i].x >= bound1.minimum.x)
+ && (terrainVertices[i].z <= bound1.maximum.z && terrainVertices[i].z >= bound1.minimum.z))
+ {
+ terrainVertices[i].y = dataY;
+ }
+ }
+}
+
+void SampleLargeWorld::addCity(SampleArray<PxVec3>& terrainVertices, const PxBounds3& bound, PxCollection& outCollection)
+{
+ const char* filename = getSampleMediaFilename("city.obj");
+ const char* filenameCooked = getSampleOutputFilePath("city.obj", "");
+
+ MeshBuilder::addObjMeshToPxCollection(
+ getPhysics(), getCooking(), getDefaultMaterial(),
+ filename, filenameCooked,
+ PxTransform(gCityPos, PxQuat(-PxHalfPi, PxVec3(1,0,0))), PxVec3(100, 100, 120),
+ outCollection, gCityName);
+}
+
+void SampleLargeWorld::addFarm(SampleArray<PxVec3>& terrainVertices, const PxBounds3& bound, PxCollection& outCollection)
+{
+ const char* filename = getSampleMediaFilename("barn.obj");
+ const char* filenameCooked = getSampleOutputFilePath("barn.obj", "");
+
+ MeshBuilder::addObjMeshToPxCollection(
+ getPhysics(), getCooking(), getDefaultMaterial(),
+ filename, filenameCooked,
+ PxTransform(gFarmPos, PxQuat(PxPi, PxVec3(1,0,0))), PxVec3(100, 100, 100),
+ outCollection, gFarmName);
+
+}
+
+
+void SampleLargeWorld::addFortress(SampleArray<PxVec3>& terrainVertices, const PxBounds3& bound, PxCollection& outCollection)
+{
+ const char* pathname = getSampleMediaFilename("WallTower.repx");
+
+ MeshBuilder* mb = SAMPLE_NEW(MeshBuilder)(*this, pathname );
+ const PxQuat q = PxShortestRotation(PxVec3(0.0f, 0.0f, 1.0f), PxVec3(0.0f, 1.0f, 0.0f));
+
+ mb->addRepXToPxCollection( PxTransform(gFortressPos, q), PxVec3(0.05f, 0.05f, 0.05f), outCollection);
+
+ DELETESINGLE( mb );
+}
+
+PxTriangleMesh* SampleLargeWorld::generateTriMesh(const SampleArray<PxVec3>* vertices, const SampleArray<PxU32>* indices)
+{
+ //Cooking mesh
+ PxTriangleMeshDesc meshDesc;
+ meshDesc.points.count = vertices->size();
+ meshDesc.triangles.count = indices->size()/3;
+ meshDesc.points.stride = sizeof(PxVec3);
+ meshDesc.triangles.stride = sizeof(PxU32)*3;
+ meshDesc.points.data = &(*vertices)[0];
+ meshDesc.triangles.data = &(*indices)[0];
+ meshDesc.flags = PxMeshFlags(0);
+
+ PxDefaultMemoryOutputStream stream;
+ bool ok = getCooking().cookTriangleMesh(meshDesc, stream);
+ if( !ok )
+ return NULL;
+
+ PxDefaultMemoryInputData rb(stream.getData(), stream.getSize());
+
+ return getPhysics().createTriangleMesh(rb);
+}
+
+void SampleLargeWorld::createChunk(CoordType coordX, CoordType coordY, PxCollection& outCollection)
+{
+ CoordType dim = binData.mDim;
+
+ SampleArray<SampleArray<PxVec3> >& terrainVertices = binData.mTerrainVertices;
+
+ SampleArray<SampleArray<PxU32> >& terrainIndices = binData.mTerrainIndices;
+
+ SampleArray<PxVec3> tmpV;
+ SampleArray<PxU32> tmpI;
+
+ SampleArray<PxVec3>* vertices;
+ SampleArray<PxU32>* indices;
+ PxVec3 xAxis( (coordX / dim ) * CHUNK_WIDTH * dim - 0.5f * CHUNK_WIDTH, 0.0f, 0.0f );
+ PxVec3 zAxis( 0.0f, 0.0f, ( coordY / dim ) * CHUNK_WIDTH * dim - 0.5f * CHUNK_WIDTH );
+ CoordType x = coordX %( dim << 1 ), z = coordY % ( dim <<1 );
+ PxVec3 xRevert(1.0f), zRevert(1.0f);
+
+ bool needRevertMesh = false;
+ bool useRawDirectly = true;
+
+ //The terrain template is 16*16, if chunk is out of the range, use mirror to create new one
+ if(coordX >= dim || coordY >= dim)
+ useRawDirectly = false;
+
+ if( useRawDirectly )
+ {
+ vertices = &terrainVertices[coordY * dim + coordX];
+ indices = &terrainIndices[coordY * dim + coordX];
+ }
+ else //Just use mirror to create new terrain, so terrain can be expand to infinite size
+ {
+ if( x >= dim )
+ {
+ x = x % dim;
+ xRevert.x = -1.0f;
+ needRevertMesh = !needRevertMesh;
+ x = dim - x - 1;
+ }
+
+ if( z >= dim )
+ {
+ z = z % dim;
+ zRevert.z = -1.0f;
+ needRevertMesh = !needRevertMesh;
+ z = dim - z - 1;
+ }
+
+ SampleArray<PxVec3>& templateV = terrainVertices[z*dim + x];
+
+ for( PxU32 v = 0; v < templateV.size(); v++)
+ {
+ PxVec3 vert = templateV[v];
+ if(xRevert.x < 0.0f)
+ {
+ vert.x = CHUNK_WIDTH * dim - 0.5f * CHUNK_WIDTH - templateV[v].x + xAxis.x;
+ }
+ else
+ {
+ vert.x = templateV[v].x - -0.5f * CHUNK_WIDTH + xAxis.x;
+ }
+
+ if(zRevert.z < 0.0f)
+ {
+ vert.z = CHUNK_WIDTH * dim - 0.5f * CHUNK_WIDTH - templateV[v].z + zAxis.z;
+ }
+ else
+ {
+ vert.z = templateV[v].z - -0.5f * CHUNK_WIDTH + zAxis.z;
+ }
+
+ tmpV.pushBack(vert);
+ }
+
+ vertices = &tmpV;
+
+ if(needRevertMesh)
+ {
+ SampleArray<PxU32>& templateI = terrainIndices[z*dim + x];
+
+ for(PxU32 d = 0; d < templateI.size(); d += 3)
+ {
+ tmpI.pushBack(templateI[d]);
+ tmpI.pushBack(templateI[d+2]);
+ tmpI.pushBack(templateI[d+1]);
+
+ }
+
+ indices = &tmpI;
+ }
+ else
+ indices = &terrainIndices[z*dim + x];
+
+ }
+
+ //Both pose and scale are identity, so local bounds == global bounds
+ PxBounds3 bound = PxBounds3::empty();
+
+ for( SampleArray<PxVec3>::Iterator iter = vertices->begin();
+ iter != vertices->end(); iter++ )
+ {
+ bound.include( *iter );
+ }
+
+ ChunkID id;
+ id.coord.x = coordX;
+ id.coord.y = coordY;
+ BasicRandom random(id.id);
+
+ PxBounds3 objBounds[3];
+ PxF32 flatHeight[3];
+ const float flattenScale = 1.1f;
+
+ objBounds[0] = gCityBounds;
+ objBounds[1] = gFarmBounds;
+ objBounds[1].fattenFast(1.5f);
+
+ PxBounds3 fortressScaledBound( gFortressBounds.minimum * gFortrestScale, gFortressBounds.maximum * gFortrestScale );
+ objBounds[2] = PxBounds3::centerExtents( gFortressPos + fortressScaledBound.getCenter(), fortressScaledBound.getExtents() * flattenScale);
+
+ flatHeight[0] = gCityPos.y;
+ flatHeight[1] = gFarmPos.y;
+ flatHeight[2] = gFortressPos.y;
+
+ for(PxU32 i = 0; i < 3; i++)
+ {
+ if(bound.intersects(objBounds[i]))
+ {
+ flattenChunk(*vertices, objBounds[i], flatHeight[i]);
+ }
+ }
+
+ bool isEmpty = false;
+ if(id.id == FARM_CHUNKID.id)
+ {
+ addFarm(*vertices, bound, outCollection);
+ }
+ else if(id.id == CITY_CHUNKID.id)
+ {
+ addCity(*vertices, bound, outCollection);
+ }
+ else if(id.id == FORTRESS_CHUNKID.id)
+ {
+ addFortress(*vertices, bound, outCollection);
+ }
+ else
+ {
+ isEmpty = true;
+ }
+
+ PxTriangleMesh* triangleMesh = generateTriMesh(vertices, indices);
+ if( !triangleMesh )
+ return;
+
+ PxTriangleMeshGeometry meshGeom(triangleMesh);
+ PxMaterial& material = getDefaultMaterial();
+ //Create terrain actor
+ PxRigidStatic* actor = PxCreateStatic(getPhysics(), PxTransform(PxIdentity), meshGeom, material);
+
+ //Enable ccd for terrain
+ PxShape* meshShape = getShape(*actor);
+ SampleLargeWorld::setCCDActive(*meshShape);
+ meshShape->setFlag(PxShapeFlag::eVISUALIZATION, false);
+
+ if(id.id == FOREST_CHUNKID.id)
+ {
+ addTrees(meshGeom, bound, outCollection);
+ isEmpty = false;
+ }
+
+ if(isEmpty)
+ {
+#if ADD_WIND_MILL
+ addWindMills(meshGeom, random, bound, outCollection);
+#endif
+ }
+
+ outCollection.add(*actor);
+ return;
+}
+
+BackgroundLoader::BackgroundLoader(SampleLargeWorld& sample, CoordType halfRange, CoordType terrainRange, PxF32 chunkWidth)
+ : mSampleLargeWorld(&sample)
+ , mPhysics(sample.getPhysics())
+ , mScene(sample.getActiveScene())
+ , mMaterial(sample.getDefaultMaterial())
+ , mHalfRange(halfRange)
+ , mTerrainRange(terrainRange)
+ , mChunkWidth(chunkWidth)
+{
+ mCurChunkId.id = 0;
+ mDiskIOTime = 0;
+ mPhyXStreamTime = 0;
+ mGraphicStreamTime = 0;
+
+ mLoaderThread = PX_NEW(Thread)(loaderThread, this);
+ mLoaderThread->start(0);
+ mSr = PxSerialization::createSerializationRegistry(mPhysics);
+}
+
+BackgroundLoader::~BackgroundLoader()
+{
+ mLoaderThread->signalQuit();
+ mRequestReady.set();
+ mLoaderThread->waitForQuit();
+ delete mLoaderThread;
+
+ deleteLoadedRenderQueue();
+
+ deleteCollections();
+ mSr->release();
+}
+
+void BackgroundLoader::onTick()
+{
+ //Use local queue here to minimize the lock time, prevent blocking loader thread.
+ std::vector<ChunkCommand> theCmdQueue;
+ std::vector<DeferredLoadedRenderData> theLoadedRenderDataQueue;
+
+ mQueueLock.lock();
+ theCmdQueue = mChunkCmds;
+ theLoadedRenderDataQueue = mLoadedRenderDataQueue;
+ mLoadedRenderDataQueue.clear();
+ mChunkCmds.clear();
+ mQueueLock.unlock();
+
+ size_t cmdQueueSize = theCmdQueue.size();
+ if(cmdQueueSize == 0)
+ return;
+
+ //filter the same chunk with different commands
+ for(PxU32 i = 0; i < cmdQueueSize; ++i)
+ {
+ ChunkCommand cmdQueue = theCmdQueue[i];
+
+ bool isOutdatedCommand = false;
+ for(PxU32 j = i + 1; j < cmdQueueSize && !isOutdatedCommand; ++j)
+ {
+ isOutdatedCommand = (cmdQueue.id == theCmdQueue[j].id);
+ }
+
+ if(isOutdatedCommand)
+ continue;
+
+ ChunkCommandType::Enum type = cmdQueue.type;
+ if(type == ChunkCommandType::eAdd)
+ {
+ addReadyChunkToScene(cmdQueue.id);
+ }
+ else if(type == ChunkCommandType::eRemove)
+ {
+ destroyChunk(cmdQueue.id);
+ }
+ }
+
+ Ps::Time localTimer;
+ for(PxU32 i = 0; i < theLoadedRenderDataQueue.size(); i++)
+ {
+ DeferredLoadedRenderData& cq = theLoadedRenderDataQueue[i];
+ RenderBaseActor* renderMesh = mSampleLargeWorld->createRenderMeshFromRawMesh( cq);
+
+ //Link physic actor to render actor
+ cq.shape->userData = renderMesh;
+
+ {
+ PxSceneReadLock scopedLock(mSampleLargeWorld->getActiveScene());
+ renderMesh->setPhysicsShape(cq.shape, cq.shape->getActor());
+ renderMesh->setEnableCameraCull(true);
+
+ PxTriangleMeshGeometry geometry;
+ cq.shape->getTriangleMeshGeometry(geometry);
+ renderMesh->setMeshScale(geometry.scale);
+ }
+ PxVec3 *verts = const_cast<PxVec3*>(cq.mVerts);
+ SAMPLE_FREE(verts);
+
+ PxU32 *indices = const_cast<PxU32*>(cq.mIndices);
+ SAMPLE_FREE(indices);
+
+ PxReal *normals = (PxReal*)const_cast<PxVec3*>(cq.mVertexNormals);
+ SAMPLE_FREE(normals);
+
+ PxReal* uvs = const_cast<PxReal*>(cq.mUVs);
+ SAMPLE_FREE(uvs);
+ }
+
+ Ps::Time::Second timeCost = localTimer.getElapsedSeconds();
+
+ mLoaderStatusLock.lockWriter();
+ mGraphicStreamTime += timeCost;
+ mLoaderStatusLock.unlockWriter();
+
+ {
+ PxSceneWriteLock scopedLock(mSampleLargeWorld->getActiveScene());
+ for(PxU32 i = 0; i < mRemovingActors.size(); i++)
+ {
+ mRemovingActors[i]->release();
+ }
+ }
+
+ mRemovingActors.clear();
+}
+
+void BackgroundLoader::updateChunk(const PxVec3& cameraPos)
+{
+ ChunkID id = getChunkIdFromPosition(cameraPos, mTerrainRange, mChunkWidth);
+ if( id.id == mCurChunkId.id)
+ return;
+
+ mLocalBounds = PxBounds3::centerExtents( PxVec3(id.coord.x*mChunkWidth, 0.0f, id.coord.y*mChunkWidth), PxVec3(mChunkWidth));
+ updateDynamicChunkId();
+
+ mCurChunkId = id;
+ CoordType x = PxClamp(id.coord.x, (CoordType)mHalfRange, (CoordType)(mTerrainRange - mHalfRange));
+ CoordType y = PxClamp(id.coord.y, (CoordType)mHalfRange, (CoordType)(mTerrainRange - mHalfRange));
+
+ std::vector<ChunkID> requestQueue;
+ for( CoordType i = PxMax( (CoordType)0, (CoordType)(x - mHalfRange) ); i <= PxMin( (CoordType)N2, (CoordType)(x + mHalfRange)); i++ )
+ {
+ for( CoordType j = PxMax( (CoordType)0, (CoordType)(y - mHalfRange)); j <= PxMin( (CoordType)N2, (CoordType)(y + mHalfRange)); j++ )
+ {
+ ChunkID id;
+ id.coord.x = i;
+ id.coord.y = j;
+ requestQueue.push_back(id);
+ }
+ }
+
+ mQueueLock.lock();
+ mRequestQueue = requestQueue;
+ mQueueLock.unlock();
+ mRequestReady.set();
+}
+
+static const char* getPlatformName()
+{
+#if PX_X86
+ return "PC32";
+#elif PX_X64
+ return "PC64";
+#elif PX_ARM_FAMILY
+ return "ARM";
+#else
+ return "";
+#endif
+}
+
+const char* BackgroundLoader::getPathname(ChunkID id)
+{
+ char tmpFilename[256];
+ sprintf(tmpFilename, "chunk_%d_%d_%s.bin", id.coord.x, id.coord.y, getPlatformName());
+
+ return mSampleLargeWorld->getSampleOutputFilePath(tmpFilename,"");
+}
+
+void BackgroundLoader::serialize(PxCollection* collection, ChunkID id)
+{
+ const char* theBinPath = getPathname( id );
+ SampleFramework::File* fp = NULL;
+ PxToolkit::fopen_s(&fp, theBinPath, "rb");
+ if( fp )
+ {
+ fclose(fp);
+ return;
+ }
+
+ PxCollection* theExtRef = PxCreateCollection();
+ theExtRef->add(mMaterial, MATERIAL_ID);
+ PxDefaultMemoryOutputStream memBuf;
+
+ PxSerialization::complete(*collection, *mSr, theExtRef);
+ bool bRet = PxSerialization::serializeCollectionToBinary(memBuf, *collection, *mSr, theExtRef, true );
+ PX_ASSERT(bRet);
+ PX_UNUSED(bRet);
+
+ PxDefaultFileOutputStream s( getPathname( id ) );
+
+ Ps::Time localTimer;
+ localTimer.getElapsedSeconds();
+ s.write( memBuf.getData(), memBuf.getSize());
+ mDiskIOTimeCounter += localTimer.getElapsedSeconds();
+
+ theExtRef->release();
+}
+
+//PhysX support buffer inserting actor, so no care about whether physic is simulating here
+void BackgroundLoader::addReadyChunkToScene(ChunkID id)
+{
+ PxCollection *theCollection = NULL;
+ {
+ shdfnd::Mutex::ScopedLock al( mCollectionLock );
+ CollectionIdMap::iterator it = mCollectionIdMap.find(id);
+ if(it == mCollectionIdMap.end() || it->second.addToScene )
+ {
+ return;
+ }
+
+ theCollection = it->second.collection;
+ if( !theCollection )
+ return;
+
+ it->second.addToScene = true;
+ }
+
+ PxU32 count = theCollection->getNbObjects();
+ for(PxU32 i = 0; i < count; i++)
+ {
+ PxBase *object = &theCollection->getObject(i);
+ PX_ASSERT(object);
+ PxType theType = object->getConcreteType();
+ if( theType == PxConcreteType::eRIGID_DYNAMIC )
+ {
+ PxRigidActor *actor = static_cast<PxRigidActor*>(object);
+ mSampleLargeWorld->createRenderObjectsFromActor(actor);
+ }
+ else if(theType == PxConcreteType::eRIGID_STATIC)
+ {
+ PxRigidStatic* actor = static_cast<PxRigidStatic*>(object);
+ PxShape* shape = getShape( *actor );
+ PxTriangleMeshGeometry geometry;
+ shape->getTriangleMeshGeometry(geometry);
+ //We create static render mesh in another loop
+ if(shape->getGeometryType() != PxGeometryType::eTRIANGLEMESH)
+ {
+ mSampleLargeWorld->createRenderObjectsFromActor(actor, NULL);
+ }
+ }
+ }
+
+ {
+ PxSceneWriteLock scopedLock(mSampleLargeWorld->getActiveScene());
+ mScene.addCollection(*theCollection);
+ }
+
+ //Melt the freezon important actors on this chunk
+ for(unsigned i = 0; i < mDyncActors.size(); ++i)
+ {
+ DynamicObjects* obj = mDyncActors[i];
+ if( obj->id.id == id.id && obj->isImportant )
+ {
+ PxSceneWriteLock scopedLock(mSampleLargeWorld->getActiveScene());
+ obj->actor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, false);
+ }
+ }
+}
+
+//just removed the actors from scene, do not release them
+void BackgroundLoader::destroyChunk(ChunkID id)
+{
+ PxCollection *theCollection = NULL;
+ bool inScene = false;
+ void* memory = NULL;
+ {
+ shdfnd::Mutex::ScopedLock al(mCollectionLock);
+
+ CollectionIdMap::iterator it = mCollectionIdMap.find(id);
+ if( it == mCollectionIdMap.end() )
+ return;
+
+ inScene = it->second.addToScene;
+ theCollection = it->second.collection;
+ memory = it->second.memory;
+ mCollectionIdMap.erase(it);
+ }
+
+
+ if(inScene)
+ {
+ //remove objects from scene first
+ PxU32 count = theCollection->getNbObjects();
+ {
+ PxSceneWriteLock scopedLock(mSampleLargeWorld->getActiveScene());
+ for(PxU32 i = 0; i < count; i++)
+ {
+ PxBase *object = &theCollection->getObject(i);
+ PX_ASSERT(object);
+ PxType type = object->getConcreteType();
+ if( type == PxConcreteType::eRIGID_STATIC || type == PxConcreteType::eRIGID_DYNAMIC )
+ {
+ mScene.removeActor( *static_cast<PxActor*>(object) );
+ }
+ }
+ }
+ }
+
+ //serialize the objects when they are not used
+ serialize(theCollection, id);
+ PxCollectionExt::releaseObjects(*theCollection);
+ theCollection->release();
+
+ if(memory)
+ {
+ free(memory);
+ memory = NULL;
+ }
+
+ //Remove the un-important actors on this chunk, freeze the important actors
+ std::vector<DynamicObjects*>::iterator it = mDyncActors.begin();
+ while(it != mDyncActors.end())
+ {
+ PxRigidDynamic* actor = (*it)->actor;
+ PxVec3 actorPos;
+ {
+ PxSceneReadLock scopedLock(mSampleLargeWorld->getActiveScene());
+ actorPos = actor->getGlobalPose().p;
+ }
+
+ ChunkID theId = getChunkIdFromPosition(actorPos, mTerrainRange, mChunkWidth);
+ if( theId.id == id.id )
+ {
+ PxSceneWriteLock scopedLock(mSampleLargeWorld->getActiveScene());
+ if( !(*it)->isImportant )
+ {
+ mSampleLargeWorld->removeActor(actor);
+ mDyncActors.erase(it);
+ it = mDyncActors.begin();
+ continue;
+ }
+ else
+ {
+ actor->setRigidBodyFlag(PxRigidBodyFlag::eKINEMATIC, true);
+ }
+ }
+ ++it;
+ }
+}
+
+static bool isActorNULL(DynamicObjects* o)
+{
+ return o->actor == NULL;
+}
+
+void BackgroundLoader::updateDynamicChunkId()
+{
+ for(unsigned i = 0; i < mDyncActors.size(); ++i)
+ {
+ DynamicObjects* obj = mDyncActors[i];
+ PxRigidDynamic* actor = mDyncActors[i] ->actor;
+ PxVec3 pos;
+ {
+ PxSceneReadLock scopedLock(mSampleLargeWorld->getActiveScene());
+ pos = actor->getGlobalPose().p;
+ }
+
+ //Remove the un-important which go out of current grid.
+ if(obj->isImportant == false && !mLocalBounds.contains(pos))
+ {
+ mRemovingActors.push_back(actor);
+ obj->actor = NULL;
+ }
+ else
+ {
+ ChunkID theId = getChunkIdFromPosition(pos, mTerrainRange, mChunkWidth);
+ obj->id.id = theId.id;
+ }
+ }
+
+ std::vector<DynamicObjects*>::iterator new_end = std::remove_if( mDyncActors.begin(), mDyncActors.end(), isActorNULL);
+ mDyncActors.erase( new_end, mDyncActors.end());
+}
+
+PxCollection* BackgroundLoader::loadChunk(ChunkID id)
+{
+ {
+ shdfnd::Mutex::ScopedLock al(mCollectionLock);
+ if( mCollectionIdMap.find(id) != mCollectionIdMap.end() )
+ return NULL;
+ }
+
+ const char* theBinPath = getPathname( id );
+
+ PxCollection* theCollection = NULL;
+ void *theMemory = NULL;
+
+ Ps::Time localTimer;
+
+ SampleFramework::File* fp = NULL;
+ PxToolkit::fopen_s(&fp, theBinPath, "rb");
+ //If cannot find/deserilize the chunk file, create one collection
+ if( !fp )
+ {
+ theCollection = PxCreateCollection();
+ mSampleLargeWorld->createChunk( id.coord.x, id.coord.y, *theCollection );
+ }
+ else
+ {
+ localTimer.getElapsedSeconds();
+ PxU32 fileSize = getFileSize(theBinPath);
+ theMemory = malloc(fileSize + PX_SERIAL_FILE_ALIGN);
+
+ void* theMemoryA = (void*)((size_t(theMemory) + PX_SERIAL_FILE_ALIGN)&~(PX_SERIAL_FILE_ALIGN-1));
+ size_t numRead = fread(theMemoryA, 1, fileSize, fp);
+ fclose(fp);
+
+ mDiskIOTimeCounter += localTimer.getElapsedSeconds();
+ if(numRead != fileSize)
+ {
+ free(theMemory);
+ theMemory = NULL;
+ return NULL;
+ }
+
+ PxCollection* theExtRef = PxCreateCollection();
+ theExtRef->add(mMaterial, MATERIAL_ID);
+
+ theCollection = PxSerialization::createCollectionFromBinary(theMemoryA, *mSr, theExtRef);
+
+ theExtRef->release();
+
+ mPhyXStreamTimeCounter += localTimer.getElapsedSeconds();
+
+ }
+
+ CollectionMemory temp;
+ temp.collection = theCollection;
+ temp.memory = theMemory;
+ temp.addToScene = false;
+
+ {
+ shdfnd::Mutex::ScopedLock al(mCollectionLock);
+ mCollectionIdMap[id] = temp;
+ }
+
+ return theCollection;
+}
+
+
+void BackgroundLoader::deleteLoadedRenderQueue()
+{
+ std::vector<DeferredLoadedRenderData> theLoadedRenderDataQueue;
+
+ mQueueLock.lock();
+ theLoadedRenderDataQueue = mLoadedRenderDataQueue;
+ mQueueLock.unlock();
+
+ for(PxU32 i = 0; i < theLoadedRenderDataQueue.size(); i++)
+ {
+ DeferredLoadedRenderData& cq = theLoadedRenderDataQueue[i];
+
+ PxVec3 *verts = const_cast<PxVec3*>(cq.mVerts);
+ SAMPLE_FREE(verts);
+
+ PxU32 *indices = const_cast<PxU32*>(cq.mIndices);
+ SAMPLE_FREE(indices);
+
+ PxReal *normals = (PxReal*)const_cast<PxVec3*>(cq.mVertexNormals);
+ SAMPLE_FREE(normals);
+
+ PxReal* uvs = const_cast<PxReal*>(cq.mUVs);
+ SAMPLE_FREE(uvs);
+ }
+}
+
+void BackgroundLoader::deleteCollections()
+{
+ CollectionIdMap::iterator it = mCollectionIdMap.begin();
+ while(it!= mCollectionIdMap.end())
+ {
+ destroyChunk(it->first);
+ it = mCollectionIdMap.begin();
+ }
+}
+
+BackgroundLoader::DeferredLoadedRenderData* BackgroundLoader::createRawMeshFromObjMesh(const char* name, const PxTransform& pos, DeferredLoadedRenderData& rawMesh)
+{
+ PxU32 materialID = 0;
+ const char* filename = NULL;
+ if(Ps::stricmp(name, gCityName) == 0)
+ {
+ filename = "city.obj";
+ materialID = MATERIAL_BUILDING;
+ }
+ else if(Ps::stricmp(name, gFarmName) == 0)
+ {
+ filename = "barn.obj";
+ materialID = MATERIAL_FARM;
+ }
+ else if(Ps::stricmp(name, gTreeName) == 0)
+ {
+ filename = "tree.obj";
+ materialID = MATERIAL_TREE;
+ }
+
+ if( mSampleLargeWorld->createRAWMeshFromObjMesh( filename, pos, materialID, rawMesh ) != NULL )
+ return &rawMesh;
+ else
+ return NULL;
+}
+
+void* BackgroundLoader::loaderThread()
+{
+ std::vector<ChunkID> lastUpdateQueue;
+
+#if ENABLE_PROGRESS_BAR
+ mLoaderStatusLock.lockWriter();
+ mPhyXStreamTime = 0.0f;
+ mGraphicStreamTime = 0.0f;
+ mLoaderStatusLock.unlockWriter();
+#endif
+ while(mRequestReady.wait())
+ {
+ physx::shdfnd::Thread::yield();
+
+ mRequestReady.reset();
+
+ if (mLoaderThread->quitIsSignalled())
+ {
+ mLoaderThread->quit();
+ break;
+ }
+
+#if ENABLE_PROGRESS_BAR
+ mLoaderStatusLock.lockWriter();
+ mQueryLength = 0;
+ mQueryProgress = 0.0f;
+ mLoaderStatusLock.unlockWriter();
+#endif
+
+ bool bDirty = false;
+
+ std::vector<ChunkID> requestedQueue;
+ std::vector<DeferredLoadedRenderData> loadedRenderDataQueue;
+ std::vector<ChunkCommand> theChunkCmds;
+
+ mQueueLock.lock();
+ requestedQueue = mRequestQueue;
+ mQueueLock.unlock();
+
+ for(unsigned iLast = 0; iLast < lastUpdateQueue.size(); iLast++)
+ {
+ bool bFound = false;
+ for(unsigned iRequest = 0; iRequest < requestedQueue.size(); iRequest++)
+ {
+ bFound = lastUpdateQueue[iLast].id == requestedQueue[iRequest].id;
+ if(bFound)
+ break;
+ }
+ if(!bFound)
+ {
+ bDirty = true;
+ theChunkCmds.push_back(ChunkCommand(ChunkCommandType::eRemove,lastUpdateQueue[iLast]));
+ }
+ }
+
+ for(unsigned iRequest = 0; iRequest < requestedQueue.size(); iRequest++)
+ {
+ bool bFound = false;
+ for(unsigned iLast = 0; iLast < lastUpdateQueue.size(); iLast++)
+ {
+ bFound = requestedQueue[iRequest].id == lastUpdateQueue[iLast].id;
+ if(bFound)
+ break;
+ }
+ if(!bFound)
+ {
+ bDirty = true;
+ theChunkCmds.push_back(ChunkCommand(ChunkCommandType::eAdd,requestedQueue[iRequest]));
+ }
+ }
+
+ if(bDirty)
+ {
+ lastUpdateQueue = requestedQueue;
+ }
+ else
+ continue;
+
+ Ps::Time time;
+ Ps::Time::Second normalTime = 0.0f;
+ mDiskIOTimeCounter = 0.0f;
+ mPhyXStreamTimeCounter = 0.0f;
+ mGraphicStreamTimeCounter = 0.0f;
+
+ PxU32 loadedQueueSize = 0;
+ PxU32 cmdQueueSize = static_cast<PxU32>(theChunkCmds.size());
+ for(unsigned i = 0; i < cmdQueueSize; i++)
+ {
+ if(theChunkCmds[i].type == ChunkCommandType::eRemove )
+ {
+ continue;
+ }
+
+ ++loadedQueueSize;
+
+ PxCollection* theCollection = loadChunk(theChunkCmds[i].id);
+
+ time.getElapsedSeconds();
+
+ if(theCollection != NULL )
+ {
+ PxU32 nb = theCollection->getNbObjects();
+ for(PxU32 o = 0; o < nb; o++)
+ {
+ PxBase* s = &theCollection->getObject( o );
+ const PxType serialType = s->getConcreteType();
+
+ if( serialType==PxConcreteType::eRIGID_STATIC )
+ {
+ PxRigidStatic* actor = static_cast<PxRigidStatic*>(s);
+ const char* name = actor->getName();
+ //If actor has name, it should have only one shape
+ if(name)
+ {
+ PxShape* meshShape = NULL;
+ PxTriangleMeshGeometry geometry;
+ PxTransform actorPos;
+ {
+ meshShape = getShape(*actor);
+ meshShape->getTriangleMeshGeometry(geometry);
+ actorPos = actor->getGlobalPose();
+ }
+
+ DeferredLoadedRenderData rawMesh;
+ if( createRawMeshFromObjMesh(name, actorPos, rawMesh) != NULL )
+ {
+ rawMesh.shape = meshShape;
+ loadedRenderDataQueue.push_back(rawMesh);
+ }
+ }
+ else
+ {
+ const PxU32 numShapes = actor->getNbShapes();
+ PxShape** shapes = (PxShape**)SAMPLE_ALLOC(sizeof(PxShape*)*numShapes);
+ actor->getShapes(shapes, numShapes);
+ for(PxU32 i = 0; i < numShapes; i++)
+ {
+ PxShape* shape = shapes[i];
+ if(shape->getGeometryType() == PxGeometryType::eTRIANGLEMESH)
+ {
+ PxTriangleMeshGeometry mesh;
+ shape->getTriangleMeshGeometry(mesh);
+ DeferredLoadedRenderData rawMesh;
+ if( mSampleLargeWorld->createRawMeshFromMeshGeom(mesh, rawMesh) != NULL )
+ {
+ rawMesh.mTransform = actor->getGlobalPose();
+ rawMesh.shape = shape;
+ loadedRenderDataQueue.push_back(rawMesh);
+ }
+ }
+ }
+ SAMPLE_FREE(shapes);
+ }
+ }
+ }
+ physx::shdfnd::Thread::yield();
+ }
+
+ normalTime += time.getElapsedSeconds();
+
+#if ENABLE_PROGRESS_BAR
+ mLoaderStatusLock.lockWriter();
+ mQueryLength++;
+ PX_ASSERT(cmdQueueSize != 0);
+ mQueryProgress = (PxF32)i/(PxF32)cmdQueueSize;
+ mLoaderStatusLock.unlockWriter();
+#endif
+ if (mLoaderThread->quitIsSignalled())
+ {
+ break;
+ }
+
+ physx::shdfnd::Thread::yield();
+ }
+
+ mGraphicStreamTimeCounter += normalTime;
+ mQueueLock.lock();
+ mChunkCmds = theChunkCmds;
+ mLoadedRenderDataQueue = loadedRenderDataQueue;
+ mQueueLock.unlock();
+
+ if (mLoaderThread->quitIsSignalled())
+ {
+ mLoaderThread->quit();
+ break;
+ }
+#if ENABLE_PROGRESS_BAR
+ mLoaderStatusLock.lockWriter();
+ mQueryLength=0;
+ mQueryProgress = 0.0f;
+ mLoaderStatusLock.unlockWriter();
+#endif
+
+ mLoaderStatusLock.lockWriter();
+ mDiskIOTime = mDiskIOTimeCounter;;
+ mPhyXStreamTime = mPhyXStreamTimeCounter;
+ mGraphicStreamTime = mGraphicStreamTimeCounter;
+ mLoaderStatusLock.unlockWriter();
+ }
+
+ return NULL;
+}
+
+void* BackgroundLoader::loaderThread(void* loader)
+{
+ return ((BackgroundLoader*) loader)->loaderThread();
+}