diff options
| author | git perforce import user <a@b> | 2016-10-25 12:29:14 -0600 |
|---|---|---|
| committer | Sheikh Dawood Abdul Ajees <Sheikh Dawood Abdul Ajees> | 2016-10-25 18:56:37 -0500 |
| commit | 3dfe2108cfab31ba3ee5527e217d0d8e99a51162 (patch) | |
| tree | fa6485c169e50d7415a651bf838f5bcd0fd3bfbd /PhysX_3.4/Samples/SampleLargeWorld/ChunkLoader.cpp | |
| download | physx-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.cpp | 1232 |
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(); +} |