示例#1
0
void FKConvexElem::AddCachedSolidConvexGeom(TArray<FDynamicMeshVertex>& VertexBuffer, TArray<int32>& IndexBuffer, const FColor VertexColor)
{
#if WITH_PHYSX
	if(ConvexMesh)
	{

		int32 StartVertOffset = VertexBuffer.Num();

		// get PhysX data
		const PxVec3* PVertices = ConvexMesh->getVertices();
		const PxU8* PIndexBuffer = ConvexMesh->getIndexBuffer();
		PxU32 NbPolygons = ConvexMesh->getNbPolygons();

		for(PxU32 i=0;i<NbPolygons;i++)
		{
			PxHullPolygon Data;
			bool bStatus = ConvexMesh->getPolygonData(i, Data);
			check(bStatus);

			const PxU8* indices = PIndexBuffer + Data.mIndexBase;

			// create tangents from the first and second vertices of each polygon
			const FVector TangentX = P2UVector(PVertices[indices[1]]-PVertices[indices[0]]).SafeNormal();
			const FVector TangentZ = FVector(Data.mPlane[0], Data.mPlane[1], Data.mPlane[2]).SafeNormal();
			const FVector TangentY = (TangentX ^ TangentZ).SafeNormal();

			// add vertices 
			for(PxU32 j=0;j<Data.mNbVerts;j++)
			{
				int32 VertIndex = indices[j];

				FDynamicMeshVertex Vert1;
				Vert1.Position = P2UVector(PVertices[VertIndex]);
				Vert1.Color = VertexColor;
				Vert1.SetTangents(
					TangentX,
					TangentY,
					TangentZ
					);
				VertexBuffer.Add(Vert1);
			}

			// Add indices
			PxU32 nbTris = Data.mNbVerts - 2;
			for(PxU32 j=0;j<nbTris;j++)
			{
				IndexBuffer.Add(StartVertOffset+0);
				IndexBuffer.Add(StartVertOffset+j+2);
				IndexBuffer.Add(StartVertOffset+j+1);
			}

			StartVertOffset += Data.mNbVerts;
		}
	}
	else
	{
		UE_LOG(LogPhysics, Log, TEXT("FKConvexElem::AddCachedSolidConvexGeom : No ConvexMesh, so unable to draw."));
	}
#endif // WITH_PHYSX
}
示例#2
0
void UDestructibleComponent::SetChunkVisible( int32 ChunkIndex, bool bVisible )
{
#if WITH_APEX
	// Bone 0 is a dummy root bone
	const int32 BoneIndex = ChunkIdxToBoneIdx(ChunkIndex);

	if( bVisible )
	{
		UnHideBone(BoneIndex);

		if (NULL != ApexDestructibleActor)
		{
			physx::PxShape** PShapes;
			const physx::PxU32 PShapeCount = ApexDestructibleActor->getChunkPhysXShapes(PShapes, ChunkIndex);
			if (PShapeCount > 0)
			{
				const physx::PxMat44 ChunkPoseRT = ApexDestructibleActor->getChunkPose(ChunkIndex);	// Unscaled
				const physx::PxTransform Transform(ChunkPoseRT);
				SetChunkWorldRT(ChunkIndex, P2UQuat(Transform.q), P2UVector(Transform.p));
			}
		}
	}
	else
	{
		HideBone(BoneIndex, PBO_None);
	}

	// Mark the transform as dirty, so the bounds are updated and sent to the render thread
	MarkRenderTransformDirty();

	// New bone positions need to be sent to render thread
	MarkRenderDynamicDataDirty();
#endif
}
void FConstraintInstance::GetConstraintForce(FVector& OutLinearForce, FVector& OutAngularForce)
{
	OutLinearForce = FVector::ZeroVector;
	OutAngularForce = FVector::ZeroVector;
#if WITH_PHYSX
	ExecuteOnUnbrokenJointReadOnly([&] (const PxD6Joint* Joint)
	{
		PxVec3 PxOutLinearForce;
		PxVec3 PxOutAngularForce;
		Joint->getConstraint()->getForce(PxOutLinearForce, PxOutAngularForce);

		OutLinearForce = P2UVector(PxOutLinearForce);
		OutAngularForce = P2UVector(PxOutAngularForce);
	});
#endif
}
static FVector FindHeightFieldOpposingNormal(const PxLocationHit& PHit, const FVector& TraceDirectionDenorm, const FVector InNormal)
{
	if (IsInvalidFaceIndex(PHit.faceIndex))
	{
		return InNormal;
	}

	PxHeightFieldGeometry PHeightFieldGeom;
	const bool bReadGeomSuccess = PHit.shape->getHeightFieldGeometry(PHeightFieldGeom);
	check(bReadGeomSuccess);	//we should only call this function when we have a heightfield
	if (PHeightFieldGeom.heightField)
	{
		const PxU32 TriIndex = PHit.faceIndex;
		const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor);

		PxTriangle Tri;
		PxMeshQuery::getTriangle(PHeightFieldGeom, PShapeWorldPose, TriIndex, Tri);

		PxVec3 TriNormal;
		Tri.normal(TriNormal);
		return P2UVector(TriNormal);
	}

	return InNormal;
}
示例#5
0
/** Get the position of this constraint in world space. */
FVector FConstraintInstance::GetConstraintLocation()
{
#if WITH_PHYSX
	PxD6Joint* Joint = (PxD6Joint*)	ConstraintData;
	if (!Joint)
	{
		return FVector::ZeroVector;
	}

	PxRigidActor* JointActor0, *JointActor1;
	Joint->getActors(JointActor0, JointActor1);

	PxVec3 JointPos(0);

	// get the first anchor point in global frame
	if(JointActor0)
	{
		JointPos = JointActor0->getGlobalPose().transform(Joint->getLocalPose(PxJointActorIndex::eACTOR0).p);
	}

	// get the second archor point in global frame
	if(JointActor1)
	{
		JointPos += JointActor1->getGlobalPose().transform(Joint->getLocalPose(PxJointActorIndex::eACTOR1).p);
	}

	JointPos *= 0.5f;
	
	return P2UVector(JointPos);

#else
	return FVector::ZeroVector;
#endif
}
示例#6
0
FBoxSphereBounds UDestructibleComponent::CalcBounds(const FTransform& LocalToWorld) const
{
#if WITH_APEX
	if( ApexDestructibleActor == NULL )
	{
		// Fallback if we don't have physics
		return Super::CalcBounds(LocalToWorld);
	}

	const PxBounds3& PBounds = ApexDestructibleActor->getBounds();

	return FBoxSphereBounds( FBox( P2UVector(PBounds.minimum), P2UVector(PBounds.maximum) ) );
#else	// #if WITH_APEX
	return Super::CalcBounds(LocalToWorld);
#endif	// #if WITH_APEX
}
void SDestructibleMeshEditorViewport::RefreshViewport()
{
	// Update chunk visibilities
#if WITH_APEX
#if WITH_EDITORONLY_DATA
	if (DestructibleMesh != NULL && DestructibleMesh->FractureSettings != NULL && DestructibleMesh->ApexDestructibleAsset != NULL && PreviewComponent->IsRegistered())
	{
		const NxRenderMeshAsset* ApexRenderMeshAsset = DestructibleMesh->ApexDestructibleAsset->getRenderMeshAsset();
		if (ApexRenderMeshAsset != NULL)
		{
			NxExplicitHierarchicalMesh& EHM =  DestructibleMesh->FractureSettings->ApexDestructibleAssetAuthoring->getExplicitHierarchicalMesh();
			if (DestructibleMesh->ApexDestructibleAsset->getPartIndex(0) < ApexRenderMeshAsset->getPartCount())
			{
				const PxBounds3& Level0Bounds = ApexRenderMeshAsset->getBounds(DestructibleMesh->ApexDestructibleAsset->getPartIndex(0));
				const PxVec3 Level0Center = !Level0Bounds.isEmpty() ? Level0Bounds.getCenter() : PxVec3(0.0f);
				for (uint32 ChunkIndex = 0; ChunkIndex < DestructibleMesh->ApexDestructibleAsset->getChunkCount(); ++ChunkIndex)
				{
					const uint32 PartIndex = DestructibleMesh->ApexDestructibleAsset->getPartIndex(ChunkIndex);

					if (PartIndex >= ApexRenderMeshAsset->getPartCount())
					{
						continue;
					}
					
					uint32 ChunkDepth = 0;			
					for (int32 ParentIndex = DestructibleMesh->ApexDestructibleAsset->getChunkParentIndex(ChunkIndex); 
						ParentIndex >= 0; 
						ParentIndex = DestructibleMesh->ApexDestructibleAsset->getChunkParentIndex(ParentIndex))
					{
						++ChunkDepth;
					}

					const bool bChunkVisible = ChunkDepth == PreviewDepth;
					PreviewComponent->SetChunkVisible(ChunkIndex, bChunkVisible);
					if (bChunkVisible)
					{
						const PxBounds3& ChunkBounds = ApexRenderMeshAsset->getBounds(PartIndex);
						const PxVec3 ChunkCenter = !ChunkBounds.isEmpty() ? ChunkBounds.getCenter() : PxVec3(0.0f);
						const PxVec3 Displacement = ExplodeAmount*(ChunkCenter - Level0Center);
						PreviewComponent->SetChunkWorldRT(ChunkIndex, FQuat(0.0f, 0.0f, 0.0f, 1.0f), P2UVector(Displacement));
					}
				}

				PreviewComponent->BoundsScale = 100;
				// Send bounds to render thread at end of frame
				PreviewComponent->UpdateComponentToWorld();
		
				// Send bones to render thread right now, so the invalidated display is rerendered with
				// uptodate information
				PreviewComponent->DoDeferredRenderUpdates_Concurrent();
			}
		}
	}
#endif // WITH_EDITORONLY_DATA
#endif // WITH_APEX

	// Invalidate the viewport's display.
	SceneViewport->InvalidateDisplay();
}
void UDestructibleComponent::RefreshBoneTransforms()
{
#if WITH_APEX
	if(ApexDestructibleActor != NULL && SkeletalMesh)
	{
		UDestructibleMesh* TheDestructibleMesh = GetDestructibleMesh();

		// Save a pointer to the APEX NxDestructibleAsset
		physx::NxDestructibleAsset* ApexDestructibleAsset = TheDestructibleMesh->ApexDestructibleAsset;
		check(ApexDestructibleAsset);

		{
			// Lock here so we don't encounter race conditions with the destruction processing
			FPhysScene* PhysScene = World->GetPhysicsScene();
			check(PhysScene);
			const uint32 SceneType = (BodyInstance.bUseAsyncScene && PhysScene->HasAsyncScene()) ? PST_Async : PST_Sync;
			PxScene* PScene = PhysScene->GetPhysXScene(SceneType);
			check(PScene);
			SCOPED_SCENE_WRITE_LOCK(PScene);
			SCOPED_SCENE_READ_LOCK(PScene);

			// Try to acquire event buffer
			const physx::NxDestructibleChunkEvent* EventBuffer;
			physx::PxU32 EventBufferSize;
			if (ApexDestructibleActor->acquireChunkEventBuffer(EventBuffer, EventBufferSize))
			{
				// Buffer acquired
				while (EventBufferSize--)
				{
					const physx::NxDestructibleChunkEvent& Event = *EventBuffer++;
					// Right now the only events are visibility changes.  So as an optimization we won't check for the event type.
	//				if (Event.event & physx::NxDestructibleChunkEvent::VisibilityChanged)
					const bool bVisible = (Event.event & physx::NxDestructibleChunkEvent::ChunkVisible) != 0;
					SetChunkVisible(Event.chunkIndex, bVisible);
				}
				// Release buffer (will be cleared)
				ApexDestructibleActor->releaseChunkEventBuffer();
			}
		}

		// Update poses for visible chunks
		const physx::PxU16* VisibleChunks = ApexDestructibleActor->getVisibleChunks();
		physx::PxU16 VisibleChunkCount = ApexDestructibleActor->getNumVisibleChunks();
		while (VisibleChunkCount--)
		{
			const physx::PxU16 ChunkIndex = *VisibleChunks++;
			// BRGTODO : Make a direct method to access the Px objects' quats
			const physx::PxMat44 ChunkPoseRT = ApexDestructibleActor->getChunkPose(ChunkIndex);	// Unscaled
			const physx::PxTransform Transform(ChunkPoseRT);
			SetChunkWorldRT(ChunkIndex, P2UQuat(Transform.q), P2UVector(Transform.p));
		}

		// Send bones to render thread at end of frame
		MarkRenderDynamicDataDirty();
}
#endif	// #if WITH_APEX
}
FMatrix PTransform2UMatrix(const PxTransform& PTM)
{
	FQuat UQuat = P2UQuat(PTM.q);
	FVector UPos = P2UVector(PTM.p);

	FMatrix Result = FQuatRotationTranslationMatrix(UQuat, UPos);

	return Result;
}
FTransform P2UTransform(const PxTransform& PTM)
{
	FQuat UQuat = P2UQuat(PTM.q);
	FVector UPos = P2UVector(PTM.p);

	FTransform Result = FTransform(UQuat, UPos);

	return Result;
}
示例#11
0
static void BatchPxRenderBufferLines(class ULineBatchComponent& LineBatcherToUse, const PxRenderBuffer& DebugData)
{
	int32 NumPoints = DebugData.getNbPoints();
	if (NumPoints > 0)
	{
		const PxDebugPoint* Points = DebugData.getPoints();
		for (int32 i = 0; i<NumPoints; i++)
		{
			LineBatcherToUse.DrawPoint(P2UVector(Points->pos), FColor((uint32)Points->color), 2, SDPG_World);

			Points++;
		}
	}

	// Build a list of all the lines we want to draw
	TArray<FBatchedLine> DebugLines;

	// Add all the 'lines' from PhysX
	int32 NumLines = DebugData.getNbLines();
	if (NumLines > 0)
	{
		const PxDebugLine* Lines = DebugData.getLines();
		for (int32 i = 0; i<NumLines; i++)
		{
			new(DebugLines)FBatchedLine(P2UVector(Lines->pos0), P2UVector(Lines->pos1), FColor((uint32)Lines->color0), 0.f, 0.0f, SDPG_World);
			Lines++;
		}
	}

	// Add all the 'triangles' from PhysX
	int32 NumTris = DebugData.getNbTriangles();
	if (NumTris > 0)
	{
		const PxDebugTriangle* Triangles = DebugData.getTriangles();
		for (int32 i = 0; i<NumTris; i++)
		{
			new(DebugLines)FBatchedLine(P2UVector(Triangles->pos0), P2UVector(Triangles->pos1), FColor((uint32)Triangles->color0), 0.f, 0.0f, SDPG_World);
			new(DebugLines)FBatchedLine(P2UVector(Triangles->pos1), P2UVector(Triangles->pos2), FColor((uint32)Triangles->color1), 0.f, 0.0f, SDPG_World);
			new(DebugLines)FBatchedLine(P2UVector(Triangles->pos2), P2UVector(Triangles->pos0), FColor((uint32)Triangles->color2), 0.f, 0.0f, SDPG_World);
			Triangles++;
		}
	}

	// Draw them all in one call.
	if (DebugLines.Num() > 0)
	{
		LineBatcherToUse.DrawLines(DebugLines);
	}
}
static FVector FindBoxOpposingNormal(const PxLocationHit& PHit, const FVector& TraceDirectionDenorm, const FVector InNormal)
{
	// We require normal info for our algorithm.
	const bool bNormalData = (PHit.flags & PxHitFlag::eNORMAL);
	if (!bNormalData)
	{
		return InNormal;
	}

	PxBoxGeometry PxBoxGeom;
	const bool bReadGeomSuccess = PHit.shape->getBoxGeometry(PxBoxGeom);
	check(bReadGeomSuccess); // This function should only be used for box geometry

	const PxTransform LocalToWorld = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor);
	
	// Find which faces were included in the contact normal, and for multiple faces, use the one most opposing the sweep direction.
	const PxVec3 ContactNormalLocal = LocalToWorld.rotateInv(PHit.normal);
	const float* ContactNormalLocalPtr = &ContactNormalLocal.x;
	const PxVec3 TraceDirDenormWorld = U2PVector(TraceDirectionDenorm);
	const float* TraceDirDenormWorldPtr = &TraceDirDenormWorld.x;
	const PxVec3 TraceDirDenormLocal = LocalToWorld.rotateInv(TraceDirDenormWorld);
	const float* TraceDirDenormLocalPtr = &TraceDirDenormLocal.x;

	PxVec3 BestLocalNormal(ContactNormalLocal);
	float* BestLocalNormalPtr = &BestLocalNormal.x;
	float BestOpposingDot = FLT_MAX;

	for (int32 i=0; i < 3; i++)
	{
		// Select axis of face to compare to, based on normal.
		if (ContactNormalLocalPtr[i] > KINDA_SMALL_NUMBER)
		{
			const float TraceDotFaceNormal = TraceDirDenormLocalPtr[i]; // TraceDirDenormLocal.dot(BoxFaceNormal)
			if (TraceDotFaceNormal < BestOpposingDot)
			{
				BestOpposingDot = TraceDotFaceNormal;
				BestLocalNormal = PxVec3(0.f);
				BestLocalNormalPtr[i] = 1.f;
			}
		}
		else if (ContactNormalLocalPtr[i] < -KINDA_SMALL_NUMBER)
		{
			const float TraceDotFaceNormal = -TraceDirDenormLocalPtr[i]; // TraceDirDenormLocal.dot(BoxFaceNormal)
			if (TraceDotFaceNormal < BestOpposingDot)
			{
				BestOpposingDot = TraceDotFaceNormal;
				BestLocalNormal = PxVec3(0.f);
				BestLocalNormalPtr[i] = -1.f;
			}
		}
	}

	// Fill in result
	const PxVec3 WorldNormal = LocalToWorld.rotate(BestLocalNormal);
	return P2UVector(WorldNormal);
}
示例#13
0
FVector UVehicleWheel::GetPhysicsLocation()
{
	if ( WheelShape )
	{
		PxVec3 PLocation = VehicleSim->PVehicle->getRigidDynamicActor()->getGlobalPose().transform( WheelShape->getLocalPose() ).p;
		return P2UVector( PLocation );
	}

	return FVector(0.0f);
}
示例#14
0
// NB: ElemTM is assumed to have no scaling in it!
void FKConvexElem::DrawElemWire(FPrimitiveDrawInterface* PDI, const FTransform& ElemTM, const FVector& Scale3D, const FColor Color)
{
#if WITH_PHYSX

	FTransform LocalToWorld = ElemTM;
	LocalToWorld.SetScale3D(Scale3D);

	PxConvexMesh* Mesh = ConvexMesh;

	if(Mesh)
	{
		// Draw each triangle that makes up the convex hull
		PxU32 NbVerts = Mesh->getNbVertices();
		const PxVec3* Vertices = Mesh->getVertices();
		
		TArray<FVector> TransformedVerts;
		TransformedVerts.AddUninitialized(NbVerts);
		for(PxU32 i=0; i<NbVerts; i++)
		{
			TransformedVerts[i] = LocalToWorld.TransformPosition( P2UVector(Vertices[i]) );
		}
						
		const PxU8* PIndexBuffer = Mesh->getIndexBuffer();
		PxU32 NbPolygons = Mesh->getNbPolygons();

		for(PxU32 i=0;i<NbPolygons;i++)
		{
			PxHullPolygon Data;
			bool bStatus = Mesh->getPolygonData(i, Data);
			check(bStatus);

			const PxU8* PIndices = PIndexBuffer + Data.mIndexBase;
		
			for(PxU16 j=0;j<Data.mNbVerts;j++)
			{
				// Get the verts that make up this line.
				int32 I0 = PIndices[j];
				int32 I1 = PIndices[j+1];

				// Loop back last and first vertices
				if(j==Data.mNbVerts - 1)
				{
					I1 = PIndices[0];
				}

				PDI->DrawLine( TransformedVerts[I0], TransformedVerts[I1], Color, SDPG_World );
			}
		}
	}
	else
	{
		UE_LOG(LogPhysics, Log, TEXT("FKConvexElem::DrawElemWire : No ConvexMesh, so unable to draw."));
	}
#endif // WITH_PHYSX
}
示例#15
0
void UDestructibleComponent::OnDamageEvent(const NxApexDamageEventReportData& InDamageEvent)
{
	FVector HitPosition = P2UVector(InDamageEvent.hitPosition);
	FVector HitDirection = P2UVector(InDamageEvent.hitDirection);

	OnComponentFracture.Broadcast(HitPosition, HitDirection);
	if (ADestructibleActor * DestructibleActor = Cast<ADestructibleActor>(GetOwner()))
	{
		DestructibleActor->OnActorFracture.Broadcast(HitPosition, HitDirection);
	}

	SpawnFractureEffectsFromDamageEvent(InDamageEvent);

	// After receiving damage, no longer receive decals.
	if (bReceivesDecals)
	{
		bReceivesDecals = false;
		MarkRenderStateDirty();
	}
}
示例#16
0
float FKConvexElem::GetVolume(const FVector& Scale) const
{
	float Volume = 0.0f;

#if WITH_PHYSX
	if (ConvexMesh != NULL)
	{
		// Preparation for convex mesh scaling implemented in another changelist
		FTransform ScaleTransform = FTransform(FQuat::Identity, FVector::ZeroVector, Scale);

		int32 NumPolys = ConvexMesh->getNbPolygons();
		PxHullPolygon PolyData;

		const PxVec3* Vertices = ConvexMesh->getVertices();
		const PxU8* Indices = ConvexMesh->getIndexBuffer();

		for (int32 PolyIdx = 0; PolyIdx < NumPolys; ++PolyIdx)
		{
			if (ConvexMesh->getPolygonData(PolyIdx, PolyData))
			{
				for (int32 VertIdx = 2; VertIdx < PolyData.mNbVerts; ++ VertIdx)
				{
					// Grab triangle indices that we hit
					int32 I0 = Indices[PolyData.mIndexBase + 0];
					int32 I1 = Indices[PolyData.mIndexBase + (VertIdx - 1)];
					int32 I2 = Indices[PolyData.mIndexBase + VertIdx];


					Volume += SignedVolumeOfTriangle(ScaleTransform.TransformPosition(P2UVector(Vertices[I0])), 
						ScaleTransform.TransformPosition(P2UVector(Vertices[I1])), 
						ScaleTransform.TransformPosition(P2UVector(Vertices[I2])));
				}
			}
		}
	}
#endif // WITH_PHYSX

	return Volume;
}
void FDestructibleMeshEditorViewportClient::Draw( const FSceneView* View,FPrimitiveDrawInterface* PDI )
{
	FEditorViewportClient::Draw(View, PDI);

#if WITH_APEX
	const bool DrawChunkMarker = true;

	UDestructibleComponent* Comp = PreviewDestructibleComp.Get();
	
	if (Comp)
	{
		if (Comp->DestructibleMesh != NULL && Comp->DestructibleMesh->FractureSettings != NULL)
		{
			if (Comp->DestructibleMesh->ApexDestructibleAsset != NULL)
			{
				NxDestructibleAsset* Asset = Comp->DestructibleMesh->ApexDestructibleAsset;
				const NxRenderMeshAsset* RenderMesh = Asset->getRenderMeshAsset();

				for (uint32 i=0; i < Asset->getChunkCount(); ++i)
				{
					int32 PartIdx = Asset->getPartIndex(i);
					int32 BoneIdx = i+1;

					if ( SelectedChunkIndices.Contains(i) )
					{
						PxBounds3 PBounds = RenderMesh->getBounds(PartIdx);

						FVector Center = P2UVector(PBounds.getCenter()) + Comp->GetBoneLocation(Comp->GetBoneName(BoneIdx));
						FVector Extent = P2UVector(PBounds.getExtents());

						FBox Bounds(Center - Extent, Center + Extent);
						DrawWireBox(PDI, Bounds, FColor::Blue, SDPG_World);
					}
				}
			}
		}
	}
#endif // WITH_APEX
}
示例#18
0
void UDestructibleComponent::SpawnFractureEffectsFromDamageEvent(const NxApexDamageEventReportData& InDamageEvent)
{
	// Use the component's fracture effects if the override is selected, otherwise use fracture effects from the asset
	TArray<FFractureEffect>& UseFractureEffects = (bFractureEffectOverride || !SkeletalMesh) ? FractureEffects : CastChecked<UDestructibleMesh>(SkeletalMesh)->FractureEffects;

	UDestructibleMesh* TheDestructibleMesh = GetDestructibleMesh();
	if (!TheDestructibleMesh)
	{
		return;
	}


	// We keep track of the handled parent chunks here
	TArray<int32> HandledParents;
	for(physx::PxU32 eventN = 0; eventN < InDamageEvent.fractureEventListSize; ++eventN)
	{
		const NxApexChunkData& chunkData = InDamageEvent.fractureEventList[eventN];
		if( chunkData.depth < (physx::PxU32)UseFractureEffects.Num() )
		{
			// We can get the root chunk here as well, so make sure that the parent index is 0, even for the root chunk
			int32 ParentIdx = FMath::Max(TheDestructibleMesh->ApexDestructibleAsset->getChunkParentIndex(chunkData.index), 0);
			
			// We can test a number of flags - we'll play an effect if the chunk was destroyed
			// As we only get the fractured event here for chunks that come free, we spawn fracture
			// effects only once per unique parent
			if((chunkData.flags & NxApexChunkFlag::FRACTURED) && !HandledParents.Contains(ParentIdx))
			{
				FVector Position = P2UVector(chunkData.worldBounds.getCenter());
				FFractureEffect& FractureEffect = UseFractureEffects[chunkData.depth];
				if( FractureEffect.Sound != NULL )
				{
					// Spawn sound
					UGameplayStatics::PlaySoundAtLocation( this, FractureEffect.Sound, Position );
				}
				if( FractureEffect.ParticleSystem != NULL )
				{
					// Spawn particle system
					UParticleSystemComponent* ParticleSystemComponent = UGameplayStatics::SpawnEmitterAtLocation( this, FractureEffect.ParticleSystem, Position );

					// Disable shadows, since destructibles tend to generate a lot of these
					if (ParticleSystemComponent != NULL)
					{
						ParticleSystemComponent->CastShadow = false;
					}
				}

				HandledParents.Add(ParentIdx);
			}			
		}
	}
}
示例#19
0
void FConstraintInstance::GetConstraintForce(FVector& OutLinearForce, FVector& OutAngularForce)
{
#if WITH_PHYSX
	if (PxD6Joint* Joint = GetUnbrokenJoint())
	{
		PxVec3 PxOutLinearForce;
		PxVec3 PxOutAngularForce;
		Joint->getConstraint()->getForce(PxOutLinearForce, PxOutAngularForce);

		OutLinearForce = P2UVector(PxOutLinearForce);
		OutAngularForce = P2UVector(PxOutAngularForce);
	}
	else
	{
		OutLinearForce = FVector::ZeroVector;
		OutAngularForce = FVector::ZeroVector;
	}

#else
	OutLinearForce = FVector::ZeroVector;
	OutAngularForce = FVector::ZeroVector;
#endif
}
void SetupDriveHelper(const UWheeledVehicleMovementComponent4W* VehicleData, const PxVehicleWheelsSimData* PWheelsSimData, PxVehicleDriveSimData4W& DriveData)
{
	PxVehicleDifferential4WData DifferentialSetup;
	GetVehicleDifferential4WSetup(VehicleData->DifferentialSetup, DifferentialSetup);
	
	DriveData.setDiffData(DifferentialSetup);

	PxVehicleEngineData EngineSetup;
	GetVehicleEngineSetup(VehicleData->EngineSetup, EngineSetup);
	DriveData.setEngineData(EngineSetup);

	PxVehicleClutchData ClutchSetup;
	ClutchSetup.mStrength = VehicleData->ClutchStrength;
	DriveData.setClutchData(ClutchSetup);

	FVector WheelCentreOffsets[4];
	WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eFRONT_LEFT] = P2UVector(PWheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_LEFT));
	WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT] = P2UVector(PWheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eFRONT_RIGHT));
	WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eREAR_LEFT] = P2UVector(PWheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_LEFT));
	WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eREAR_RIGHT] = P2UVector(PWheelsSimData->getWheelCentreOffset(PxVehicleDrive4WWheelOrder::eREAR_RIGHT));

	PxVehicleAckermannGeometryData AckermannSetup;
	AckermannSetup.mAccuracy = VehicleData->AckermannAccuracy;
	AckermannSetup.mAxleSeparation = FMath::Abs(WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eFRONT_LEFT].X - WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eREAR_LEFT].X);
	AckermannSetup.mFrontWidth = FMath::Abs(WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eFRONT_RIGHT].Y - WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eFRONT_LEFT].Y);
	AckermannSetup.mRearWidth = FMath::Abs(WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eREAR_RIGHT].Y - WheelCentreOffsets[PxVehicleDrive4WWheelOrder::eREAR_LEFT].Y);
	DriveData.setAckermannGeometryData(AckermannSetup);

	PxVehicleGearsData GearSetup;
	GetVehicleGearSetup(VehicleData->GearSetup, GearSetup);	
	DriveData.setGearsData(GearSetup);

	PxVehicleAutoBoxData AutoBoxSetup;
	GetVehicleAutoBoxSetup(VehicleData->AutoBoxSetup, AutoBoxSetup);
	DriveData.setAutoBoxData(AutoBoxSetup);
}
static FVector FindConvexMeshOpposingNormal(const PxLocationHit& PHit, const FVector& TraceDirectionDenorm, const FVector InNormal)
{
	if (IsInvalidFaceIndex(PHit.faceIndex))
	{
		return InNormal;
	}

	PxConvexMeshGeometry PConvexMeshGeom;
	bool bSuccess = PHit.shape->getConvexMeshGeometry(PConvexMeshGeom);
	check(bSuccess);	//should only call this function when we have a convex mesh

	if (PConvexMeshGeom.convexMesh)
	{
		check(PHit.faceIndex < PConvexMeshGeom.convexMesh->getNbPolygons());

		const PxU32 PolyIndex = PHit.faceIndex;
		PxHullPolygon PPoly;
		bool bSuccessData = PConvexMeshGeom.convexMesh->getPolygonData(PolyIndex, PPoly);
		if (bSuccessData)
		{
			// Account for non-uniform scale in local space normal.
			const PxVec3 PPlaneNormal(PPoly.mPlane[0], PPoly.mPlane[1], PPoly.mPlane[2]);
			const PxVec3 PLocalPolyNormal = TransformNormalToShapeSpace(PConvexMeshGeom.scale, PPlaneNormal.getNormalized());

			// Convert to world space
			const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor);
			const PxVec3 PWorldPolyNormal = PShapeWorldPose.rotate(PLocalPolyNormal);
			const FVector OutNormal = P2UVector(PWorldPolyNormal);

#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
			if (!OutNormal.IsNormalized())
			{
				UE_LOG(LogPhysics, Warning, TEXT("Non-normalized Normal (Hit shape is ConvexMesh): %s (LocalPolyNormal:%s)"), *OutNormal.ToString(), *P2UVector(PLocalPolyNormal).ToString());
				UE_LOG(LogPhysics, Warning, TEXT("WorldTransform \n: %s"), *P2UTransform(PShapeWorldPose).ToString());
			}
#endif
			return OutNormal;
		}
	}

	return InNormal;
}
static bool FindHeightFieldOpposingNormal(const PxLocationHit& PHit, const FVector& TraceDirectionDenorm, FVector& OutNormal)
{

    PxHeightFieldGeometry PHeightFieldGeom;
    bool bSuccess = PHit.shape->getHeightFieldGeometry(PHeightFieldGeom);
    check(bSuccess);	//we should only call this function when we have a heightfield
    if (PHeightFieldGeom.heightField)
    {
        const PxU32 TriIndex = PHit.faceIndex;
        const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor);

        PxTriangle Tri;
        PxMeshQuery::getTriangle(PHeightFieldGeom, PShapeWorldPose, TriIndex, Tri);

        PxVec3 TriNormal;
        Tri.normal(TriNormal);
        OutNormal = P2UVector(TriNormal);

        return true;
    }

    return false;
}
static bool ComputeInflatedMTD_Internal(const float MtdInflation, const PxLocationHit& PHit, FHitResult& OutResult, const PxTransform& QueryTM, const PxGeometry& Geom, const PxTransform& PShapeWorldPose)
{
	PxGeometry* InflatedGeom = NULL;

	PxVec3 PxMtdNormal(0.f);
	PxF32 PxMtdDepth = 0.f;
	const PxGeometry& POtherGeom = PHit.shape->getGeometry().any();
	const bool bMtdResult = PxGeometryQuery::computePenetration(PxMtdNormal, PxMtdDepth, Geom, QueryTM, POtherGeom, PShapeWorldPose);
	if (bMtdResult)
	{
		if (PxMtdNormal.isFinite())
		{
			OutResult.ImpactNormal = P2UVector(PxMtdNormal);
			OutResult.PenetrationDepth = FMath::Max(FMath::Abs(PxMtdDepth) - MtdInflation, 0.f) + KINDA_SMALL_NUMBER;
			return true;
		}
		else
		{
			UE_LOG(LogPhysics, Verbose, TEXT("Warning: ComputeInflatedMTD_Internal: MTD returned NaN :( normal: (X:%f, Y:%f, Z:%f)"), PxMtdNormal.x, PxMtdNormal.y, PxMtdNormal.z);
		}
	}

	return false;
}
/** Util to convert an overlapped shape into a sweep hit result, returns whether it was a blocking hit. */
static bool ConvertOverlappedShapeToImpactHit(const UWorld* World, const PxLocationHit& PHit, const FVector& StartLoc, const FVector& EndLoc, FHitResult& OutResult, const PxGeometry& Geom, const PxTransform& QueryTM, const PxFilterData& QueryFilter, bool bReturnPhysMat)
{
	SCOPE_CYCLE_COUNTER(STAT_CollisionConvertOverlapToHit);

	const PxShape* PShape = PHit.shape;
	const PxRigidActor* PActor = PHit.actor;
	const uint32 FaceIdx = PHit.faceIndex;

	// See if this is a 'blocking' hit
	PxFilterData PShapeFilter = PShape->getQueryFilterData();
	PxSceneQueryHitType::Enum HitType = FPxQueryFilterCallback::CalcQueryHitType(QueryFilter, PShapeFilter);
	const bool bBlockingHit = (HitType == PxSceneQueryHitType::eBLOCK); 
	OutResult.bBlockingHit = bBlockingHit;

	// Time of zero because initially overlapping
	OutResult.bStartPenetrating = true;
	OutResult.Time = 0.f;
	OutResult.Distance = 0.f;

	// Return start location as 'safe location'
	OutResult.Location = P2UVector(QueryTM.p);
	OutResult.ImpactPoint = OutResult.Location; // @todo not really sure of a better thing to do here...

	OutResult.TraceStart = StartLoc;
	OutResult.TraceEnd = EndLoc;

	const bool bFiniteNormal = PHit.normal.isFinite();
	const bool bValidNormal = (PHit.flags & PxHitFlag::eNORMAL) && bFiniteNormal;

	// Use MTD result if possible. We interpret the MTD vector as both the direction to move and the opposing normal.
	if (bValidNormal)
	{
		OutResult.ImpactNormal = P2UVector(PHit.normal);
		OutResult.PenetrationDepth = FMath::Abs(PHit.distance);
	}
	else
	{
		// Fallback normal if we can't find it with MTD or otherwise.
		OutResult.ImpactNormal = FVector::UpVector;
		OutResult.PenetrationDepth = 0.f;
		if (!bFiniteNormal)
		{
			UE_LOG(LogPhysics, Verbose, TEXT("Warning: ConvertOverlappedShapeToImpactHit: MTD returned NaN :( normal: (X:%f, Y:%f, Z:%f)"), PHit.normal.x, PHit.normal.y, PHit.normal.z);
		}
	}

#if DRAW_OVERLAPPING_TRIS
	if (CVarShowInitialOverlaps.GetValueOnAnyThread() != 0 && World && World->IsGameWorld())
	{
		FVector DummyNormal(0.f);
		const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PShape, *PActor);
		FindOverlappedTriangleNormal(World, Geom, QueryTM, PShape, PShapeWorldPose, DummyNormal, 0.f, true);
	}
#endif

	if (bBlockingHit)
	{
		// Zero-distance hits are often valid hits and we can extract the hit normal.
		// For invalid normals we can try other methods as well (get overlapping triangles).
		if (PHit.distance == 0.f || !bValidNormal)
		{
			const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PShape, *PActor);

			// Try MTD with a small inflation for better accuracy, then a larger one in case the first one fails due to precision issues.
			static const float SmallMtdInflation = 0.250f;
			static const float LargeMtdInflation = 1.750f;

			if (ComputeInflatedMTD(SmallMtdInflation, PHit, OutResult, QueryTM, Geom, PShapeWorldPose) ||
				ComputeInflatedMTD(LargeMtdInflation, PHit, OutResult, QueryTM, Geom, PShapeWorldPose))
			{
				// Success
			}
			else
			{
				static const float SmallOverlapInflation = 0.250f;
				if (FindOverlappedTriangleNormal(World, Geom, QueryTM, PShape, PShapeWorldPose, OutResult.ImpactNormal, 0.f, false) ||
					FindOverlappedTriangleNormal(World, Geom, QueryTM, PShape, PShapeWorldPose, OutResult.ImpactNormal, SmallOverlapInflation, false))
				{
					// Success
				}
				else
				{
					// MTD failed, use point distance. This is not ideal.
					// Note: faceIndex seems to be unreliable for convex meshes in these cases, so not using FindGeomOpposingNormal() for them here.
					PxGeometry& PGeom = PShape->getGeometry().any();
					PxVec3 PClosestPoint;
					const float Distance = PxGeometryQuery::pointDistance(QueryTM.p, PGeom, PShapeWorldPose, &PClosestPoint);

					if (Distance < KINDA_SMALL_NUMBER)
					{
						UE_LOG(LogCollision, Verbose, TEXT("Warning: ConvertOverlappedShapeToImpactHit: Query origin inside shape, giving poor MTD."));
						PClosestPoint = PxShapeExt::getWorldBounds(*PShape, *PActor).getCenter();
					}

					OutResult.ImpactNormal = (OutResult.Location - P2UVector(PClosestPoint)).GetSafeNormal();
				}
			}
		}
	}
	else
	{
		// non blocking hit (overlap).
		if (!bValidNormal)
		{
			OutResult.ImpactNormal = (StartLoc - EndLoc).GetSafeNormal();
			ensure(OutResult.ImpactNormal.IsNormalized());
		}
	}

	OutResult.Normal = OutResult.ImpactNormal;
	SetHitResultFromShapeAndFaceIndex(PShape, PActor, FaceIdx, OutResult, bReturnPhysMat);

	return bBlockingHit;
}
FVector FindBestOverlappingNormal(const UWorld* World, const PxGeometry& Geom, const PxTransform& QueryTM, const GeomType& ShapeGeom, const PxTransform& PShapeWorldPose, PxU32* HitTris, int32 NumTrisHit, bool bCanDrawOverlaps = false)
{
#if DRAW_OVERLAPPING_TRIS
	const float Lifetime = 5.f;
	bCanDrawOverlaps &= World && World->IsGameWorld() && World->PersistentLineBatcher && (World->PersistentLineBatcher->BatchedLines.Num() < 2048);
	if (bCanDrawOverlaps)
	{
		TArray<FOverlapResult> Overlaps;
		DrawGeomOverlaps(World, Geom, QueryTM, Overlaps, Lifetime);
	}
	const FLinearColor LineColor = FLinearColor::Green;
	const FLinearColor NormalColor = FLinearColor::Red;
	const FLinearColor PointColor = FLinearColor::Yellow;
#endif // DRAW_OVERLAPPING_TRIS

	// Track the best triangle plane distance
	float BestPlaneDist = -BIG_NUMBER;
	FVector BestPlaneNormal(0, 0, 1);
	// Iterate over triangles
	for (int32 TriIdx = 0; TriIdx < NumTrisHit; TriIdx++)
	{
		PxTriangle Tri;
		PxMeshQuery::getTriangle(ShapeGeom, PShapeWorldPose, HitTris[TriIdx], Tri);

		const FVector A = P2UVector(Tri.verts[0]);
		const FVector B = P2UVector(Tri.verts[1]);
		const FVector C = P2UVector(Tri.verts[2]);

		FVector TriNormal = ((B - A) ^ (C - A));
		TriNormal = TriNormal.GetSafeNormal();

		const FPlane TriPlane(A, TriNormal);

		const FVector QueryCenter = P2UVector(QueryTM.p);
		const float DistToPlane = TriPlane.PlaneDot(QueryCenter);

		if (DistToPlane > BestPlaneDist)
		{
			BestPlaneDist = DistToPlane;
			BestPlaneNormal = TriNormal;
		}

#if DRAW_OVERLAPPING_TRIS
		if (bCanDrawOverlaps && (World->PersistentLineBatcher->BatchedLines.Num() < 2048))
		{
			static const float LineThickness = 0.9f;
			static const float NormalThickness = 0.75f;
			static const float PointThickness = 5.0f;
			World->PersistentLineBatcher->DrawLine(A, B, LineColor, SDPG_Foreground, LineThickness, Lifetime);
			World->PersistentLineBatcher->DrawLine(B, C, LineColor, SDPG_Foreground, LineThickness, Lifetime);
			World->PersistentLineBatcher->DrawLine(C, A, LineColor, SDPG_Foreground, LineThickness, Lifetime);
			const FVector Centroid((A + B + C) / 3.f);
			World->PersistentLineBatcher->DrawLine(Centroid, Centroid + (35.0f*TriNormal), NormalColor, SDPG_Foreground, NormalThickness, Lifetime);
			World->PersistentLineBatcher->DrawPoint(Centroid + (35.0f*TriNormal), NormalColor, PointThickness, SDPG_Foreground, Lifetime);
			World->PersistentLineBatcher->DrawPoint(A, PointColor, PointThickness, SDPG_Foreground, Lifetime);
			World->PersistentLineBatcher->DrawPoint(B, PointColor, PointThickness, SDPG_Foreground, Lifetime);
			World->PersistentLineBatcher->DrawPoint(C, PointColor, PointThickness, SDPG_Foreground, Lifetime);
		}
#endif // DRAW_OVERLAPPING_TRIS
	}

	return BestPlaneNormal;
}
void ConvertQueryImpactHit(const UWorld* World, const PxLocationHit& PHit, FHitResult& OutResult, float CheckLength, const PxFilterData& QueryFilter, const FVector& StartLoc, const FVector& EndLoc, const PxGeometry* const Geom, const PxTransform& QueryTM, bool bReturnFaceIndex, bool bReturnPhysMat)
{
	SCOPE_CYCLE_COUNTER(STAT_ConvertQueryImpactHit);

	checkSlow(PHit.flags & PxHitFlag::eDISTANCE);
	const bool bInitialOverlap = PHit.hadInitialOverlap();
	if (bInitialOverlap && Geom != nullptr)
	{
		ConvertOverlappedShapeToImpactHit(World, PHit, StartLoc, EndLoc, OutResult, *Geom, QueryTM, QueryFilter, bReturnPhysMat);
		return;
	}

	// See if this is a 'blocking' hit
	const PxFilterData PShapeFilter = PHit.shape->getQueryFilterData();
	const PxSceneQueryHitType::Enum HitType = FPxQueryFilterCallback::CalcQueryHitType(QueryFilter, PShapeFilter);
	OutResult.bBlockingHit = (HitType == PxSceneQueryHitType::eBLOCK); 
	OutResult.bStartPenetrating = bInitialOverlap;

	// calculate the hit time
	const float HitTime = PHit.distance/CheckLength;
	OutResult.Time = HitTime;
	OutResult.Distance = PHit.distance;

	// figure out where the the "safe" location for this shape is by moving from the startLoc toward the ImpactPoint
	const FVector TraceStartToEnd = EndLoc - StartLoc;
	const FVector SafeLocationToFitShape = StartLoc + (HitTime * TraceStartToEnd);
	OutResult.Location = SafeLocationToFitShape;

	const bool bUsePxPoint = ((PHit.flags & PxHitFlag::ePOSITION) && !bInitialOverlap);
	OutResult.ImpactPoint = bUsePxPoint ? P2UVector(PHit.position) : StartLoc;
	
	// Caution: we may still have an initial overlap, but with null Geom. This is the case for RayCast results.
	const bool bUsePxNormal = ((PHit.flags & PxHitFlag::eNORMAL) && !bInitialOverlap);
	FVector Normal = bUsePxNormal ? P2UVector(PHit.normal).GetSafeNormal() : -TraceStartToEnd.GetSafeNormal();
	OutResult.Normal = Normal;
	OutResult.ImpactNormal = Normal;

	OutResult.TraceStart = StartLoc;
	OutResult.TraceEnd = EndLoc;


#if ENABLE_CHECK_HIT_NORMAL
	CheckHitResultNormal(OutResult, TEXT("Invalid Normal from ConvertQueryImpactHit"), StartLoc, EndLoc, Geom);
#endif // ENABLE_CHECK_HIT_NORMAL

	if (bUsePxNormal && !Normal.IsNormalized())
	{
		// TraceStartToEnd should never be zero, because of the length restriction in the raycast and sweep tests.
		Normal = -TraceStartToEnd.GetSafeNormal();
		OutResult.Normal = Normal;
		OutResult.ImpactNormal = Normal;
	}

	const PxGeometryType::Enum SweptGeometryType = Geom ? Geom->getType() : PxGeometryType::eINVALID;
	OutResult.ImpactNormal = FindGeomOpposingNormal(SweptGeometryType, PHit, TraceStartToEnd, Normal);
	
	// Fill in Actor, Component, material, etc.
	SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat);

	if( PHit.shape->getGeometryType() == PxGeometryType::eHEIGHTFIELD)
	{
		// Lookup physical material for heightfields
		if (bReturnPhysMat && PHit.faceIndex != InvalidQueryHit.faceIndex)
		{
			PxMaterial* HitMaterial = PHit.shape->getMaterialFromInternalFaceIndex(PHit.faceIndex);
			if (HitMaterial != NULL)
			{
				OutResult.PhysMaterial = FPhysxUserData::Get<UPhysicalMaterial>(HitMaterial->userData);
			}
		}
	}
	else
	if(bReturnFaceIndex && PHit.shape->getGeometryType() == PxGeometryType::eTRIANGLEMESH)
	{
		PxTriangleMeshGeometry PTriMeshGeom;
		if(	PHit.shape->getTriangleMeshGeometry(PTriMeshGeom) && 
			PTriMeshGeom.triangleMesh != NULL &&
			PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles() )
		{
			OutResult.FaceIndex	= PTriMeshGeom.triangleMesh->getTrianglesRemap()[PHit.faceIndex];
		}
	}
}
static FVector FindTriMeshOpposingNormal(const PxLocationHit& PHit, const FVector& TraceDirectionDenorm, const FVector InNormal)
{
	if (IsInvalidFaceIndex(PHit.faceIndex))
	{
		return InNormal;
	}

	PxTriangleMeshGeometry PTriMeshGeom;
	bool bSuccess = PHit.shape->getTriangleMeshGeometry(PTriMeshGeom);
	check(bSuccess);	//this function should only be called when we have a trimesh

	if (PTriMeshGeom.triangleMesh)
	{
		check(PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles());

		const PxU32 TriIndex = PHit.faceIndex;
		const void* Triangles = PTriMeshGeom.triangleMesh->getTriangles();

		// Grab triangle indices that we hit
		int32 I0, I1, I2;

		if (PTriMeshGeom.triangleMesh->getTriangleMeshFlags() & PxTriangleMeshFlag::eHAS_16BIT_TRIANGLE_INDICES)
		{
			PxU16* P16BitIndices = (PxU16*)Triangles;
			I0 = P16BitIndices[(TriIndex * 3) + 0];
			I1 = P16BitIndices[(TriIndex * 3) + 1];
			I2 = P16BitIndices[(TriIndex * 3) + 2];
		}
		else
		{
			PxU32* P32BitIndices = (PxU32*)Triangles;
			I0 = P32BitIndices[(TriIndex * 3) + 0];
			I1 = P32BitIndices[(TriIndex * 3) + 1];
			I2 = P32BitIndices[(TriIndex * 3) + 2];
		}

		// Get verts we hit (local space)
		const PxVec3* PVerts = PTriMeshGeom.triangleMesh->getVertices();
		const PxVec3 V0 = PVerts[I0];
		const PxVec3 V1 = PVerts[I1];
		const PxVec3 V2 = PVerts[I2];

		// Find normal of triangle (local space), and account for non-uniform scale
		const PxVec3 PTempNormal = ((V1 - V0).cross(V2 - V0)).getNormalized();
		const PxVec3 PLocalTriNormal = TransformNormalToShapeSpace(PTriMeshGeom.scale, PTempNormal);

		// Convert to world space
		const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor);
		const PxVec3 PWorldTriNormal = PShapeWorldPose.rotate(PLocalTriNormal);
		FVector OutNormal = P2UVector(PWorldTriNormal);

		if (PTriMeshGeom.meshFlags & PxMeshGeometryFlag::eDOUBLE_SIDED)
		{
			//double sided mesh so we need to consider direction of query
			const float sign = FVector::DotProduct(OutNormal, TraceDirectionDenorm) > 0.f ? -1.f : 1.f;
			OutNormal *= sign;
		}

#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
		if (!OutNormal.IsNormalized())
		{
			UE_LOG(LogPhysics, Warning, TEXT("Non-normalized Normal (Hit shape is TriangleMesh): %s (V0:%s, V1:%s, V2:%s)"), *OutNormal.ToString(), *P2UVector(V0).ToString(), *P2UVector(V1).ToString(), *P2UVector(V2).ToString());
			UE_LOG(LogPhysics, Warning, TEXT("WorldTransform \n: %s"), *P2UTransform(PShapeWorldPose).ToString());
		}
#endif
		return OutNormal;
	}

	return InNormal;
}
EConvertQueryResult ConvertQueryImpactHit(const UWorld* World, const PxLocationHit& PHit, FHitResult& OutResult, float CheckLength, const PxFilterData& QueryFilter, const FVector& StartLoc, const FVector& EndLoc, const PxGeometry* const Geom, const PxTransform& QueryTM, bool bReturnFaceIndex, bool bReturnPhysMat)
{
	SCOPE_CYCLE_COUNTER(STAT_ConvertQueryImpactHit);

#if WITH_EDITOR
	if(bReturnFaceIndex && World->IsGameWorld())
	{
		if(!ensure(UPhysicsSettings::Get()->bSuppressFaceRemapTable == false))
		{
			UE_LOG(LogPhysics, Error, TEXT("A scene query is relying on face indices, but bSuppressFaceRemapTable is true."));
			bReturnFaceIndex = false;
		}
	}
#endif

	checkSlow(PHit.flags & PxHitFlag::eDISTANCE);
	const bool bInitialOverlap = PHit.hadInitialOverlap();
	if (bInitialOverlap && Geom != nullptr)
	{
		ConvertOverlappedShapeToImpactHit(World, PHit, StartLoc, EndLoc, OutResult, *Geom, QueryTM, QueryFilter, bReturnPhysMat);
		return EConvertQueryResult::Valid;
	}

	// See if this is a 'blocking' hit
	const PxFilterData PShapeFilter = PHit.shape->getQueryFilterData();
	const PxQueryHitType::Enum HitType = FPxQueryFilterCallback::CalcQueryHitType(QueryFilter, PShapeFilter);
	OutResult.bBlockingHit = (HitType == PxQueryHitType::eBLOCK); 
	OutResult.bStartPenetrating = bInitialOverlap;

	// calculate the hit time
	const float HitTime = PHit.distance/CheckLength;
	OutResult.Time = HitTime;
	OutResult.Distance = PHit.distance;

	// figure out where the the "safe" location for this shape is by moving from the startLoc toward the ImpactPoint
	const FVector TraceStartToEnd = EndLoc - StartLoc;
	const FVector SafeLocationToFitShape = StartLoc + (HitTime * TraceStartToEnd);
	OutResult.Location = SafeLocationToFitShape;

	const bool bUsePxPoint = ((PHit.flags & PxHitFlag::ePOSITION) && !bInitialOverlap);
	if (bUsePxPoint && !PHit.position.isFinite())
	{
#if ENABLE_NAN_DIAGNOSTIC
		SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat);
		UE_LOG(LogCore, Error, TEXT("ConvertQueryImpactHit() NaN details:\n>> Actor:%s (%s)\n>> Component:%s\n>> Item:%d\n>> BoneName:%s\n>> Time:%f\n>> Distance:%f\n>> Location:%s\n>> bIsBlocking:%d\n>> bStartPenetrating:%d"),
			*GetNameSafe(OutResult.GetActor()), OutResult.Actor.IsValid() ? *OutResult.GetActor()->GetPathName() : TEXT("no path"),
			*GetNameSafe(OutResult.GetComponent()), OutResult.Item, *OutResult.BoneName.ToString(),
			OutResult.Time, OutResult.Distance, *OutResult.Location.ToString(), OutResult.bBlockingHit ? 1 : 0, OutResult.bStartPenetrating ? 1 : 0);
#endif // ENABLE_NAN_DIAGNOSTIC

		OutResult.Reset();
		logOrEnsureNanError(TEXT("ConvertQueryImpactHit() received NaN/Inf for position: %.2f %.2f %.2f"), PHit.position.x, PHit.position.y, PHit.position.z);
		return EConvertQueryResult::Invalid;
	}

	OutResult.ImpactPoint = bUsePxPoint ? P2UVector(PHit.position) : StartLoc;
	
	// Caution: we may still have an initial overlap, but with null Geom. This is the case for RayCast results.
	const bool bUsePxNormal = ((PHit.flags & PxHitFlag::eNORMAL) && !bInitialOverlap);
	if (bUsePxNormal && !PHit.normal.isFinite())
	{
#if ENABLE_NAN_DIAGNOSTIC
		SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat);
		UE_LOG(LogCore, Error, TEXT("ConvertQueryImpactHit() NaN details:\n>> Actor:%s (%s)\n>> Component:%s\n>> Item:%d\n>> BoneName:%s\n>> Time:%f\n>> Distance:%f\n>> Location:%s\n>> bIsBlocking:%d\n>> bStartPenetrating:%d"),
			*GetNameSafe(OutResult.GetActor()), OutResult.Actor.IsValid() ? *OutResult.GetActor()->GetPathName() : TEXT("no path"),
			*GetNameSafe(OutResult.GetComponent()), OutResult.Item, *OutResult.BoneName.ToString(),
			OutResult.Time, OutResult.Distance, *OutResult.Location.ToString(), OutResult.bBlockingHit ? 1 : 0, OutResult.bStartPenetrating ? 1 : 0);
#endif // ENABLE_NAN_DIAGNOSTIC

		OutResult.Reset();
		logOrEnsureNanError(TEXT("ConvertQueryImpactHit() received NaN/Inf for normal: %.2f %.2f %.2f"), PHit.normal.x, PHit.normal.y, PHit.normal.z);
		return EConvertQueryResult::Invalid;
	}

	FVector Normal = bUsePxNormal ? P2UVector(PHit.normal).GetSafeNormal() : -TraceStartToEnd.GetSafeNormal();
	OutResult.Normal = Normal;
	OutResult.ImpactNormal = Normal;

	OutResult.TraceStart = StartLoc;
	OutResult.TraceEnd = EndLoc;


#if ENABLE_CHECK_HIT_NORMAL
	CheckHitResultNormal(OutResult, TEXT("Invalid Normal from ConvertQueryImpactHit"), StartLoc, EndLoc, Geom);
#endif // ENABLE_CHECK_HIT_NORMAL

	if (bUsePxNormal && !Normal.IsNormalized())
	{
		// TraceStartToEnd should never be zero, because of the length restriction in the raycast and sweep tests.
		Normal = -TraceStartToEnd.GetSafeNormal();
		OutResult.Normal = Normal;
		OutResult.ImpactNormal = Normal;
	}

	const PxGeometryType::Enum SweptGeometryType = Geom ? Geom->getType() : PxGeometryType::eINVALID;
	OutResult.ImpactNormal = FindGeomOpposingNormal(SweptGeometryType, PHit, TraceStartToEnd, Normal);

	// Fill in Actor, Component, material, etc.
	SetHitResultFromShapeAndFaceIndex(PHit.shape, PHit.actor, PHit.faceIndex, OutResult, bReturnPhysMat);

	PxGeometryType::Enum PGeomType = PHit.shape->getGeometryType();

	if(PGeomType == PxGeometryType::eHEIGHTFIELD)
	{
		// Lookup physical material for heightfields
		if (bReturnPhysMat && PHit.faceIndex != InvalidQueryHit.faceIndex)
		{
			PxMaterial* HitMaterial = PHit.shape->getMaterialFromInternalFaceIndex(PHit.faceIndex);
			if (HitMaterial != NULL)
			{
				OutResult.PhysMaterial = FPhysxUserData::Get<UPhysicalMaterial>(HitMaterial->userData);
			}
		}
	}
	else if (bReturnFaceIndex && PGeomType == PxGeometryType::eTRIANGLEMESH)
	{
		PxTriangleMeshGeometry PTriMeshGeom;
		if(	PHit.shape->getTriangleMeshGeometry(PTriMeshGeom) && 
			PTriMeshGeom.triangleMesh != NULL &&
			PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles() )
		{
			if (const PxU32* TriangleRemap = PTriMeshGeom.triangleMesh->getTrianglesRemap())
			{
				OutResult.FaceIndex	= TriangleRemap[PHit.faceIndex];
			}
		}
	}

	return EConvertQueryResult::Valid;
}
void FPhysXSimEventCallback::onContact(const PxContactPairHeader& PairHeader, const PxContactPair* Pairs, PxU32 NumPairs)
{
	// Check actors are not destroyed
	if( PairHeader.flags & (PxContactPairHeaderFlag::eREMOVED_ACTOR_0 | PxContactPairHeaderFlag::eREMOVED_ACTOR_1) )
	{
		UE_LOG(LogPhysics, Log, TEXT("%d onContact(): Actors have been deleted!"), GFrameCounter );
		return;
	}

	const PxActor* PActor0 = PairHeader.actors[0];
	const PxActor* PActor1 = PairHeader.actors[1];
	check(PActor0 && PActor1);

	const PxRigidBody* PRigidBody0 = PActor0->is<PxRigidBody>();
	const PxRigidBody* PRigidBody1 = PActor1->is<PxRigidBody>();

	const FBodyInstance* BodyInst0 = FPhysxUserData::Get<FBodyInstance>(PActor0->userData);
	const FBodyInstance* BodyInst1 = FPhysxUserData::Get<FBodyInstance>(PActor1->userData);
	
	bool bEitherDestructible = false;

	// check if it's a destructible actor
	if (BodyInst0 == NULL)
	{
		if (const FDestructibleChunkInfo* DestructibleChunkInfo = FPhysxUserData::Get<FDestructibleChunkInfo>(PActor0->userData))
		{
			bEitherDestructible = true;
			BodyInst0 = DestructibleChunkInfo->OwningComponent.IsValid() ? &DestructibleChunkInfo->OwningComponent->BodyInstance : NULL;
		}
	}

	if (BodyInst1 == NULL)
	{
		if (const FDestructibleChunkInfo* DestructibleChunkInfo = FPhysxUserData::Get<FDestructibleChunkInfo>(PActor1->userData))
		{
			bEitherDestructible = true;
			BodyInst1 = DestructibleChunkInfo->OwningComponent.IsValid() ? &DestructibleChunkInfo->OwningComponent->BodyInstance : NULL;
		}
	}

	//if nothing valid just exit
	//if a destructible mesh you can get chunks that hit other chunks from the same body... this causes a lot of spam and doesn't seem like a very useful notification so I'm turning it off
	if(BodyInst0 == NULL || BodyInst1 == NULL || BodyInst0 == BodyInst1)
	{
		return;
	}

	//destruction applies damage when it hits something. Unfortunately it relies on the same flag that generates onContact.
	//We only want onContact events to happen if the user actually selected bNotifyRigidBodyCollision so we have to check if this is the case
	if (bEitherDestructible)
	{
		if (BodyInst0->bNotifyRigidBodyCollision == false && BodyInst1->bNotifyRigidBodyCollision == false)
		{
			return;
		}
	}

	TArray<FCollisionNotifyInfo>& PendingCollisionNotifies = OwningScene->GetPendingCollisionNotifies(SceneType);

	uint32 PreAddingCollisionNotify = PendingCollisionNotifies.Num() - 1;
	TArray<int32> PairNotifyMapping = FBodyInstance::AddCollisionNotifyInfo(BodyInst0, BodyInst1, Pairs, NumPairs, PendingCollisionNotifies);

	// Iterate through contact points
	for(uint32 PairIdx=0; PairIdx<NumPairs; PairIdx++)
	{
		int32 NotifyIdx = PairNotifyMapping[PairIdx];
		if (NotifyIdx == -1)	//the body instance this pair belongs to is not listening for events
		{
			continue;
		}

		FCollisionNotifyInfo * NotifyInfo = &PendingCollisionNotifies[NotifyIdx];
		FCollisionImpactData* ImpactInfo = &(NotifyInfo->RigidCollisionData);

		const PxContactPair* Pair = Pairs + PairIdx;

		// Get the two shapes that are involved in the collision
		const PxShape* Shape0 = Pair->shapes[0];
		check(Shape0);
		const PxShape* Shape1 = Pair->shapes[1];
		check(Shape1);

		// Get materials
		PxMaterial* Material0 = nullptr;
		UPhysicalMaterial* PhysMat0  = nullptr;
		if(Shape0->getNbMaterials() == 1)	//If we have simple geometry or only 1 material we set it here. Otherwise do it per face
		{
			Shape0->getMaterials(&Material0, 1);		
			PhysMat0 = Material0 ? FPhysxUserData::Get<UPhysicalMaterial>(Material0->userData) : nullptr;
		}

		PxMaterial* Material1 = nullptr;
		UPhysicalMaterial* PhysMat1  = nullptr;
		if (Shape1->getNbMaterials() == 1)	//If we have simple geometry or only 1 material we set it here. Otherwise do it per face
		{
			Shape1->getMaterials(&Material1, 1);
			PhysMat1 = Material1 ? FPhysxUserData::Get<UPhysicalMaterial>(Material1->userData) : nullptr;
		}

		// Iterate over contact points
		PxContactPairPoint ContactPointBuffer[16];
		int32 NumContactPoints = Pair->extractContacts(ContactPointBuffer, 16);
		for(int32 PointIdx=0; PointIdx<NumContactPoints; PointIdx++)
		{
			const PxContactPairPoint& Point = ContactPointBuffer[PointIdx];

			const PxVec3 NormalImpulse = Point.impulse.dot(Point.normal) * Point.normal; // project impulse along normal
			ImpactInfo->TotalNormalImpulse += P2UVector(NormalImpulse);
			ImpactInfo->TotalFrictionImpulse += P2UVector(Point.impulse - NormalImpulse); // friction is component not along contact normal

			// Get per face materials
			if(!Material0)	//there is complex geometry or multiple materials so resolve the physical material here
			{
				if(PxMaterial* Material0PerFace = Shape0->getMaterialFromInternalFaceIndex(Point.internalFaceIndex0))
				{
					PhysMat0 = FPhysxUserData::Get<UPhysicalMaterial>(Material0PerFace->userData);
				}
			}

			if (!Material1)	//there is complex geometry or multiple materials so resolve the physical material here
			{
				if(PxMaterial* Material1PerFace = Shape1->getMaterialFromInternalFaceIndex(Point.internalFaceIndex1))
				{
					PhysMat1 = FPhysxUserData::Get<UPhysicalMaterial>(Material1PerFace->userData);
				}
				
			}
			
			new(ImpactInfo->ContactInfos) FRigidBodyContactInfo(
				P2UVector(Point.position), 
				P2UVector(Point.normal), 
				-1.f * Point.separation, 
				PhysMat0, 
				PhysMat1);
		}	
	}

	for (int32 NotifyIdx = PreAddingCollisionNotify + 1; NotifyIdx < PendingCollisionNotifies.Num(); NotifyIdx++)
	{
		FCollisionNotifyInfo * NotifyInfo = &PendingCollisionNotifies[NotifyIdx];
		FCollisionImpactData* ImpactInfo = &(NotifyInfo->RigidCollisionData);
		// Discard pairs that don't generate any force (eg. have been rejected through a modify contact callback).
		if (ImpactInfo->TotalNormalImpulse.SizeSquared() < KINDA_SMALL_NUMBER)
		{
			PendingCollisionNotifies.RemoveAt(NotifyIdx);
			NotifyIdx--;
		}
	}
}
/** Util to convert an overlapped shape into a sweep hit result, returns whether it was a blocking hit. */
static bool ConvertOverlappedShapeToImpactHit(const PxShape* PShape,  const PxRigidActor* PActor, const FVector& StartLoc, const FVector& EndLoc, FHitResult& OutResult, const PxGeometry& Geom, const PxTransform& QueryTM, const PxFilterData& QueryFilter, bool bReturnPhysMat, uint32 FaceIdx)
{
	OutResult.TraceStart = StartLoc;
	OutResult.TraceEnd = EndLoc;

	SetHitResultFromShapeAndFaceIndex(PShape, PActor, FaceIdx, OutResult, bReturnPhysMat);

	// Time of zero because initially overlapping
	OutResult.Time = 0.f;
	OutResult.bStartPenetrating = true;

	// See if this is a 'blocking' hit
	PxFilterData PShapeFilter = PShape->getQueryFilterData();
	PxSceneQueryHitType::Enum HitType = FPxQueryFilterCallback::CalcQueryHitType(QueryFilter, PShapeFilter);
	OutResult.bBlockingHit = (HitType == PxSceneQueryHitType::eBLOCK); 

	// Return start location as 'safe location'
	OutResult.Location = P2UVector(QueryTM.p);
	OutResult.ImpactPoint = OutResult.Location; // @todo not really sure of a better thing to do here...

	const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PShape, *PActor); 


	PxTriangleMeshGeometry PTriMeshGeom;
	if(PShape->getTriangleMeshGeometry(PTriMeshGeom))
	{
		PxU32 HitTris[64];
		bool bOverflow = false;
		int32 NumTrisHit = PxMeshQuery::findOverlapTriangleMesh(Geom, QueryTM, PTriMeshGeom, PShapeWorldPose, HitTris, 64, 0, bOverflow);

#if DRAW_OVERLAPPING_TRIS
		TArray<FOverlapResult> Overlaps;
		DrawGeomOverlaps(World, Geom, QueryTM, Overlaps);

		TArray<FBatchedLine> Lines;
		const FLinearColor LineColor = FLinearColor(1.f,0.7f,0.7f);
		const FLinearColor NormalColor = FLinearColor(1.f,1.f,1.f);
		const float Lifetime = 5.f;
#endif // DRAW_OVERLAPPING_TRIS

		// Track the best triangle plane distance
		float BestPlaneDist = -BIG_NUMBER;
		FVector BestPlaneNormal(0,0,1);
		FVector BestPointOnPlane(0,0,0);
		// Iterate over triangles
		for(int32 TriIdx = 0; TriIdx<NumTrisHit; TriIdx++)
		{
			PxTriangle Tri;
			PxMeshQuery::getTriangle(PTriMeshGeom, PShapeWorldPose, HitTris[TriIdx], Tri);

			const FVector A = P2UVector(Tri.verts[0]);
			const FVector B = P2UVector(Tri.verts[1]);
			const FVector C = P2UVector(Tri.verts[2]);

			FVector TriNormal = ((B-A) ^ (C-A));

			// Use a more accurate normalization that avoids InvSqrtEst
			const float TriNormalSize = TriNormal.Size();
			TriNormal = (TriNormalSize >= KINDA_SMALL_NUMBER ? TriNormal/TriNormalSize : FVector::ZeroVector);

			const FPlane TriPlane(A, TriNormal);

			const FVector QueryCenter = P2UVector(QueryTM.p);
			const float DistToPlane = TriPlane.PlaneDot(QueryCenter);

			if(DistToPlane > BestPlaneDist)
			{
				BestPlaneDist = DistToPlane;
				BestPlaneNormal = TriNormal;
				BestPointOnPlane = A;
			}

#if DRAW_OVERLAPPING_TRIS
			Lines.Add(FBatchedLine(A, B, LineColor, Lifetime, 0.1f, SDPG_Foreground));
			Lines.Add(FBatchedLine(B, C, LineColor, Lifetime, 0.1f, SDPG_Foreground));
			Lines.Add(FBatchedLine(C, A, LineColor, Lifetime, 0.1f, SDPG_Foreground));
			Lines.Add(FBatchedLine(A, A+(50.f*TriNormal), NormalColor, Lifetime, 0.1f, SDPG_Foreground));
#endif // DRAW_OVERLAPPING_TRIS
		}

#if DRAW_OVERLAPPING_TRIS
		if ( World->PersistentLineBatcher )
		{
			World->PersistentLineBatcher->DrawLines(Lines);
		}
#endif // DRAW_OVERLAPPING_TRIS

		OutResult.ImpactNormal = BestPlaneNormal;
	}
	else
	{
		// use vector center of shape to query as good direction to move in
		PxGeometry& PGeom = PShape->getGeometry().any();
		PxVec3 PClosestPoint;
		float Distance = PxGeometryQuery::pointDistance(QueryTM.p, PGeom, PShapeWorldPose, &PClosestPoint);

		if(Distance < KINDA_SMALL_NUMBER)
		{
			//UE_LOG(LogCollision, Warning, TEXT("ConvertOverlappedShapeToImpactHit: Query origin inside shape, giving poor MTD."));			
			PClosestPoint = PxShapeExt::getWorldBounds(*PShape, *PActor).getCenter(); 
		}

		OutResult.ImpactNormal = (OutResult.Location - P2UVector(PClosestPoint)).SafeNormal();
	}

	// Compute depenetration vector and distance if possible.
	PxVec3 PxMtdNormal(0.f);
	PxF32 PxMtdDepth = 0.f;
	PxGeometry& POtherGeom = PShape->getGeometry().any();

	const bool bMtdResult = PxGeometryQuery::computePenetration(PxMtdNormal, PxMtdDepth, Geom, QueryTM, POtherGeom, PShapeWorldPose);
	if (bMtdResult)
	{
		const FVector MtdNormal = P2UVector(PxMtdNormal);
		OutResult.Normal = MtdNormal;
		OutResult.PenetrationDepth = FMath::Abs(PxMtdDepth) + KINDA_SMALL_NUMBER; // TODO: why are we getting negative values here from mtd sometimes?
	}
	else
	{
		OutResult.Normal = OutResult.ImpactNormal;
	}

	return OutResult.bBlockingHit;
}