예제 #1
0
PX_FORCE_INLINE void RTreePage::growChildBounds(PxU32 index, const RTreeNodeQ& child)
{
	PX_ASSERT(index < RTreePage::SIZE);
	minx[index] = PxMin(minx[index], child.minx);
	miny[index] = PxMin(miny[index], child.miny);
	minz[index] = PxMin(minz[index], child.minz);
	maxx[index] = PxMax(maxx[index], child.maxx);
	maxy[index] = PxMax(maxy[index], child.maxy);
	maxz[index] = PxMax(maxz[index], child.maxz);
}
예제 #2
0
PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreePage& page, int nodeIndex)
{
	PX_ASSERT(nodeIndex < RTreePage::SIZE);
	minx = PxMin(minx, page.minx[nodeIndex]);
	miny = PxMin(miny, page.miny[nodeIndex]);
	minz = PxMin(minz, page.minz[nodeIndex]);
	maxx = PxMax(maxx, page.maxx[nodeIndex]);
	maxy = PxMax(maxy, page.maxy[nodeIndex]);
	maxz = PxMax(maxz, page.maxz[nodeIndex]);
}
예제 #3
0
//process value in range(-1,1)
PX_FORCE_INLINE PxF32 processAnalogValue
(const PxF32 riseRate, const PxF32 fallRate,  
 const PxF32 currentVal, const PxF32 targetVal,
 const PxF32 timestep)
{
	PX_ASSERT(PxAbs(targetVal)<=1.01f);

	PxF32 val=0.0f;	// PT: the following code could leave that variable uninitialized!!!!!
	if(0==targetVal)
	{
		//Drift slowly back to zero 
		if(currentVal>0)
		{
			val=currentVal-fallRate*timestep;
			val=PxMax(val,0.0f);
		}
		else if(currentVal<0)
		{
			val=currentVal+fallRate*timestep;
			val=PxMin(val,0.0f);
		}
	}
	else
	{
		if(currentVal < targetVal)
		{
			if(currentVal<0)
			{
				val=currentVal + fallRate*timestep;
				val=PxMin(val,targetVal);
			}
			else
			{
				val=currentVal + riseRate*timestep;
				val=PxMin(val,targetVal);
			}
		}
		else 
		{
			if(currentVal>0)
			{
				val=currentVal - fallRate*timestep;
				val=PxMax(val,targetVal);
			}
			else
			{
				val=currentVal - riseRate*timestep;
				val=PxMax(val,targetVal);
			}
		}	
	}
	return val;
}
예제 #4
0
PxsMaterialCombiner::PxsCombinedMaterial PxsMaterialCombiner::combineIsotropicFriction(const PxsMaterialData& mat0, const PxsMaterialData& mat1)
{
	PxsCombinedMaterial dest;

	dest.flags = (mat0.flags | mat1.flags); //& (PxMaterialFlag::eDISABLE_STRONG_FRICTION|PxMaterialFlag::eDISABLE_FRICTION);	//eventually set DisStrongFric flag, lower all others.

	if (!(dest.flags & PxMaterialFlag::eDISABLE_FRICTION))
	{
		const PxI32 fictionCombineMode = PxMax(mat0.getFrictionCombineMode(), mat1.getFrictionCombineMode());
		PxReal dynFriction = 0.f;
		PxReal staFriction = 0.f;


		switch (fictionCombineMode)
		{
		case PxCombineMode::eAVERAGE:
			dynFriction = 0.5f * (mat0.dynamicFriction + mat1.dynamicFriction);
			staFriction = 0.5f * (mat0.staticFriction + mat1.staticFriction);
			break;
		case PxCombineMode::eMIN:
			dynFriction = PxMin(mat0.dynamicFriction, mat1.dynamicFriction);
			staFriction = PxMin(mat0.staticFriction, mat1.staticFriction);
			break;
		case PxCombineMode::eMULTIPLY:
			dynFriction = (mat0.dynamicFriction * mat1.dynamicFriction);
			staFriction = (mat0.staticFriction * mat1.staticFriction);
			break;
		case PxCombineMode::eMAX:
			dynFriction = PxMax(mat0.dynamicFriction, mat1.dynamicFriction);
			staFriction = PxMax(mat0.staticFriction, mat1.staticFriction);
			break;
		}   

		dynFriction*=mDynamicFrictionScaling;
		staFriction*=mStaticFrictionScaling;
		//isotropic case
		const PxReal fDynFriction = PxMax(dynFriction, 0.f);

		const PxReal fStaFriction = physx::intrinsics::fsel(staFriction - fDynFriction, staFriction, fDynFriction);
		dest.dynFriction = fDynFriction;
		dest.staFriction = fStaFriction;
	}
	else
	{
		dest.flags |= PxMaterialFlag::eDISABLE_STRONG_FRICTION;
		dest.staFriction = 0.0f;
		dest.dynFriction = 0.0f;
	}

	return dest;
}
예제 #5
0
void SampleVehicleWayPoints::update(const PxTransform& playerTransform, const PxF32 timestep)
{
	//Increment the elapsed time
	mTimeElapsed+=timestep;

	//Work out the point on the crossing line of the next way-point that is closest to the player.
	const PxTransform& nextWayPoint=mWayPoints[mProgress+1];
	const PxVec3 v=nextWayPoint.p;
	const PxVec3 w=nextWayPoint.q.getBasisVector0();
	const PxVec3 p=playerTransform.p;
	const PxVec3 pv=p-v;
	const PxF32 t=pv.dot(w);

	//Test if the player's position is inside the width of the line crossing the next way-point.
	if(PxAbs(t) < LINEWIDTH)
	{
		//Now test if the shortest distance to the next crossing line is smaller than a threshold.
		const PxVec3 linePos=v+w*t;
		const PxVec3 diff=p-linePos;
		const PxF32 dist2=diff.magnitudeSquared();
		if(dist2<LINEDISTANCE2)
		{
			mProgress++;
		}
	}

	if(mProgress == mNumWayPoints-1)
	{
		mMinTimeElapsed=PxMin(mTimeElapsed, mMinTimeElapsed);
		mTimeElapsed=0;
		mProgress=0;
	}
}
float variableOscillator::updateVariableOscillator(float deltaTime)
{
	float returnVal;
	float halfRange;

	mCumTime += deltaTime;

	// has the function crossed a max or a min?
	if ((mGoingUp  && (mCumTime > (mPeriod / 2.0f))) ||
	        (!mGoingUp && (mCumTime > mPeriod)))
	{
		mStartVal = mLastVal;
		if (mGoingUp)
		{
			mEndVal = computeEndVal(mStartVal, mMin);
		}
		else
		{
			mEndVal = computeEndVal(mStartVal, mMax);
			mCumTime = mCumTime - mPeriod;
		}
		mGoingUp = !mGoingUp;
	}
	halfRange = 0.5f * PxAbs(mEndVal - mStartVal);
	returnVal = -halfRange * PxCos(mCumTime * PxTwoPi / mPeriod) + halfRange + PxMin(mStartVal, mEndVal);
	mLastVal = returnVal;

	return(returnVal);
}
	float ApexCudaProfileSession::flushProfileInfo(ProfileData& pd)
	{
		CUevent start = (CUevent)pd.start;
		CUevent stop = (CUevent)pd.stop;

		uint32_t op = 1;
		float startTf = 0.f, stopTf = 0.f;
		uint64_t startT = 0, stopT = 0;
		CUT_SAFE_CALL(cuEventSynchronize(start));		
		CUT_SAFE_CALL(cuEventElapsedTime(&startTf, (CUevent)mTimer, start));		
		startT = static_cast<uint64_t>(startTf * mManager->mTimeFormat) ;
		mMemBuf.write(&op, sizeof(op));
		mMemBuf.write(&startT, sizeof(startT));
		mMemBuf.write(&pd.id, sizeof(pd.id));

		op = 2;
		CUT_SAFE_CALL(cuEventSynchronize((CUevent)stop));
		CUT_SAFE_CALL(cuEventElapsedTime(&stopTf, (CUevent)mTimer, (CUevent)stop));
		stopT = static_cast<uint64_t>(stopTf * mManager->mTimeFormat);
		mMemBuf.write(&op, sizeof(op));
		mMemBuf.write(&stopT, sizeof(stopT));
		mMemBuf.write(&pd.id, sizeof(pd.id));

		CUT_SAFE_CALL(cuEventDestroy((CUevent)start));
		CUT_SAFE_CALL(cuEventDestroy((CUevent)stop));

		mFrameStart = PxMin(mFrameStart, startTf);
		mFrameFinish = PxMax(mFrameFinish, stopTf);
		return stopTf - startTf;
	}
예제 #8
0
	PxVec3 ComputeChassisAABBDimensions(const PxConvexMesh* chassisConvexMesh)
	{
		const PxU32 numChassisVerts=chassisConvexMesh->getNbVertices();
		const PxVec3* chassisVerts=chassisConvexMesh->getVertices();
		PxVec3 chassisMin(PX_MAX_F32,PX_MAX_F32,PX_MAX_F32);
		PxVec3 chassisMax(-PX_MAX_F32,-PX_MAX_F32,-PX_MAX_F32);
		for(PxU32 i=0;i<numChassisVerts;i++)
		{
			chassisMin.x=PxMin(chassisMin.x,chassisVerts[i].x);
			chassisMin.y=PxMin(chassisMin.y,chassisVerts[i].y);
			chassisMin.z=PxMin(chassisMin.z,chassisVerts[i].z);
			chassisMax.x=PxMax(chassisMax.x,chassisVerts[i].x);
			chassisMax.y=PxMax(chassisMax.y,chassisVerts[i].y);
			chassisMax.z=PxMax(chassisMax.z,chassisVerts[i].z);
		}
		const PxVec3 chassisDims=chassisMax-chassisMin;
		return chassisDims;
	}
예제 #9
0
void VariableStepper::substepStrategy(const PxReal stepSize, PxU32& substepCount, PxReal& substepSize)
{
	if(mAccumulator > mMaxSubStepSize)
		mAccumulator = 0.0f;

	// don't step less than the min step size, just accumulate
	mAccumulator  += stepSize;
	if(mAccumulator < mMinSubStepSize)
	{
		substepCount = 0;
		return;
	}

	substepCount = PxMin(PxU32(PxCeil(mAccumulator/mMaxSubStepSize)), mMaxSubSteps);
	substepSize = PxMin(mAccumulator/substepCount, mMaxSubStepSize);

	mAccumulator -= PxReal(substepCount)*substepSize;
}
예제 #10
0
	PxConvexMesh* CreateWheelConvexMesh(const PxVec3* verts, const PxU32 numVerts)
	{
		//Extract the wheel radius and width from the aabb of the wheel convex mesh.
		PxVec3 wheelMin(PX_MAX_F32,PX_MAX_F32,PX_MAX_F32);
		PxVec3 wheelMax(-PX_MAX_F32,-PX_MAX_F32,-PX_MAX_F32);
		for(PxU32 i=0;i<numVerts;i++)
		{
			wheelMin.x=PxMin(wheelMin.x,verts[i].x);
			wheelMin.y=PxMin(wheelMin.y,verts[i].y);
			wheelMin.z=PxMin(wheelMin.z,verts[i].z);
			wheelMax.x=PxMax(wheelMax.x,verts[i].x);
			wheelMax.y=PxMax(wheelMax.y,verts[i].y);
			wheelMax.z=PxMax(wheelMax.z,verts[i].z);
		}
		const PxF32 wheelWidth=wheelMax.x-wheelMin.x;
		const PxF32 wheelRadius=PxMax(wheelMax.y,wheelMax.z);

		return CreateCylinderConvexMesh(wheelWidth,wheelRadius,8);
	}
예제 #11
0
PxU32 NpArticulation::getLinks(PxArticulationLink** buffer, PxU32 bufferSize) const
{
	NP_READ_CHECK(getOwnerScene());
	const PxU32 size = mArticulationLinks.size();

	const PxU32 writeCount = PxMin(size, bufferSize);
	for(PxU32 i=0; i<writeCount; i++)
		buffer[i] = mArticulationLinks[i];

	return writeCount;
//	return Ps::dumpPointerArray((const void**)mArticulationLinks.begin(), mArticulationLinks.size(), (void**)buffer, bufferSize);
}
예제 #12
0
void ComputeWheelWidthsAndRadii(PxConvexMesh** wheelConvexMeshes, PxF32* wheelWidths, PxF32* wheelRadii)
{
	for(PxU32 i = 0; i < 4; i++)
	{
		const PxU32 numWheelVerts = wheelConvexMeshes[i]->getNbVertices();
		const PxVec3* wheelVerts = wheelConvexMeshes[i]->getVertices();
		PxVec3 wheelMin(PX_MAX_F32, PX_MAX_F32, PX_MAX_F32);
		PxVec3 wheelMax(-PX_MAX_F32, -PX_MAX_F32, -PX_MAX_F32);
		for(PxU32 j = 0; j < numWheelVerts; j++)
		{
			wheelMin.x = PxMin(wheelMin.x, wheelVerts[j].x);
			wheelMin.y = PxMin(wheelMin.y, wheelVerts[j].y);
			wheelMin.z = PxMin(wheelMin.z, wheelVerts[j].z);
			wheelMax.x = PxMax(wheelMax.x, wheelVerts[j].x);
			wheelMax.y = PxMax(wheelMax.y, wheelVerts[j].y);
			wheelMax.z = PxMax(wheelMax.z, wheelVerts[j].z);
		}
		wheelWidths[i] = wheelMax.y - wheelMin.y;
		wheelRadii[i] = PxMax(wheelMax.x, wheelMax.z) * 0.975f;
	}
}
예제 #13
0
PX_FORCE_INLINE void RTreePage::computeBounds(RTreeNodeQ& newBounds)
{
	RTreeValue _minx = MX, _miny = MX, _minz = MX, _maxx = MN, _maxy = MN, _maxz = MN;
	for (PxU32 j = 0; j < RTreePage::SIZE; j++)
	{
		if (isEmpty(j))
			continue;
		_minx = PxMin(_minx, minx[j]);
		_miny = PxMin(_miny, miny[j]);
		_minz = PxMin(_minz, minz[j]);
		_maxx = PxMax(_maxx, maxx[j]);
		_maxy = PxMax(_maxy, maxy[j]);
		_maxz = PxMax(_maxz, maxz[j]);
	}
	newBounds.minx = _minx;
	newBounds.miny = _miny;
	newBounds.minz = _minz;
	newBounds.maxx = _maxx;
	newBounds.maxy = _maxy;
	newBounds.maxz = _maxz;
}
예제 #14
0
void FixedStepper::substepStrategy(const PxReal stepSize, PxU32& substepCount, PxReal& substepSize)
{
	if(mAccumulator > mFixedSubStepSize)
		mAccumulator = 0.0f;

	// don't step less than the step size, just accumulate
	mAccumulator  += stepSize;
	if(mAccumulator < mFixedSubStepSize)
	{
		substepCount = 0;
		return;
	}

	substepSize = mFixedSubStepSize;
	substepCount = PxMin(PxU32(mAccumulator/mFixedSubStepSize), mMaxSubSteps);

	mAccumulator -= PxReal(substepCount)*substepSize;
}
예제 #15
0
//process value in range(0,1)
PX_FORCE_INLINE PxF32 processPositiveAnalogValue
(const PxF32 riseRate, const PxF32 fallRate,
 const PxF32 currentVal, const PxF32 targetVal,
 const PxF32 timestep)
{
	PX_ASSERT(targetVal>=-0.01f && targetVal<=1.01f);
	PxF32 val;
	if(currentVal<targetVal)
	{
		val=currentVal + riseRate*timestep;
		val=PxMin(val,targetVal);
	}
	else 
	{
		val=currentVal - fallRate*timestep;
		val=PxMax(val,targetVal);
	}
	return val;
}
예제 #16
0
	void setMissingPropertiesToDefault( RepXNode* topNode, RepXReaderWriter& editor, const RepXDefaultEntry* defaults, PxU32 numDefaults, TNameOffsetMap& map )
	{
		for ( RepXNode* child = topNode->mFirstChild; child != NULL; child = child->mNextSibling )
			setMissingPropertiesToDefault( child, editor, defaults, numDefaults, map );

		const TNameOffsetMap::Entry* entry( map.find( topNode->mName ) );
		if ( entry )
		{
			RepXReaderWriter& theReader( editor );
			theReader.setNode( *topNode );
			char nameBuffer[512] = {0};
			size_t nameLen = strlen( topNode->mName );
			//For each default property entry for this node type.
			for ( const RepXDefaultEntry* item = defaults + entry->second; strncmp( item->name, topNode->mName, nameLen ) == 0; ++item )
			{
				bool childAdded = false;
				const char* nameStart = item->name + nameLen;
				++nameStart;
				theReader.pushCurrentContext();
				const char* str = nameStart;
				while( *str )
				{
					 const char *period = nextPeriod( str );
					 size_t len = PxMin( period - str, ptrdiff_t(1023) ); //can't be too careful these days.
					 memcpy( nameBuffer, str, len );
					 nameBuffer[len] = 0;
					 if ( theReader.gotoChild( nameBuffer ) == false )
					 {
						 childAdded = true;
						 theReader.addOrGotoChild( nameBuffer );
					 }
					 if (*period )
						 str = period + 1;
					 else
						 str = period;
				}
				if ( childAdded )
					theReader.setCurrentItemValue( item->value );
				theReader.popCurrentContext();
			}
		}
	}
void CookingAbstract::PhysicalMesh::computeTriangleAreas()
{
	smallestTriangleArea = largestTriangleArea = 0.0f;

	if (indices == NULL || vertices == NULL)
	{
		return;
	}

	smallestTriangleArea = PX_MAX_F32;

	for (PxU32 i = 0; i < numIndices; i += 3)
	{
		const PxVec3 edge1 = vertices[indices[i + 1]] - vertices[indices[i]];
		const PxVec3 edge2 = vertices[indices[i + 2]] - vertices[indices[i]];
		const PxF32 triangleArea = edge1.cross(edge2).magnitude();

		largestTriangleArea = PxMax(largestTriangleArea, triangleArea);
		smallestTriangleArea = PxMin(smallestTriangleArea, triangleArea);
	}
}
	PX_FORCE_INLINE PxU32 collideWithMeshTriangle(PxVec3& surfaceNormal, PxVec3& surfacePos,
								  PxVec3& proxSurfaceNormal, PxVec3& proxSurfacePos,
								  PxReal& ccTime, PxReal& distOldToSurface,
								  const PxVec3& oldPos, const PxVec3& newPos,
								  const PxVec3& origin, const PxVec3& e0,
								  const PxVec3& e1, bool hasCC,
								  const PxReal& collRadius, const PxReal& proxRadius)
	{
		PxU32 flags = 0;

		PxReal collisionRadius2 = collRadius * collRadius;
		PxReal proximityRadius2 = proxRadius * proxRadius;

		PxVec3 motion = newPos - oldPos;

		// dc and proximity tests
		PxVec3 tmpV = origin - newPos;

		PxReal a = e0.dot(e0);
		PxReal b = e0.dot(e1);
		PxReal c = e1.dot(e1);
		PxReal d = e0.dot(tmpV);
		PxReal e = e1.dot(tmpV);
		PxVec3 coords;
		coords.x = b*e - c*d;	// s * det
		coords.y = b*d - a*e;	// t * det
		coords.z = a*c - b*b;	// det

		bool insideCase = false;
		PxVec3 clampedCoords(PxVec3(0));
		if (coords.x <= 0.0f) 
		{
			c = PxMax(c, FLT_MIN);
			clampedCoords.y = -e/c;
		}
		else if (coords.y <= 0.0f) 
		{
			a = PxMax(a, FLT_MIN);
			clampedCoords.x = -d/a;
		}
		else if (coords.x + coords.y > coords.z) 
		{
			PxReal denominator = a + c - b - b;
			PxReal numerator   = c + e - b - d;
			denominator = PxMax(denominator, FLT_MIN);
			clampedCoords.x = numerator / denominator;
			clampedCoords.y = 1.0f - clampedCoords.x;
		}
		else // all inside 
		{	
			PxReal tmpF = PxMax(coords.z, FLT_MIN);
			tmpF = 1.0f / tmpF;
			clampedCoords.x = coords.x * tmpF;
			clampedCoords.y = coords.y * tmpF;
			insideCase = true;
		}
		clampedCoords.x = PxMax(clampedCoords.x, 0.0f);
		clampedCoords.y = PxMax(clampedCoords.y, 0.0f);
		clampedCoords.x = PxMin(clampedCoords.x, 1.0f);
		clampedCoords.y = PxMin(clampedCoords.y, 1.0f);

		// Closest point to particle inside triangle
		PxVec3 closest = origin + e0 * clampedCoords.x + e1 * clampedCoords.y;

		PxVec3 triangleOffset = newPos - closest;
		PxReal triangleDistance2 = triangleOffset.magnitudeSquared();

		PxVec3 triangleNormal = e0.cross(e1);
		PxReal e0e1Span = triangleNormal.magnitude();
		
		bool isInFront = triangleOffset.dot(triangleNormal) > 0.0f;

		// MS: Possible optimzation
		/*
		if (isInFront && (triangleDistance2 >= proximityRadius2))
			return flags;
		*/

		bool isInProximity = insideCase && (triangleDistance2 < proximityRadius2) && isInFront;
		bool isInDiscrete = (triangleDistance2 < collisionRadius2) && isInFront;

		if (!hasCC)
		{
			// Only apply discrete and proximity collisions if no continuous collisions was detected so far (for any colliding shape)

			if (isInDiscrete)
			{
				if (triangleDistance2 > PXS_FLUID_COLL_TRI_DISTANCE)
				{
					surfaceNormal = triangleOffset * PxRecipSqrt(triangleDistance2);
				}
				else
				{
					surfaceNormal = triangleNormal * (1.0f / e0e1Span);
				}
				surfacePos = closest + (surfaceNormal * collRadius);
				flags |= PXS_FLUID_COLL_FLAG_L_DC;
			}
			
			if (isInProximity)
			{
				proxSurfaceNormal = triangleNormal * (1.0f / e0e1Span);
				proxSurfacePos = closest + (proxSurfaceNormal * collRadius);
				flags |= PXS_FLUID_COLL_FLAG_L_PROX;

				tmpV = (oldPos - origin); //this time it's not the newPosition offset.
				distOldToSurface = proxSurfaceNormal.dot(tmpV);	// Need to return the distance to decide which constraints should be thrown away
			}
		}

		if (!isInDiscrete && !isInProximity)
		{
			// cc test (let's try only executing this if no discrete coll, or proximity happend).
			tmpV = origin - oldPos; //this time it's not the newPosition offset.
			PxReal pDistN = triangleNormal.dot(tmpV);
			PxReal rLengthN = triangleNormal.dot(motion);

			if (pDistN > 0.0f || rLengthN >= pDistN) 
				return flags;

			//we are in the half closed interval [0.0f, 1.0)
			
			PxReal t = pDistN / rLengthN;
			PX_ASSERT((t >= 0.0f) && (t < 1.0f));

			PxVec3 relativePOSITION = (motion * t);
			PxVec3 testPoint = oldPos + relativePOSITION;

			// a,b,c and coords.z don't depend on test point -> still valid
			tmpV = origin - testPoint;
			d = e0.dot(tmpV);
			e = e1.dot(tmpV);
			coords.x = b*e - c*d;
			coords.y = b*d - a*e;

			//maybe we don't need this for rare case leaking on triangle boundaries? 
			PxReal eps = coords.z * PXS_FLUID_COLL_RAY_EPSILON_FACTOR;

			if ((coords.x >= -eps) && (coords.y >= -eps) && (coords.x + coords.y <= coords.z + eps)) 
			{
				PxReal invLengthN = (1.0f / e0e1Span);
				distOldToSurface = -pDistN * invLengthN;	// Need to return the distance to decide which constraints should be thrown away
				surfaceNormal = triangleNormal * invLengthN;
				//surfacePos = testPoint + (surfaceNormal * collRadius);
				computeContinuousTargetPosition(surfacePos, oldPos, relativePOSITION, surfaceNormal, collRadius);
				ccTime = t;
				flags |= PXS_FLUID_COLL_FLAG_L_CC;
			}
		}

		return flags;
	}
void PxVehicleCopyDynamicsData(const PxVehicleCopyDynamicsMap& wheelMap, const PxVehicleWheels& src, PxVehicleWheels* trg)
{
	PX_CHECK_AND_RETURN(trg, "PxVehicleCopyDynamicsData requires that trg is a valid vehicle pointer");

	PX_CHECK_AND_RETURN(src.getVehicleType() == trg->getVehicleType(), "PxVehicleCopyDynamicsData requires that both src and trg are the same type of vehicle");

#ifdef PX_CHECKED
	{
		const PxU32 numWheelsSrc = src.mWheelsSimData.getNbWheels();
		const PxU32 numWheelsTrg = trg->mWheelsSimData.getNbWheels();
		PxU8 copiedWheelsSrc[PX_MAX_NB_WHEELS];
		PxMemZero(copiedWheelsSrc, sizeof(PxU8) * PX_MAX_NB_WHEELS);
		PxU8 setWheelsTrg[PX_MAX_NB_WHEELS];
		PxMemZero(setWheelsTrg, sizeof(PxU8) * PX_MAX_NB_WHEELS);
		for(PxU32 i = 0; i < PxMin(numWheelsSrc, numWheelsTrg); i++)
		{
			const PxU32 srcWheelId = wheelMap.sourceWheelIds[i];
			PX_CHECK_AND_RETURN(srcWheelId < numWheelsSrc, "PxVehicleCopyDynamicsData - wheelMap contains illegal source wheel id");
			PX_CHECK_AND_RETURN(0 == copiedWheelsSrc[srcWheelId], "PxVehicleCopyDynamicsData - wheelMap contains illegal source wheel id");
			copiedWheelsSrc[srcWheelId] = 1;

			const PxU32 trgWheelId = wheelMap.targetWheelIds[i];
			PX_CHECK_AND_RETURN(trgWheelId < numWheelsTrg, "PxVehicleCopyDynamicsData - wheelMap contains illegal target wheel id");
			PX_CHECK_AND_RETURN(0 == setWheelsTrg[trgWheelId], "PxVehicleCopyDynamicsData - wheelMap contains illegal target wheel id");
			setWheelsTrg[trgWheelId]=1;
		}
	}
#endif


	const PxU32 numWheelsSrc = src.mWheelsSimData.getNbWheels();
	const PxU32 numWheelsTrg = trg->mWheelsSimData.getNbWheels();

	//Set all dynamics data on the target to zero.
	//Be aware that setToRestState sets the rigid body to 
	//rest so set the momentum back after calling setToRestState.
	PxRigidDynamic* actorTrg = trg->getRigidDynamicActor();
	PxVec3 linVel = actorTrg->getLinearVelocity();
	PxVec3 angVel = actorTrg->getAngularVelocity();
	switch(src.getVehicleType())
	{
	case PxVehicleTypes::eDRIVE4W:
		((PxVehicleDrive4W*)trg)->setToRestState();
		break;
	case PxVehicleTypes::eDRIVENW:
		((PxVehicleDriveNW*)trg)->setToRestState();
		break;
	case PxVehicleTypes::eDRIVETANK:
		((PxVehicleDriveTank*)trg)->setToRestState();
		break;
	case PxVehicleTypes::eNODRIVE:
		((PxVehicleNoDrive*)trg)->setToRestState();
		break;
	default:
		break;
	}
	actorTrg->setLinearVelocity(linVel);
	actorTrg->setAngularVelocity(angVel);


	//Keep a track of the wheels on trg that have their dynamics data set as a copy from src.
	PxU8 setWheelsTrg[PX_MAX_NB_WHEELS];
	PxMemZero(setWheelsTrg, sizeof(PxU8) * PX_MAX_NB_WHEELS);

	//Keep a track of the average wheel rotation speed of all enabled wheels on src.
	PxU32 numEnabledWheelsSrc = 0;
	PxF32 accumulatedWheelRotationSpeedSrc = 0.0f;

	//Copy wheel dynamics data from src wheels to trg wheels.
	//Track the target wheels that have been given dynamics data from src wheels.
	//Compute the accumulated wheel rotation speed of all enabled src wheels.
	const PxU32 numMappedWheels = PxMin(numWheelsSrc, numWheelsTrg);
	for(PxU32 i = 0; i < numMappedWheels; i++)
	{
		const PxU32 srcWheelId = wheelMap.sourceWheelIds[i];
		const PxU32 trgWheelId = wheelMap.targetWheelIds[i];

		trg->mWheelsDynData.copy(src.mWheelsDynData, srcWheelId, trgWheelId);

		setWheelsTrg[trgWheelId] = 1;

		if(!src.mWheelsSimData.getIsWheelDisabled(srcWheelId))
		{
			numEnabledWheelsSrc++;
			accumulatedWheelRotationSpeedSrc += src.mWheelsDynData.getWheelRotationSpeed(srcWheelId);
		}
	}

	//Compute the average wheel rotation speed of src.
	PxF32 averageWheelRotationSpeedSrc = 0;
	if(numEnabledWheelsSrc > 0)
	{
		averageWheelRotationSpeedSrc = (accumulatedWheelRotationSpeedSrc/ (1.0f * numEnabledWheelsSrc));
	}

	//For wheels on trg that have not had their dynamics data copied from src just set
	//their wheel rotation speed to the average wheel rotation speed.
	for(PxU32 i = 0; i < numWheelsTrg; i++)
	{
		if(0 == setWheelsTrg[i] && !trg->mWheelsSimData.getIsWheelDisabled(i))
		{
			trg->mWheelsDynData.setWheelRotationSpeed(i, averageWheelRotationSpeedSrc);
		}
	}

	//Copy the engine rotation speed/gear states/autobox states/etc.
	switch(src.getVehicleType())
	{
	case PxVehicleTypes::eDRIVE4W:
	case PxVehicleTypes::eDRIVENW:
	case PxVehicleTypes::eDRIVETANK:
		{
			const PxVehicleDriveDynData& driveDynDataSrc = ((const PxVehicleDrive&)src).mDriveDynData;
			PxVehicleDriveDynData* driveDynDataTrg = &((PxVehicleDrive*)trg)->mDriveDynData;
			*driveDynDataTrg = driveDynDataSrc;
		}
		break;
	default:
		break;
	}
}
예제 #20
0
PX_FORCE_INLINE void RTreeNodeQ::grow(const RTreeNodeQ& node)
{
	minx = PxMin(minx, node.minx); miny = PxMin(miny, node.miny); minz = PxMin(minz, node.minz);
	maxx = PxMax(maxx, node.maxx); maxy = PxMax(maxy, node.maxy); maxz = PxMax(maxz, node.maxz);
}
예제 #21
0
void RTree::refitAllStaticTree(CallbackRefit& cb, PxBounds3* retBounds)
{
	PxU8* treeNodes8 = PX_IS_X64 ? CAST_U8(get64BitBasePage()) : CAST_U8((mFlags & IS_DYNAMIC) ? NULL : mPages);

	// since pages are ordered we can scan back to front and the hierarchy will be updated
	for (PxI32 iPage = PxI32(mTotalPages)-1; iPage>=0; iPage--)
	{
		RTreePage& page = mPages[iPage];
		for (PxU32 j = 0; j < RTREE_PAGE_SIZE; j++)
		{
			if (page.isEmpty(j))
				continue;
			if (page.isLeaf(j))
			{
				Vec3V childMn, childMx;
				cb.recomputeBounds(page.ptrs[j]-1, childMn, childMx); // compute the bound around triangles
				PxVec3 mn3, mx3;
				V3StoreU(childMn, mn3);
				V3StoreU(childMx, mx3);
				page.minx[j] = mn3.x; page.miny[j] = mn3.y; page.minz[j] = mn3.z;
				page.maxx[j] = mx3.x; page.maxy[j] = mx3.y; page.maxz[j] = mx3.z;
			} else
			{
				const RTreePage* child = (const RTreePage*)(treeNodes8 + page.ptrs[j]);
				PX_COMPILE_TIME_ASSERT(RTREE_PAGE_SIZE == 4);
				bool first = true;
				for (PxU32 k = 0; k < RTREE_PAGE_SIZE; k++)
				{
					if (child->isEmpty(k))
						continue;
					if (first)
					{
						page.minx[j] = child->minx[k]; page.miny[j] = child->miny[k]; page.minz[j] = child->minz[k];
						page.maxx[j] = child->maxx[k]; page.maxy[j] = child->maxy[k]; page.maxz[j] = child->maxz[k];
						first = false;
					} else
					{
						page.minx[j] = PxMin(page.minx[j], child->minx[k]);
						page.miny[j] = PxMin(page.miny[j], child->miny[k]);
						page.minz[j] = PxMin(page.minz[j], child->minz[k]);
						page.maxx[j] = PxMax(page.maxx[j], child->maxx[k]);
						page.maxy[j] = PxMax(page.maxy[j], child->maxy[k]);
						page.maxz[j] = PxMax(page.maxz[j], child->maxz[k]);
					}
				}
			}
		}
	}

	if (retBounds)
	{
		RTreeNodeQ bound1;
		for (PxU32 ii = 0; ii<mNumRootPages; ii++)
		{
			mPages[ii].computeBounds(bound1);
			if (ii == 0)
			{
				retBounds->minimum = PxVec3(bound1.minx, bound1.miny, bound1.minz);
				retBounds->maximum = PxVec3(bound1.maxx, bound1.maxy, bound1.maxz);
			} else
			{
				retBounds->minimum = retBounds->minimum.minimum(PxVec3(bound1.minx, bound1.miny, bound1.minz));
				retBounds->maximum = retBounds->maximum.maximum(PxVec3(bound1.maxx, bound1.maxy, bound1.maxz));
			}
		}
	}

#ifdef PX_CHECKED
	validate(&cb);
#endif
}
예제 #22
0
	void dampVec3(const PxVec3& oldPosition, PxVec3& newPosition, PxF32 timestep)
	{
		PxF32 t = 0.7f * timestep * 8.0f;
		t = PxMin(t, 1.0f);
		newPosition = oldPosition * (1 - t) + newPosition * t;
	}
예제 #23
0
void PxsFluidDynamics::updatePacketLocalHash(PxsSphUpdateType updateType, PxVec3* forceBuf, PxsFluidParticle* particles, const PxsParticleCell& packet,
											 const PxsFluidPacketSections& packetSections, const PxsFluidPacketHaloRegions& haloRegions, 
											 PxsFluidDynamicsTempBuffers& tempBuffers)
{
	// Particle index lists for local hash of particle cells (for two subpackets A and B).
	PxU32* particleIndicesSpA = tempBuffers.indicesSubpacketA;
	PxU32* particleIndicesSpB = tempBuffers.indicesSubpacketB;

	// Local hash tables for particle cells (for two subpackets A and B).
	PxsParticleCell* particleCellsSpA = tempBuffers.cellHashTableSubpacketA;
	PxsParticleCell* particleCellsSpB = tempBuffers.cellHashTableSubpacketB;

	PxVec3 packetCorner = PxVec3(PxReal(packet.coords.x), PxReal(packet.coords.y), PxReal(packet.coords.z)) * mParams.packetSize;

	PxU32 particlesLeftA0 = packet.numParticles;
	PxsFluidParticle* particlesSpA0 = particles + packet.firstParticle;
	PxVec3* forceBufA0 = forceBuf + packet.firstParticle;

	while (particlesLeftA0)
	{
		PxU32 numParticlesSpA = PxMin(particlesLeftA0, static_cast<PxU32>(PXS_FLUID_SUBPACKET_PARTICLE_LIMIT_FORCE_DENSITY));

		// Make sure the number of hash buckets is a power of 2 (requirement for the used hash function)
		const PxU32 numCellHashBucketsSpA = Ps::nextPowerOfTwo(numParticlesSpA + 1);
		PX_ASSERT(numCellHashBucketsSpA <= tempBuffers.cellHashMaxSize);

		// Get local cell hash for the current subpacket
		PxsFluidSpatialHash::buildLocalHash(particlesSpA0, numParticlesSpA, particleCellsSpA,
			particleIndicesSpA, tempBuffers.hashKeys, numCellHashBucketsSpA, mParams.cellSizeInv, packetCorner);

		//---------------------------------------------------------------------------------------------------

		//
		// Compute particle interactions between particles within the current subpacket.
		//

		updateCellsSubpacket(updateType, forceBufA0, particlesSpA0, particleCellsSpA, particleIndicesSpA, numCellHashBucketsSpA, mParams, tempBuffers);

		//---------------------------------------------------------------------------------------------------

		//
		// Compute particle interactions between particles of current subpacket and particles
		// of other subpackets within the same packet (i.e., we process all subpacket pairs).
		//
		
		PxU32 particlesLeftB = particlesLeftA0 - numParticlesSpA;
		PxsFluidParticle* particlesSpB = particlesSpA0 + numParticlesSpA;
		PxVec3* forceBufB = forceBufA0 + numParticlesSpA;

		while (particlesLeftB)
		{
			PxU32 numParticlesSpB = PxMin(particlesLeftB, static_cast<PxU32>(PXS_FLUID_SUBPACKET_PARTICLE_LIMIT_FORCE_DENSITY));
			
			// Make sure the number of hash buckets is a power of 2 (requirement for the used hash function)
			const PxU32 numCellHashBucketsSpB = Ps::nextPowerOfTwo(numParticlesSpB + 1);
			PX_ASSERT(numCellHashBucketsSpB <= tempBuffers.cellHashMaxSize);

			// Get local cell hash for other subpacket
			PxsFluidSpatialHash::buildLocalHash(particlesSpB, numParticlesSpB, particleCellsSpB, particleIndicesSpB, tempBuffers.hashKeys,
				numCellHashBucketsSpB, mParams.cellSizeInv, packetCorner);

			// For the cells of subpacket A, find neighboring cells in the subpacket B and compute particle interactions.
			updateCellsSubpacketPair(updateType, forceBufA0, forceBufB, particlesSpA0, particlesSpB, particleCellsSpA, particleCellsSpB,
				particleIndicesSpA, particleIndicesSpB, numCellHashBucketsSpA, numCellHashBucketsSpB, true, mParams, tempBuffers, numParticlesSpA < numParticlesSpB);

			particlesLeftB -= numParticlesSpB;
			particlesSpB += numParticlesSpB;
			forceBufB += numParticlesSpB;
		}

		particlesLeftA0 -= numParticlesSpA;
		particlesSpA0 += numParticlesSpA;
		forceBufA0 += numParticlesSpA;
	}

	//---------------------------------------------------------------------------------------------------

	//
	// Compute particle interactions between particles of sections of the current packet and particles of neighboring
	// halo regions
	//

	PX_ASSERT(PXS_FLUID_BRUTE_FORCE_PARTICLE_THRESHOLD_HALO_VS_SECTION <= PXS_FLUID_SUBPACKET_PARTICLE_LIMIT_FORCE_DENSITY);
	if (haloRegions.maxNumParticles != 0)
	{
		for(PxU32 s=0; s < 26; s++)
		{
			PxU32 numSectionParticles = packetSections.numParticles[s];
			if (numSectionParticles == 0)
				continue;

			bool sectionEnablesBruteForce = (numSectionParticles <= PXS_FLUID_BRUTE_FORCE_PARTICLE_THRESHOLD_HALO_VS_SECTION);

			SectionToHaloTable& neighborHaloRegions = sSectionToHaloTable[s];
			PxU32 numHaloNeighbors = neighborHaloRegions.numHaloRegions;

			PxU32 particlesLeftA = numSectionParticles;
			PxsFluidParticle* particlesSpA = particles + packetSections.firstParticle[s];
			PxVec3* forceBufA = forceBuf + packetSections.firstParticle[s];

			while (particlesLeftA)
			{
				PxU32 numParticlesSpA = PxMin(particlesLeftA, static_cast<PxU32>(PXS_FLUID_SUBPACKET_PARTICLE_LIMIT_FORCE_DENSITY));

				// Compute particle interactions between particles of the current subpacket (of the section) 
				// and particles of neighboring halo regions relevant. 

				//Process halo regions which need local hash building first.
				bool isLocalHashValid = false;

				// Make sure the number of hash buckets is a power of 2 (requirement for the used hash function)
				const PxU32 numCellHashBucketsSpA = Ps::nextPowerOfTwo(numParticlesSpA + 1);
				PX_ASSERT(numCellHashBucketsSpA <= tempBuffers.cellHashMaxSize);
#if MERGE_HALO_REGIONS
				//Read halo region particles into temporary buffer 
				PxU32 numMergedHaloParticles = 0;
				for(PxU32 h = 0; h < numHaloNeighbors; h++)
				{
					PxU32 haloRegionIdx = neighborHaloRegions.haloRegionIndices[h];
					PxU32 numHaloParticles = haloRegions.numParticles[haloRegionIdx];

					// chunk regions into subpackets!
					PxU32 particlesLeftB = numHaloParticles;
					PxsFluidParticle* particlesSpB = particles + haloRegions.firstParticle[haloRegionIdx];
					PxVec3* forceBufB = forceBuf + haloRegions.firstParticle[haloRegionIdx];
					while (particlesLeftB)
					{
						PxU32 numParticlesSpB = PxMin(particlesLeftB, static_cast<PxU32>(PXS_FLUID_SUBPACKET_PARTICLE_LIMIT_FORCE_DENSITY));
		
						// if there are plenty of particles already, don't bother to do the copy for merging.
						if (numParticlesSpB > PXS_FLUID_BRUTE_FORCE_PARTICLE_THRESHOLD_HALO_VS_SECTION)
						{
							updateSubpacketPairHalo(forceBufA, particlesSpA, numParticlesSpA, particleCellsSpA, particleIndicesSpA, isLocalHashValid, numCellHashBucketsSpA, 
								forceBufB, particlesSpB, numParticlesSpB, particleCellsSpB, particleIndicesSpB, packetCorner, updateType, hashKeyArray, tempBuffers);							
						}
						else
						{
							if (numMergedHaloParticles + numParticlesSpB > PXS_FLUID_SUBPACKET_PARTICLE_LIMIT_FORCE_DENSITY)
							{
								//flush
								updateSubpacketPairHalo(forceBufA, particlesSpA, numParticlesSpA, particleCellsSpA, particleIndicesSpA, isLocalHashValid, numCellHashBucketsSpA, 
									tempBuffers.mergedHaloRegions, numMergedHaloParticles, particleCellsSpB, particleIndicesSpB, packetCorner, updateType, hashKeyArray, tempBuffers);
								numMergedHaloParticles = 0;
							}

							for (PxU32 k = 0; k < numParticlesSpB; ++k)
								tempBuffers.mergedHaloRegions[numMergedHaloParticles++] = particlesSpB[k];
						}

						particlesLeftB -= numParticlesSpB;
						particlesSpB += numParticlesSpB;						
					}
				}

				//flush
				updateSubpacketPairHalo(forceBufA, particlesSpA, numParticlesSpA, particleCellsSpA, particleIndicesSpA, isLocalHashValid, numCellHashBucketsSpA, 
					tempBuffers.mergedHaloRegions, numMergedHaloParticles, particleCellsSpB, particleIndicesSpB, packetCorner, updateType, hashKeyArray, tempBuffers);
#else // MERGE_HALO_REGIONS
				for(PxU32 h = 0; h < numHaloNeighbors; h++)
				{
					PxU32 haloRegionIdx = neighborHaloRegions.haloRegionIndices[h];
					PxU32 numHaloParticles = haloRegions.numParticles[haloRegionIdx];

					bool haloRegionEnablesBruteForce = (numHaloParticles <= PXS_FLUID_BRUTE_FORCE_PARTICLE_THRESHOLD_HALO_VS_SECTION);

					if (sectionEnablesBruteForce && haloRegionEnablesBruteForce)
						continue;

					if (!isLocalHashValid)
					{
						// Get local cell hash for the current subpacket
						PxsFluidSpatialHash::buildLocalHash(particlesSpA, numParticlesSpA, particleCellsSpA,
							particleIndicesSpA, tempBuffers.hashKeys, numCellHashBucketsSpA, mParams.cellSizeInv, packetCorner);
						isLocalHashValid = true;
					}

					PxU32 particlesLeftB = numHaloParticles;
					PxsFluidParticle* particlesSpB = particles + haloRegions.firstParticle[haloRegionIdx];
				
					while (particlesLeftB)
					{
						PxU32 numParticlesSpB = PxMin(particlesLeftB, static_cast<PxU32>(PXS_FLUID_SUBPACKET_PARTICLE_LIMIT_FORCE_DENSITY));

						// It is important that no data is written to particles in halo regions since they belong to
						// a neighboring packet. The interaction effect of the current packet on the neighboring packet will be
						// considered when the neighboring packet is processed.
						
						// Make sure the number of hash buckets is a power of 2 (requirement for the used hash function)
						const PxU32 numCellHashBucketsSpB = Ps::nextPowerOfTwo(numParticlesSpB + 1);
						PX_ASSERT(numCellHashBucketsSpB <= tempBuffers.cellHashMaxSize);

						// Get local cell hash for other subpacket
						PxsFluidSpatialHash::buildLocalHash(particlesSpB, numParticlesSpB, particleCellsSpB, particleIndicesSpB, tempBuffers.hashKeys,
							numCellHashBucketsSpB, mParams.cellSizeInv, packetCorner);

						// For the cells of subpacket A, find neighboring cells in the subpacket B and compute particle interactions.
						updateCellsSubpacketPair(updateType, forceBufA, NULL, particlesSpA, particlesSpB, particleCellsSpA, particleCellsSpB,
							particleIndicesSpA, particleIndicesSpB, numCellHashBucketsSpA, numCellHashBucketsSpB, false, mParams, tempBuffers, numParticlesSpA > numParticlesSpB);
						
						particlesLeftB -= numParticlesSpB;
						particlesSpB += numParticlesSpB;						
					}
				}

				//Now process halo regions which don't need local hash building.
				PxU32 mergedIndexCount = 0;
				for(PxU32 h = 0; h < numHaloNeighbors; h++)
				{
					PxU32 haloRegionIdx = neighborHaloRegions.haloRegionIndices[h];
					PxU32 numHaloParticles = haloRegions.numParticles[haloRegionIdx];
					if (numHaloParticles == 0)
						continue;

					bool haloRegionEnablesBruteForce = (numHaloParticles <= PXS_FLUID_BRUTE_FORCE_PARTICLE_THRESHOLD_HALO_VS_SECTION);

					if (!sectionEnablesBruteForce || !haloRegionEnablesBruteForce)
						continue;

					// The section and the halo region do not have enough particles to make it worth
					// building a local cell hash --> use brute force approach

					// This is given by the brute force condition (haloRegionEnablesBruteForce). Its necessary to 
					// make sure a halo region alone fits into the merge buffer.
					PX_ASSERT(numHaloParticles <= PXS_FLUID_SUBPACKET_PARTICLE_LIMIT_FORCE_DENSITY);

					if (mergedIndexCount + numHaloParticles > PXS_FLUID_SUBPACKET_PARTICLE_LIMIT_FORCE_DENSITY)
					{
						updateParticleGroupPair(forceBufA, NULL, particlesSpA, particles,
							tempBuffers.orderedIndicesSubpacket, numSectionParticles,
							tempBuffers.mergedIndices, mergedIndexCount,
							false, updateType == PXS_SPH_DENSITY,
							mParams, tempBuffers.simdPositionsSubpacket, tempBuffers.indexStream);	
						mergedIndexCount = 0;
					}

					PxU32 hpIndex = haloRegions.firstParticle[haloRegionIdx];
					for (PxU32 k = 0; k < numHaloParticles; k++)
						tempBuffers.mergedIndices[mergedIndexCount++] = hpIndex++;
				}

				if (mergedIndexCount > 0)
				{
					updateParticleGroupPair(forceBufA, NULL, particlesSpA, particles,
						tempBuffers.orderedIndicesSubpacket, numSectionParticles,
						tempBuffers.mergedIndices, mergedIndexCount,
						false, updateType == PXS_SPH_DENSITY,
						mParams, tempBuffers.simdPositionsSubpacket, tempBuffers.indexStream);
				}
#endif // MERGE_HALO_REGIONS

				particlesLeftA -= numParticlesSpA;
				particlesSpA += numParticlesSpA;
				forceBufA += numParticlesSpA;
			}
		}
	}
}
void ParticleEmitterPressure::stepInternal(ParticleData& particles, PxReal dt, const PxVec3& externalAcceleration, PxReal maxParticleVelocity)
{
	PX_ASSERT(mNumX > 0 && mNumY > 0);
	
	mSimulationAcceleration = externalAcceleration;
	mSimulationMaxVelocity = maxParticleVelocity;
	
	PxU32 numEmittedParticles = 0;

	PxU32 maxParticlesPerStep = (PxU32)physx::shdfnd::floor(mMaxRate*dt);
	PxU32 maxParticles = PxMin(particles.maxParticles - particles.numParticles, maxParticlesPerStep);

	PxU32 siteNr = 0;	
	for(PxU32 y = 0; y != mNumY; y++)
	{
		PxReal offset = 0.0f;
		if (y%2) offset = mSpacingX * 0.5f;

		for(PxU32 x = 0; x != mNumX; x++)
		{
			if (isOutsideShape(x,y,offset))
				continue;

			SiteData& siteData = mSites[siteNr];

			//position noise
			PxVec3 posNoise;
			posNoise.x = randInRange(-mRandomPos.x, mRandomPos.x);
			posNoise.y = randInRange(-mRandomPos.y, mRandomPos.y);
			
			//special code for Z noise 
			if (!siteData.predecessor) 
				siteData.noiseZ = randInRange(-mRandomPos.z, mRandomPos.z);
			else
			{
				PxReal noiseZOffset = PxMin(mMaxZNoiseOffset, mRandomPos.z);
				siteData.noiseZ += randInRange(-noiseZOffset, noiseZOffset);
				siteData.noiseZ = PxClamp(siteData.noiseZ, mRandomPos.z, -mRandomPos.z);
			}

			posNoise.z = siteData.noiseZ;

			//set position
			PxVec3 sitePos = mBasePos + mAxisX*(offset+mSpacingX*x) + mAxisY*(mSpacingY*y) + mAxisZ*siteData.noiseZ;
			PxVec3 particlePos = sitePos + mAxisX*posNoise.x + mAxisY*posNoise.y;

			PxVec3 siteVel;
			computeSiteVelocity(siteVel, particlePos);

			if (siteData.predecessor)
			{
				predictPredecessorPos(siteData, dt);
			}
			else			{
				bool isSpawned = spawnParticle(particles, numEmittedParticles, maxParticles, particlePos, siteVel);
				if(isSpawned)
				{
					updatePredecessor(siteData, particlePos, siteVel);
				}
				else
				{
					siteData.predecessor = false;
					return;
				}
			}
			
			bool allSpawned = stepEmissionSite(siteData, particles, numEmittedParticles, maxParticles, sitePos, siteVel, dt);
			if(!allSpawned)
				return;

			siteNr++;
		}
	}
}