Esempio n. 1
0
void FAnimNode_Trail::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms)
{
	check(OutBoneTransforms.Num() == 0);

	if( ChainLength < 2 )
	{
		return;
	}

	// The incoming BoneIndex is the 'end' of the spline chain. We need to find the 'start' by walking SplineLength bones up hierarchy.
	// Fail if we walk past the root bone.

	const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer();
	FCompactPoseBoneIndex WalkBoneIndex = TrailBone.GetCompactPoseIndex(BoneContainer);

	TArray<FCompactPoseBoneIndex> ChainBoneIndices;
	ChainBoneIndices.AddZeroed(ChainLength);

	ChainBoneIndices[ChainLength - 1] = WalkBoneIndex;

	for (int32 i = 1; i < ChainLength; i++)
	{
		// returns to avoid a crash
		// @TODO : shows an error message why failed
		if (WalkBoneIndex == 0)
		{
			return;
		}

		// Get parent bone.
		WalkBoneIndex = BoneContainer.GetParentBoneIndex(WalkBoneIndex);

		//Insert indices at the start of array, so that parents are before children in the array.
		int32 TransformIndex = ChainLength - (i + 1);
		ChainBoneIndices[TransformIndex] = WalkBoneIndex;
	}

	OutBoneTransforms.AddZeroed(ChainLength);

	// If we have >0 this frame, but didn't last time, record positions of all the bones.
	// Also do this if number has changed or array is zero.
	bool bHasValidStrength = (Alpha > 0.f);
	if(TrailBoneLocations.Num() != ChainLength || (bHasValidStrength && !bHadValidStrength))
	{
		TrailBoneLocations.Empty();
		TrailBoneLocations.AddZeroed(ChainLength);

		for(int32 i=0; i<ChainBoneIndices.Num(); i++)
		{
			FCompactPoseBoneIndex ChildIndex = ChainBoneIndices[i];
			const FTransform& ChainTransform = MeshBases.GetComponentSpaceTransform(ChildIndex);
			TrailBoneLocations[i] = ChainTransform.GetTranslation();
		}
		OldLocalToWorld = SkelComp->GetTransformMatrix();
	}
	bHadValidStrength = bHasValidStrength;

	// transform between last frame and now.
	FMatrix OldToNewTM = OldLocalToWorld * SkelComp->GetTransformMatrix().InverseFast();

	// Add fake velocity if present to all but root bone
	if(!FakeVelocity.IsZero())
	{
		FVector FakeMovement = -FakeVelocity * ThisTimstep;

		if (bActorSpaceFakeVel && SkelComp->GetOwner())
		{
			const FTransform BoneToWorld(SkelComp->GetOwner()->GetActorQuat(), SkelComp->GetOwner()->GetActorLocation());
			FakeMovement = BoneToWorld.TransformVector(FakeMovement);
		}

		FakeMovement = SkelComp->GetTransformMatrix().InverseTransformVector(FakeMovement);
		// Then add to each bone
		for(int32 i=1; i<TrailBoneLocations.Num(); i++)
		{
			TrailBoneLocations[i] += FakeMovement;
		}
	}

	// Root bone of trail is not modified.
	FCompactPoseBoneIndex RootIndex = ChainBoneIndices[0]; 
	const FTransform& ChainTransform = MeshBases.GetComponentSpaceTransform(RootIndex);
	OutBoneTransforms[0] = FBoneTransform(RootIndex, ChainTransform);
	TrailBoneLocations[0] = ChainTransform.GetTranslation();

	// Starting one below head of chain, move bones.
	for(int32 i=1; i<ChainBoneIndices.Num(); i++)
	{
		// Parent bone position in component space.
		FCompactPoseBoneIndex ParentIndex = ChainBoneIndices[i - 1];
		FVector ParentPos = TrailBoneLocations[i-1];
		FVector ParentAnimPos = MeshBases.GetComponentSpaceTransform(ParentIndex).GetTranslation();

		// Child bone position in component space.
		FCompactPoseBoneIndex ChildIndex = ChainBoneIndices[i];
		FVector ChildPos = OldToNewTM.TransformPosition(TrailBoneLocations[i]); // move from 'last frames component' frame to 'this frames component' frame
		FVector ChildAnimPos = MeshBases.GetComponentSpaceTransform(ChildIndex).GetTranslation();

		// Desired parent->child offset.
		FVector TargetDelta = (ChildAnimPos - ParentAnimPos);

		// Desired child position.
		FVector ChildTarget = ParentPos + TargetDelta;

		// Find vector from child to target
		FVector Error = ChildTarget - ChildPos;

		// Calculate how much to push the child towards its target
		float Correction = FMath::Clamp<float>(ThisTimstep * TrailRelaxation, 0.f, 1.f);

		// Scale correction vector and apply to get new world-space child position.
		TrailBoneLocations[i] = ChildPos + (Error * Correction);

		// If desired, prevent bones stretching too far.
		if(bLimitStretch)
		{
			float RefPoseLength = TargetDelta.Size();
			FVector CurrentDelta = TrailBoneLocations[i] - TrailBoneLocations[i-1];
			float CurrentLength = CurrentDelta.Size();

			// If we are too far - cut it back (just project towards parent particle).
			if( (CurrentLength - RefPoseLength > StretchLimit) && CurrentLength > SMALL_NUMBER )
			{
				FVector CurrentDir = CurrentDelta / CurrentLength;
				TrailBoneLocations[i] = TrailBoneLocations[i-1] + (CurrentDir * (RefPoseLength + StretchLimit));
			}
		}

		// Modify child matrix
		OutBoneTransforms[i] = FBoneTransform(ChildIndex, MeshBases.GetComponentSpaceTransform(ChildIndex));
		OutBoneTransforms[i].Transform.SetTranslation(TrailBoneLocations[i]);

		// Modify rotation of parent matrix to point at this one.

		// Calculate the direction that parent bone is currently pointing.
		FVector CurrentBoneDir = OutBoneTransforms[i-1].Transform.TransformVector( GetAlignVector(ChainBoneAxis, bInvertChainBoneAxis) );
		CurrentBoneDir = CurrentBoneDir.GetSafeNormal(SMALL_NUMBER);

		// Calculate vector from parent to child.
		FVector NewBoneDir = FVector(OutBoneTransforms[i].Transform.GetTranslation() - OutBoneTransforms[i - 1].Transform.GetTranslation()).GetSafeNormal(SMALL_NUMBER);

		// Calculate a quaternion that gets us from our current rotation to the desired one.
		FQuat DeltaLookQuat = FQuat::FindBetween(CurrentBoneDir, NewBoneDir);
		FTransform DeltaTM( DeltaLookQuat, FVector(0.f) );

		// Apply to the current parent bone transform.
		FTransform TmpMatrix = FTransform::Identity;
		TmpMatrix.CopyRotationPart(OutBoneTransforms[i - 1].Transform);
		TmpMatrix = TmpMatrix * DeltaTM;
		OutBoneTransforms[i - 1].Transform.CopyRotationPart(TmpMatrix);
	}

	// For the last bone in the chain, use the rotation from the bone above it.
	OutBoneTransforms[ChainLength - 1].Transform.CopyRotationPart(OutBoneTransforms[ChainLength - 2].Transform);

	// Update OldLocalToWorld
	OldLocalToWorld = SkelComp->GetTransformMatrix();
}
FVector UTKMathFunctionLibrary::GridSnap(FVector A, float Grid)
{
	return A.GridSnap(Grid);
}
Esempio n. 3
0
FVector ABxtAsteroidSpawner::GetRandomSpawnDirection() const
{
	const FVector direction(-1.0f, 0.0, 0.0f);
	const float angle = FMath::FRandRange(-45.0f, 45.0f);
	return direction.RotateAngleAxis(angle, FVector(0.0f, 0.0f, 1.0f));
}
void AGameplayDebuggingHUDComponent::DrawEQSData(APlayerController* PC, class UGameplayDebuggingComponent *DebugComponent)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && WITH_EQS
	PrintString(DefaultContext, TEXT("\n{green}EQS {white}[Use + key to switch query]\n"));

	if (DebugComponent->EQSLocalData.Num() == 0)
	{
		return;
	}

	const int32 EQSIndex = DebugComponent->EQSLocalData.Num() > 0 ? FMath::Clamp(DebugComponent->CurrentEQSIndex, 0, DebugComponent->EQSLocalData.Num() - 1) : INDEX_NONE;
	if (!DebugComponent->EQSLocalData.IsValidIndex(EQSIndex))
	{
		return;
	}

	{
		int32 Index = 0;
		PrintString(DefaultContext, TEXT("{white}Queries: "));
		for (auto CurrentQuery : DebugComponent->EQSLocalData)
		{
			if (EQSIndex == Index)
			{
				PrintString(DefaultContext, FString::Printf(TEXT("{green}%s, "), *CurrentQuery.Name));
			}
			else
			{
				PrintString(DefaultContext, FString::Printf(TEXT("{yellow}%s, "), *CurrentQuery.Name));
			}
			Index++;
		}
		PrintString(DefaultContext, TEXT("\n"));
	}

	auto& CurrentLocalData = DebugComponent->EQSLocalData[EQSIndex];

	/** find and draw item selection */
	int32 BestItemIndex = INDEX_NONE;
	{
		APlayerController* const MyPC = Cast<APlayerController>(PlayerOwner);
		FVector CamLocation;
		FVector FireDir;
		if (!MyPC->GetSpectatorPawn())
		{
			FRotator CamRotation;
			MyPC->GetPlayerViewPoint(CamLocation, CamRotation);
			FireDir = CamRotation.Vector();
		}
		else
		{
			FireDir = DefaultContext.Canvas->SceneView->GetViewDirection();
			CamLocation = DefaultContext.Canvas->SceneView->ViewMatrices.ViewOrigin;
		}

		float bestAim = 0;
		for (int32 Index = 0; Index < CurrentLocalData.RenderDebugHelpers.Num(); ++Index)
		{
			auto& CurrentItem = CurrentLocalData.RenderDebugHelpers[Index];

			const FVector AimDir = CurrentItem.Location - CamLocation;
			float FireDist = AimDir.SizeSquared();

			FireDist = FMath::Sqrt(FireDist);
			float newAim = FireDir | AimDir;
			newAim = newAim / FireDist;
			if (newAim > bestAim)
			{
				BestItemIndex = Index;
				bestAim = newAim;
			}
		}

		if (BestItemIndex != INDEX_NONE)
		{
			DrawDebugSphere(World, CurrentLocalData.RenderDebugHelpers[BestItemIndex].Location, CurrentLocalData.RenderDebugHelpers[BestItemIndex].Radius, 8, FColor::Red, false);
			int32 FailedTestIndex = CurrentLocalData.RenderDebugHelpers[BestItemIndex].FailedTestIndex;
			if (FailedTestIndex != INDEX_NONE)
			{
				PrintString(DefaultContext, FString::Printf(TEXT("{red}Selected item failed with test %d: {yellow}%s {LightBlue}(%s)\n")
					, FailedTestIndex
					, *CurrentLocalData.Tests[FailedTestIndex].ShortName
					, *CurrentLocalData.Tests[FailedTestIndex].Detailed
					));
				PrintString(DefaultContext, FString::Printf(TEXT("{white}'%s' with score %3.3f\n\n"), *CurrentLocalData.RenderDebugHelpers[BestItemIndex].AdditionalInformation, CurrentLocalData.RenderDebugHelpers[BestItemIndex].FailedScore));
			}
		}
	}

	PrintString(DefaultContext, FString::Printf(TEXT("{white}Timestamp: {yellow}%.3f (%.2fs ago)\n")
		, CurrentLocalData.Timestamp, PC->GetWorld()->GetTimeSeconds() - CurrentLocalData.Timestamp
		));
	PrintString(DefaultContext, FString::Printf(TEXT("{white}Query ID: {yellow}%d\n")
		, CurrentLocalData.Id
		));

	PrintString(DefaultContext, FString::Printf(TEXT("{white}Query contains %d options: "), CurrentLocalData.Options.Num()));
	for (int32 OptionIndex = 0; OptionIndex < CurrentLocalData.Options.Num(); ++OptionIndex)
	{
		if (OptionIndex == CurrentLocalData.UsedOption)
		{
			PrintString(DefaultContext, FString::Printf(TEXT("{green}%s, "), *CurrentLocalData.Options[OptionIndex]));
		}
		else
		{
			PrintString(DefaultContext, FString::Printf(TEXT("{yellow}%s, "), *CurrentLocalData.Options[OptionIndex]));
		}
	}
	PrintString(DefaultContext, TEXT("\n"));

	const float RowHeight = 20.0f;
	const int32 NumTests = CurrentLocalData.Tests.Num();
	if (CurrentLocalData.NumValidItems > 0 && GetDebuggingReplicator()->EnableEQSOnHUD )
	{
		// draw test weights for best X items
		const int32 NumItems = CurrentLocalData.Items.Num();

		FCanvasTileItem TileItem(FVector2D(0, 0), GWhiteTexture, FVector2D(Canvas->SizeX, RowHeight), FLinearColor::Black);
		FLinearColor ColorOdd(0, 0, 0, 0.6f);
		FLinearColor ColorEven(0, 0, 0.4f, 0.4f);
		TileItem.BlendMode = SE_BLEND_Translucent;

		// table header		
		{
			DefaultContext.CursorY += RowHeight;
			const float HeaderY = DefaultContext.CursorY + 3.0f;
			TileItem.SetColor(ColorOdd);
			DrawItem(DefaultContext, TileItem, 0, DefaultContext.CursorY);

			float HeaderX = DefaultContext.CursorX;
			PrintString(DefaultContext, FColor::Yellow, FString::Printf(TEXT("Num items: %d"), CurrentLocalData.NumValidItems), HeaderX, HeaderY);
			HeaderX += ItemDescriptionWidth;

			PrintString(DefaultContext, FColor::White, TEXT("Score"), HeaderX, HeaderY);
			HeaderX += ItemScoreWidth;

			for (int32 TestIdx = 0; TestIdx < NumTests; TestIdx++)
			{
				PrintString(DefaultContext, FColor::White, FString::Printf(TEXT("Test %d"), TestIdx), HeaderX, HeaderY);
				HeaderX += TestScoreWidth;
			}

			DefaultContext.CursorY += RowHeight;
		}

		// valid items
		for (int32 Idx = 0; Idx < NumItems; Idx++)
		{
			TileItem.SetColor((Idx % 2) ? ColorOdd : ColorEven);
			DrawItem(DefaultContext, TileItem, 0, DefaultContext.CursorY);

			DrawEQSItemDetails(Idx, DebugComponent);
			DefaultContext.CursorY += RowHeight;
		}
		DefaultContext.CursorY += RowHeight;
	}

	// test description
	PrintString(DefaultContext, TEXT("All tests from used option:\n"));
	for (int32 TestIdx = 0; TestIdx < NumTests; TestIdx++)
	{
		FString TestDesc = FString::Printf(TEXT("{white}Test %d = {yellow}%s {LightBlue}(%s)\n"), TestIdx,
			*CurrentLocalData.Tests[TestIdx].ShortName,
			*CurrentLocalData.Tests[TestIdx].Detailed);

		PrintString(DefaultContext, TestDesc);
	}

#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
Esempio n. 5
0
static void
dLogSum(double g, const FVector &v, FVector &r)
{
  assert(v.size() <= r.size());
  dLogSum(g, v, r, v.size());
}
/**
 * @return		true if the delta was handled by this editor mode tool.
 */
bool FModeTool_Texture::InputDelta(FEditorViewportClient* InViewportClient,FViewport* InViewport,FVector& InDrag,FRotator& InRot,FVector& InScale)
{
	if( InViewportClient->GetCurrentWidgetAxis() == EAxisList::None )
	{
		return false;
	}

	// calculate delta drag for this tick for the call to GEditor->polyTexPan below which is using relative (delta) mode
	FVector deltaDrag = InDrag;
	if (true == InViewportClient->IsPerspective())
	{
		// perspective viewports pass the absolute drag so subtract the last tick's drag value to get the delta
		deltaDrag -= PreviousInputDrag;
	}
	PreviousInputDrag = InDrag;

	if( !deltaDrag.IsZero() )
	{
		// Ensure each polygon has a unique base point index.
		for( FConstLevelIterator Iterator = InViewportClient->GetWorld()->GetLevelIterator(); Iterator; ++Iterator )
		{
			UModel* Model = (*Iterator)->Model;
			for(int32 SurfaceIndex = 0;SurfaceIndex < Model->Surfs.Num();SurfaceIndex++)
			{
				FBspSurf& Surf = Model->Surfs[SurfaceIndex];

				if(Surf.PolyFlags & PF_Selected)
				{
					const FVector Base = Model->Points[Surf.pBase];
					Surf.pBase = Model->Points.Add(Base);
				}
			}
			FMatrix Mat = GLevelEditorModeTools().GetCustomDrawingCoordinateSystem();
			FVector UVW = Mat.InverseTransformVector( deltaDrag );  // InverseTransformNormal because Mat is the transform from the surface/widget's coords to world coords
			GEditor->polyTexPan( Model, UVW.X, UVW.Y, 0 );  // 0 is relative mode because UVW is made from deltaDrag - the user input since the last tick
		}
	}

	if( !InRot.IsZero() )
	{
		const FRotationMatrix RotationMatrix( InRot );

		// Ensure each polygon has unique texture vector indices.
		for ( TSelectedSurfaceIterator<> It(InViewportClient->GetWorld()) ; It ; ++It )
		{
			FBspSurf* Surf = *It;
			UModel* Model = It.GetModel();

			FVector	TextureU = Model->Vectors[Surf->vTextureU];
			FVector TextureV = Model->Vectors[Surf->vTextureV];

			TextureU = RotationMatrix.TransformPosition( TextureU );
			TextureV = RotationMatrix.TransformPosition( TextureV );

			Surf->vTextureU = Model->Vectors.Add(TextureU);
			Surf->vTextureV = Model->Vectors.Add(TextureV);

			const bool bUpdateTexCoords = true;
			const bool bOnlyRefreshSurfaceMaterials = true;
			GEditor->polyUpdateMaster(Model, It.GetSurfaceIndex(), bUpdateTexCoords, bOnlyRefreshSurfaceMaterials);
		}
	}

	if( !InScale.IsZero() )
	{
		float ScaleU = InScale.X / GEditor->GetGridSize();
		float ScaleV = InScale.Y / GEditor->GetGridSize();

		ScaleU = 1.f - (ScaleU / 100.f);
		ScaleV = 1.f - (ScaleV / 100.f);

		// Ensure each polygon has unique texture vector indices.
		for( FConstLevelIterator Iterator = InViewportClient->GetWorld()->GetLevelIterator(); Iterator; ++Iterator )
		{
			UModel* Model = (*Iterator)->Model;
			for(int32 SurfaceIndex = 0;SurfaceIndex < Model->Surfs.Num();SurfaceIndex++)
			{
				FBspSurf& Surf = Model->Surfs[SurfaceIndex];
				if(Surf.PolyFlags & PF_Selected)
				{
					const FVector TextureU = Model->Vectors[Surf.vTextureU];
					const FVector TextureV = Model->Vectors[Surf.vTextureV];

					Surf.vTextureU = Model->Vectors.Add(TextureU);
					Surf.vTextureV = Model->Vectors.Add(TextureV);
				}
			}
			GEditor->polyTexScale( Model, ScaleU, 0.f, 0.f, ScaleV, false );
		}

	}

	return true;
}
void UFlareSpacecraftDamageSystem::OnCollision(class AActor* Other, FVector HitLocation, FVector NormalImpulse)
{
	// If receive hit from over actor, like a ship we must apply collision damages.
	// The applied damage energy is 0.2% of the kinetic energy of the other actor. The kinetic
	// energy is calculated from the relative speed between the 2 actors, and only with the relative
	// speed projected in the axis of the collision normal: if 2 very fast ship only slightly touch,
	// only few energy will be decipated by the impact.
	//
	// The damages are applied only to the current actor, the ReceiveHit method of the other actor
	// will also call an it will apply its collision damages itself.

	// If the other actor is a projectile, specific weapon damage code is done in the projectile hit
	// handler: in this case we ignore the collision
	AFlareShell* OtherProjectile = Cast<AFlareShell>(Other);
	if (OtherProjectile)
	{
		return;
	}

	// No primitive component, ignore
	UPrimitiveComponent* OtherRoot = Cast<UPrimitiveComponent>(Other->GetRootComponent());
	if (!OtherRoot)
	{
		return;
	}

	// Ignore debris
	AStaticMeshActor* OtherActor = Cast<AStaticMeshActor>(Other);
	if (OtherActor)
	{
		if (OtherActor->GetName().StartsWith("Debris"))
		{
			return;
		}
	}

	// Relative velocity
	FVector DeltaVelocity = ((OtherRoot->GetPhysicsLinearVelocity() - Spacecraft->Airframe->GetPhysicsLinearVelocity()) / 100);

	// Compute the relative velocity in the impact axis then compute kinetic energy
	/*float ImpactSpeed = DeltaVelocity.Size();
	float ImpactMass = FMath::Min(Spacecraft->GetSpacecraftMass(), OtherRoot->GetMass());
	*/

	//200 m /s -> 6301.873047 * 20000 -> 300 / 2 damage

	float ImpactSpeed = 0;
	float ImpactEnergy = 0;
	float ImpactMass = Spacecraft->GetSpacecraftMass();

	// Check if the mass was set and is valid
	if (ImpactMass > KINDA_SMALL_NUMBER)
	{
		ImpactSpeed = NormalImpulse.Size() / (ImpactMass * 100.f);
		ImpactEnergy = ImpactMass * ImpactSpeed / 8402.f;
	}

	float  Radius = 0.2 + FMath::Sqrt(ImpactEnergy) * 0.11;
	//FLOGV("OnCollision %s", *Spacecraft->GetImmatriculation().ToString());
	//FLOGV("  OtherRoot->GetPhysicsLinearVelocity()=%s", *OtherRoot->GetPhysicsLinearVelocity().ToString());
	//FLOGV("  OtherRoot->GetPhysicsLinearVelocity().Size()=%f", OtherRoot->GetPhysicsLinearVelocity().Size());
	//FLOGV("  Spacecraft->Airframe->GetPhysicsLinearVelocity()=%s", *Spacecraft->Airframe->GetPhysicsLinearVelocity().ToString());
	//FLOGV("  Spacecraft->Airframe->GetPhysicsLinearVelocity().Size()=%f", Spacecraft->Airframe->GetPhysicsLinearVelocity().Size());
	//FLOGV("  dot=%f", FVector::DotProduct(DeltaVelocity.GetUnsafeNormal(), HitNormal.GetUnsafeNormal()));
	/*FLOGV("  DeltaVelocity=%s", *DeltaVelocity.ToString());
	FLOGV("  ImpactSpeed=%f", ImpactSpeed);
	FLOGV("  ImpactMass=%f", ImpactMass);
	FLOGV("  ImpactEnergy=%f", ImpactEnergy);
	FLOGV("  Radius=%f", Radius);*/



	bool HasHit = false;
	FHitResult BestHitResult;
	float BestHitDistance = 0;

	for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++)
	{
		UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[ComponentIndex]);
		if (Component)
		{
			FHitResult HitResult(ForceInit);
			FCollisionQueryParams TraceParams(FName(TEXT("Fragment Trace")), true);
			TraceParams.bTraceComplex = true;
			TraceParams.bReturnPhysicalMaterial = false;
			Component->LineTraceComponent(HitResult, HitLocation, HitLocation + Spacecraft->GetLinearVelocity().GetUnsafeNormal() * 10000, TraceParams);

			if (HitResult.Actor.IsValid()){
				float HitDistance = (HitResult.Location - HitLocation).Size();
				if (!HasHit || HitDistance < BestHitDistance)
				{
					BestHitDistance = HitDistance;
					BestHitResult = HitResult;
				}

				//FLOGV("Collide hit %s at a distance=%f", *Component->GetReadableName(), HitDistance);
				HasHit = true;
			}
		}

	}

	if (HasHit)
	{
		//DrawDebugLine(Spacecraft->GetWorld(), HitLocation, BestHitResult.Location, FColor::Magenta, true);
	}
	else
	{
		int32 BestComponentIndex = -1;
		BestHitDistance = 0;

		for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++)
		{
			UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[ComponentIndex]);
			if (Component)
			{
				float ComponentDistance = (Component->GetComponentLocation() - HitLocation).Size();
				if (BestComponentIndex == -1 || BestHitDistance > ComponentDistance)
				{
					BestComponentIndex = ComponentIndex;
					BestHitDistance = ComponentDistance;
				}
			}
		}
		UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[BestComponentIndex]);


		FCollisionQueryParams TraceParams(FName(TEXT("Fragment Trace")), true);
		TraceParams.bTraceComplex = true;
		TraceParams.bReturnPhysicalMaterial = false;
		Component->LineTraceComponent(BestHitResult, HitLocation, Component->GetComponentLocation(), TraceParams);
		//DrawDebugLine(Spacecraft->GetWorld(), HitLocation, BestHitResult.Location, FColor::Yellow, true);


	}

	AFlareSpacecraft* OtherSpacecraft = Cast<AFlareSpacecraft>(Other);
	UFlareCompany* DamageSource = NULL;
	if (OtherSpacecraft)
	{
		DamageSource = OtherSpacecraft->GetParent()->GetCompany();
		LastDamageCauser = OtherSpacecraft;
	}
	else
	{
		LastDamageCauser = NULL;
	}
	ApplyDamage(ImpactEnergy, Radius, BestHitResult.Location, EFlareDamage::DAM_Collision, DamageSource);
}
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;
}
/* Validate Normal of OutResult. We're on hunt for invalid normal */
static void CheckHitResultNormal(const FHitResult& OutResult, const TCHAR* Message, const FVector& Start=FVector::ZeroVector, const FVector& End = FVector::ZeroVector, const PxGeometry* const Geom=NULL)
{
	if(!OutResult.bStartPenetrating && !OutResult.Normal.IsNormalized())
	{
		UE_LOG(LogPhysics, Warning, TEXT("(%s) Non-normalized OutResult.Normal from hit conversion: %s (Component- %s)"), Message, *OutResult.Normal.ToString(), *GetNameSafe(OutResult.Component.Get()));
		UE_LOG(LogPhysics, Warning, TEXT("Start Loc(%s), End Loc(%s), Hit Loc(%s), ImpactNormal(%s)"), *Start.ToString(), *End.ToString(), *OutResult.Location.ToString(), *OutResult.ImpactNormal.ToString() );
		if (Geom != NULL)
		{
			if (Geom->getType() == PxGeometryType::eCAPSULE)
			{
				const PxCapsuleGeometry * Capsule = (PxCapsuleGeometry*)Geom;
				UE_LOG(LogPhysics, Warning, TEXT("Capsule radius (%f), Capsule Halfheight (%f)"), Capsule->radius, Capsule->halfHeight);
			}
		}
		ensure(OutResult.Normal.IsNormalized());
	}
}
void UInterpToMovementComponent::AddControlPointPosition(FVector Pos)
{
	UE_LOG(LogInterpToMovementComponent, Warning, TEXT("Pos:%s"),*Pos.ToString());
	ControlPoints.Add( FInterpControlPoint(Pos));
}
Esempio n. 11
0
//RickH - We could probably significantly improve speed if we put separate Z checks in place and did everything else in 2D.
FVector UAvoidanceManager::GetAvoidanceVelocity_Internal(const FNavAvoidanceData& inAvoidanceData, float DeltaTime, int32* inIgnoreThisUID)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	if (!bSystemActive)
	{
		return inAvoidanceData.Velocity;
	}
#endif
	if (DeltaTime <= 0.0f)
	{
		return inAvoidanceData.Velocity;
	}

	FVector ReturnVelocity = inAvoidanceData.Velocity * DeltaTime;
	float MaxSpeed = ReturnVelocity.Size2D();
	float CurrentTime;

	UWorld* MyWorld = Cast<UWorld>(GetOuter());
	if (MyWorld)
	{
		CurrentTime = MyWorld->TimeSeconds;
	}
	else
	{
		//No world? OK, just quietly back out and don't alter anything.
		return inAvoidanceData.Velocity;
	}

	bool Unobstructed = true;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	bool DebugMode = IsDebugOnForAll() || (inIgnoreThisUID ? IsDebugOnForUID(*inIgnoreThisUID) : false);
#endif

	//If we're moving very slowly, just push forward. Not sure it's worth avoiding at this speed, though I could be wrong.
	if (MaxSpeed < 0.01f)
	{
		return inAvoidanceData.Velocity;
	}
	AllCones.Empty(AllCones.Max());

	//DrawDebugDirectionalArrow(GetWorld(), inAvoidanceData.Center, inAvoidanceData.Center + inAvoidanceData.Velocity, 2.5f, FColor(0,255,255), true, 0.05f, SDPG_MAX);

	for (auto& AvoidanceObj : AvoidanceObjects)
	{
		if ((inIgnoreThisUID) && (*inIgnoreThisUID == AvoidanceObj.Key))
		{
			continue;
		}
		FNavAvoidanceData& OtherObject = AvoidanceObj.Value;

		//
		//Start with a few fast-rejects
		//

		//If the object has expired, ignore it
		if (OtherObject.ShouldBeIgnored())
		{
			continue;
		}

		//If other object is not in avoided group, ignore it
		if (inAvoidanceData.ShouldIgnoreGroup(OtherObject.GroupMask))
		{
			continue;
		}

		//RickH - We should have a max-radius parameter/option here, so I'm just going to hardcode one for now.
		//if ((OtherObject.Radius + _AvoidanceData.Radius + MaxSpeed + OtherObject.Velocity.Size2D()) < FVector::Dist(OtherObject.Center, _AvoidanceData.Center))
		if (FVector2D(OtherObject.Center - inAvoidanceData.Center).SizeSquared() > FMath::Square(TestRadius2D))
		{
			continue;
		}

		if (FMath::Abs(OtherObject.Center.Z - inAvoidanceData.Center.Z) > TestHeightDifference)
		{
			continue;
		}

		//If we are moving away from the obstacle, ignore it. Even if we're the slower one, let the other obstacle path around us.
		if ((ReturnVelocity | (OtherObject.Center - inAvoidanceData.Center)) <= 0.0f)
		{
			continue;
		}

		//Create data for the avoidance routine
		{
			FVector PointAWorld = inAvoidanceData.Center;
			FVector PointBRelative = OtherObject.Center - PointAWorld;
			FVector TowardB, SidewaysFromB;
			FVector VelAdjustment;
			FVector VelAfterAdjustment;
			float RadiusB = OtherObject.Radius + inAvoidanceData.Radius;

			PointBRelative.Z = 0.0f;
			TowardB = PointBRelative.SafeNormal2D();		//Don't care about height for this game. Rough height-checking will come in later, but even then it will be acceptable to do this.
			if (TowardB.IsZero())
			{
				//Already intersecting, or aligned vertically, scrap this whole object.
				continue;
			}
			SidewaysFromB.Set(-TowardB.Y, TowardB.X, 0.0f);

			//Build collision cone (two planes) and store for later use. We might consider some fast rejection here to see if we can skip the cone entirely.
			//RickH - If we built these cones in 2D, we could avoid all the cross-product matrix stuff and just use (y, -x) 90-degree rotation.
			{
				FVector PointPlane[2];
				FVector EffectiveVelocityB;
				FVelocityAvoidanceCone NewCone;

				//Use RVO (as opposed to VO) only for objects that are not overridden to max weight AND that are currently moving toward us.
				if ((OtherObject.OverrideWeightTime <= CurrentTime) && ((OtherObject.Velocity|PointBRelative) < 0.0f))
				{
					float OtherWeight = (OtherObject.Weight + (1.0f - inAvoidanceData.Weight)) * 0.5f;			//Use the average of what the other wants to be and what we want it to be.
					EffectiveVelocityB = ((inAvoidanceData.Velocity * (1.0f - OtherWeight)) + (OtherObject.Velocity * OtherWeight)) * DeltaTime;
				}
				else
				{
					EffectiveVelocityB = OtherObject.Velocity * DeltaTime;		//This is equivalent to VO (not RVO) because the other object is not going to reciprocate our avoidance.
				}
				checkSlow(EffectiveVelocityB.Z == 0.0f);

				//Make the left plane
				PointPlane[0] = EffectiveVelocityB + (PointBRelative + (SidewaysFromB * RadiusB));
				PointPlane[1].Set(PointPlane[0].X, PointPlane[0].Y, PointPlane[0].Z + 100.0f);
				NewCone.ConePlane[0] = FPlane(EffectiveVelocityB, PointPlane[0], PointPlane[1]);		//First point is relative to A, which is ZeroVector in this implementation
				checkSlow((((PointBRelative+EffectiveVelocityB)|NewCone.ConePlane[0]) - NewCone.ConePlane[0].W) > 0.0f);
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
				if (DebugMode)
				{
//					DrawDebugDirectionalArrow(MyWorld, EffectiveVelocityB + PointAWorld, PointPlane[0] + PointAWorld, 50.0f, FColor(64,64,64), true, 0.05f, SDPG_MAX);
//					DrawDebugLine(MyWorld, PointAWorld, EffectiveVelocityB + PointAWorld, FColor(64,64,64), true, 0.05f, SDPG_MAX, 5.0f);
				}
#endif

				//Make the right plane
				PointPlane[0] = EffectiveVelocityB + (PointBRelative - (SidewaysFromB * RadiusB));
				PointPlane[1].Set(PointPlane[0].X, PointPlane[0].Y, PointPlane[0].Z - 100.0f);
				NewCone.ConePlane[1] = FPlane(EffectiveVelocityB, PointPlane[0], PointPlane[1]);		//First point is relative to A, which is ZeroVector in this implementation
				checkSlow((((PointBRelative+EffectiveVelocityB)|NewCone.ConePlane[1]) - NewCone.ConePlane[1].W) > 0.0f);
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
				if (DebugMode)
				{
//					DrawDebugDirectionalArrow(MyWorld, EffectiveVelocityB + PointAWorld, PointPlane[0] + PointAWorld, 50.0f, FColor(64,64,64), true, 0.05f, SDPG_MAX);
				}
#endif

				if ((((ReturnVelocity|NewCone.ConePlane[0]) - NewCone.ConePlane[0].W) > 0.0f)
					&& (((ReturnVelocity|NewCone.ConePlane[1]) - NewCone.ConePlane[1].W) > 0.0f))
				{
					Unobstructed = false;
				}

				AllCones.Add(NewCone);
			}
		}
	}
	if (Unobstructed)
	{
		//Trivial case, our ideal velocity is available.
		return inAvoidanceData.Velocity;
	}

	//Find a good velocity that isn't inside a cone.
	if (AllCones.Num())
	{
		float AngleCurrent;
		float AngleF = ReturnVelocity.HeadingAngle();
		float BestScore = 0.0f;
		float BestScorePotential;
		FVector BestVelocity = FVector::ZeroVector;		//Worst case is we just stand completely still. Should we also allow backing up? Should we test standing still?
		const int AngleCount = 4;		//Every angle will be tested both right and left.
		float AngleOffset[AngleCount] = {FMath::DegreesToRadians<float>(23.0f), FMath::DegreesToRadians<float>(40.0f), FMath::DegreesToRadians<float>(55.0f), FMath::DegreesToRadians<float>(85.0f)};
		FVector AngleVector[AngleCount<<1];

		//Determine check angles
		for (int i = 0; i < AngleCount; ++i)
		{
			AngleCurrent = AngleF - AngleOffset[i];
			AngleVector[(i<<1)].Set(FMath::Cos(AngleCurrent), FMath::Sin(AngleCurrent), 0.0f);
			AngleCurrent = AngleF + AngleOffset[i];
			AngleVector[(i<<1) + 1].Set(FMath::Cos(AngleCurrent), FMath::Sin(AngleCurrent), 0.0f);
		}

		//Sample velocity-space destination points and drag them back to form lines
		for (int AngleToCheck = 0; AngleToCheck < (AngleCount<<1); ++AngleToCheck)
		{
			FVector VelSpacePoint = AngleVector[AngleToCheck] * MaxSpeed;

			//Skip testing if we know we can't possibly get a better score than what we have already.
			//Note: This assumes the furthest point is the highest-scoring value (i.e. VelSpacePoint is not moving backward relative to ReturnVelocity)
			BestScorePotential = (VelSpacePoint|ReturnVelocity) * (VelSpacePoint|VelSpacePoint);
			if (BestScorePotential > BestScore)
			{
				FVector CandidateVelocity = AvoidCones(AllCones, FVector::ZeroVector, VelSpacePoint, AllCones.Num());
				float CandidateScore = (CandidateVelocity|ReturnVelocity) * (CandidateVelocity|CandidateVelocity);

				//Vectors are rated by their length and their overall forward movement.
				if (CandidateScore > BestScore)
				{
					BestScore = CandidateScore;
					BestVelocity = CandidateVelocity;
				}
			}
		}
		ReturnVelocity = BestVelocity;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
		if (DebugMode)
		{
			DrawDebugDirectionalArrow(MyWorld, inAvoidanceData.Center + inAvoidanceData.Velocity, inAvoidanceData.Center + (ReturnVelocity / DeltaTime), 75.0f, FColor(64,255,64), true, 2.0f, SDPG_MAX);
		}
#endif
	}

	return ReturnVelocity / DeltaTime;		//Remove prediction-time scaling
}
bool FInstancedStaticMeshSCSEditorCustomization::HandleViewportDrag(class USceneComponent* InSceneComponent, class USceneComponent* InComponentTemplate, const FVector& InDeltaTranslation, const FRotator& InDeltaRotation, const FVector& InDeltaScale, const FVector& InPivot)
{
	check(InSceneComponent->IsA(UInstancedStaticMeshComponent::StaticClass()));

	UInstancedStaticMeshComponent* InstancedStaticMeshComponentScene = CastChecked<UInstancedStaticMeshComponent>(InSceneComponent);
	UInstancedStaticMeshComponent* InstancedStaticMeshComponentTemplate = CastChecked<UInstancedStaticMeshComponent>(InComponentTemplate);

	// transform pivot into component's space
	const FVector LocalPivot = InstancedStaticMeshComponentScene->GetComponentToWorld().InverseTransformPosition(InPivot);

	// Ensure that selected instances are up-to-date
	ValidateSelectedInstances(InstancedStaticMeshComponentScene);

	bool bMovedInstance = false;
	check(InstancedStaticMeshComponentScene->SelectedInstances.Num() == InstancedStaticMeshComponentScene->PerInstanceSMData.Num());
	for(int32 InstanceIndex = 0; InstanceIndex < InstancedStaticMeshComponentScene->SelectedInstances.Num(); InstanceIndex++)
	{
		if (InstancedStaticMeshComponentScene->SelectedInstances[InstanceIndex] && InstancedStaticMeshComponentTemplate->PerInstanceSMData.IsValidIndex(InstanceIndex))
		{
			const FMatrix& MatrixScene = InstancedStaticMeshComponentScene->PerInstanceSMData[InstanceIndex].Transform;

			FVector Translation = MatrixScene.GetOrigin();
			FRotator Rotation = MatrixScene.Rotator();
			FVector Scale = MatrixScene.GetScaleVector();

			FVector NewTranslation = Translation;
			FRotator NewRotation = Rotation;
			FVector NewScale = Scale;

			if( !InDeltaRotation.IsZero() )
			{
				NewRotation = FRotator( InDeltaRotation.Quaternion() * Rotation.Quaternion() );

				NewTranslation -= LocalPivot;
				NewTranslation = FRotationMatrix( InDeltaRotation ).TransformPosition( NewTranslation );
				NewTranslation += LocalPivot;
			}

			NewTranslation += InDeltaTranslation;

			if( !InDeltaScale.IsNearlyZero() )
			{
				const FScaleMatrix ScaleMatrix( InDeltaScale );

				FVector DeltaScale3D = ScaleMatrix.TransformPosition( Scale );
				NewScale = Scale + DeltaScale3D;

				NewTranslation -= LocalPivot;
				NewTranslation += ScaleMatrix.TransformPosition( NewTranslation );
				NewTranslation += LocalPivot;
			}

			InstancedStaticMeshComponentScene->UpdateInstanceTransform(InstanceIndex, FTransform(NewRotation, NewTranslation, NewScale));
			InstancedStaticMeshComponentTemplate->UpdateInstanceTransform(InstanceIndex, FTransform(NewRotation, NewTranslation, NewScale));

			bMovedInstance = true;
		}
	}

	return bMovedInstance;
}
Esempio n. 13
0
FRotator USplineComponent::GetWorldRotationAtTime(float Time, bool bUseConstantVelocity) const
{
	FVector WorldDir = GetWorldDirectionAtTime(Time, bUseConstantVelocity);
	return WorldDir.Rotation();
}
Esempio n. 14
0
FRotator USplineComponent::GetWorldRotationAtDistanceAlongSpline(float Distance) const
{
	const FVector Dir = GetWorldDirectionAtDistanceAlongSpline(Distance);
	return Dir.Rotation();
}
Esempio n. 15
0
FString UKismetStringLibrary::Conv_VectorToString(FVector InVec)
{
	return InVec.ToString();	
}
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;

	// 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];
		}
	}
}
Esempio n. 17
0
EPathFollowingRequestResult::Type AAIController::MoveToLocation(const FVector& Dest, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bProjectDestinationToNavigation, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass)
{
	SCOPE_CYCLE_COUNTER(STAT_MoveToLocation);

	EPathFollowingRequestResult::Type Result = EPathFollowingRequestResult::Failed;
	bool bCanRequestMove = true;

	UE_VLOG(this, LogAINavigation, Log, TEXT("MoveToLocation: Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d) Filter(%s)")
		, TEXT_AI_LOCATION(Dest), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe, *GetNameSafe(FilterClass));

	// Check input is valid
	if (Dest.ContainsNaN())
	{
		UE_VLOG(this, LogAINavigation, Error, TEXT("AAIController::MoveToLocation: Destination is not valid! Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)")
			, TEXT_AI_LOCATION(Dest), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe);

		ensure(!Dest.ContainsNaN());
		bCanRequestMove = false;
	}

	FVector GoalLocation = Dest;

	// fail if projection to navigation is required but it failed
	if (bCanRequestMove && bProjectDestinationToNavigation)
	{
		UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
		const FNavAgentProperties& AgentProps = GetNavAgentPropertiesRef();
		FNavLocation ProjectedLocation;

		if (NavSys && !NavSys->ProjectPointToNavigation(Dest, ProjectedLocation, AgentProps.GetExtent(), &AgentProps))
		{
			UE_VLOG_LOCATION(this, LogAINavigation, Error, Dest, 30.f, FLinearColor::Red, TEXT("AAIController::MoveToLocation failed to project destination location to navmesh"));
			bCanRequestMove = false;
		}

		GoalLocation = ProjectedLocation.Location;
	}

	if (bCanRequestMove && PathFollowingComponent && PathFollowingComponent->HasReached(GoalLocation, AcceptanceRadius, !bStopOnOverlap))
	{
		UE_VLOG(this, LogAINavigation, Log, TEXT("MoveToLocation: already at goal!"));

		// make sure previous move request gets aborted
		PathFollowingComponent->AbortMove(TEXT("Aborting move due to new move request finishing with AlreadyAtGoal"), FAIRequestID::AnyRequest);

		PathFollowingComponent->SetLastMoveAtGoal(true);

		OnMoveCompleted(FAIRequestID::CurrentRequest, EPathFollowingResult::Success);
		Result = EPathFollowingRequestResult::AlreadyAtGoal;
		bCanRequestMove = false;
	}

	if (bCanRequestMove)
	{
		FPathFindingQuery Query;
		const bool bValidQuery = PreparePathfinding(Query, GoalLocation, NULL, bUsePathfinding, FilterClass);
		const FAIRequestID RequestID = bValidQuery ? RequestPathAndMove(Query, NULL, AcceptanceRadius, bStopOnOverlap, NULL) : FAIRequestID::InvalidRequest;

		if (RequestID.IsValid())
		{
			bAllowStrafe = bCanStrafe;
			Result = EPathFollowingRequestResult::RequestSuccessful;
		}
	}

	if (Result == EPathFollowingRequestResult::Failed)
	{
		if (PathFollowingComponent)
		{
			PathFollowingComponent->SetLastMoveAtGoal(false);
		}

		OnMoveCompleted(FAIRequestID::InvalidRequest, EPathFollowingResult::Invalid);
	}

	return Result;
}
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;
}
Esempio n. 19
0
bool UTB_AimComponent::MissLineTrace(FHitResult &OutHit)
{
	if (!EnemyTarget)
	{
		UE_LOG(TB_Log, Error, TEXT("MissLineTrace(): EnemyTarget == NULL"));
		return false;
	}

	//Pick a HitLocation at random
	TArray<FVector> HitLocations;
	EnemyTarget->GetHitLocations(HitLocations);
	UTB_Library::Shuffle(HitLocations);

	FCollisionQueryParams Params;
	InitCollisionQueryParams(Params);
	FCollisionObjectQueryParams ObjectParams;
	FVector StartLocation = GetComponentLocation();

	float Offset = 30;

	for (auto HitLocation : HitLocations)
	{
		bool HitSomething = World->LineTraceSingle(OutHit, StartLocation, HitLocation, Params, ObjectParams);
		if (HitSomething)
		{
			if (EnemyTarget->GetUniqueID() != OutHit.Actor->GetUniqueID())
			{
				//we've hit something we didn't aim for \o/
				return true;
			}
			else
			{
				//we need to adjust the trace so it misses the target

				// Create a new vector for the trace 
				FVector TraceVector = HitLocation - StartLocation;
				TraceVector.Normalize();
				TraceVector *= Weapon->MaxRange;

				// Rotate it by 0-5 deg in all directions
				FRotator Offset(FMath::FRandRange(-10, 10), FMath::FRandRange(-10, 10), 0);
				TraceVector = Offset.RotateVector(TraceVector);

				HitSomething = World->LineTraceSingle(OutHit, StartLocation, StartLocation + TraceVector, Params, ObjectParams);
				if (HitSomething && EnemyTarget->GetUniqueID() == OutHit.Actor->GetUniqueID())
				{
					//we're still hitting the target, pray that we'll miss it when we look at the other targetpoints
					continue;
				}
				else
				{
					return HitSomething;
				}

			}
		}
	}

	UE_LOG(TB_Log, Warning, TEXT("MissLineTrace(): Can't find a way to miss"));
	return false;
}
void UEnvQueryGenerator_OnCircle::GenerateItemsForCircle(const FVector& CenterLocation, const FVector& StartDirection,
	int32 StepsCount, float AngleStep, FEnvQueryInstance& OutQueryInstance) const
{
	TArray<FVector> ItemCandidates;
	ItemCandidates.AddZeroed(StepsCount);
	for (int32 Step = 0; Step < StepsCount; ++Step)
	{
		ItemCandidates[Step] = CenterLocation + StartDirection.RotateAngleAxis(AngleStep*Step, FVector::UpVector);
	}

#if WITH_RECAST
	// @todo this needs to be optimize to batch raycasts
	const ARecastNavMesh* NavMesh = 
		(TraceData.TraceMode == EEnvQueryTrace::Navigation) || (ProjectionData.TraceMode == EEnvQueryTrace::Navigation) ?
		FEQSHelpers::FindNavMeshForQuery(OutQueryInstance) : NULL;

	if (NavMesh)
	{
		NavMesh->BeginBatchQuery();
	}
#endif

	if (TraceData.TraceMode == EEnvQueryTrace::Navigation)
	{
#if WITH_RECAST
		if (NavMesh != NULL)
		{
			TSharedPtr<const FNavigationQueryFilter> NavigationFilter = UNavigationQueryFilter::GetQueryFilter(NavMesh, TraceData.NavigationFilter);

			TArray<FNavigationRaycastWork> RaycastWorkload;
			RaycastWorkload.Reserve(ItemCandidates.Num());

			for (const auto& ItemLocation : ItemCandidates)
			{
				RaycastWorkload.Add(FNavigationRaycastWork(CenterLocation, ItemLocation));
			}

			NavMesh->BatchRaycast(RaycastWorkload, NavigationFilter);
			
			for (int32 ItemIndex = 0; ItemIndex < ItemCandidates.Num(); ++ItemIndex)
			{
				ItemCandidates[ItemIndex] = RaycastWorkload[ItemIndex].HitLocation.Location;
			}
		}
#endif
	}
	else
	{
		ECollisionChannel TraceCollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceData.TraceChannel);
		FVector TraceExtent(TraceData.ExtentX, TraceData.ExtentY, TraceData.ExtentZ);

		FCollisionQueryParams TraceParams(TEXT("EnvQueryTrace"), TraceData.bTraceComplex);
		TraceParams.bTraceAsyncScene = true;

		FBatchTracingHelper TracingHelper(OutQueryInstance.World, TraceCollisionChannel, TraceParams, TraceExtent);

		switch (TraceData.TraceShape)
		{
		case EEnvTraceShape::Line:		
			TracingHelper.DoSingleSourceMultiDestinations<EEnvTraceShape::Line>(CenterLocation, ItemCandidates);
			break;
		case EEnvTraceShape::Sphere:	
			TracingHelper.DoSingleSourceMultiDestinations<EEnvTraceShape::Sphere>(CenterLocation, ItemCandidates);
			break;
		case EEnvTraceShape::Capsule:
			TracingHelper.DoSingleSourceMultiDestinations<EEnvTraceShape::Capsule>(CenterLocation, ItemCandidates);
			break;
		case EEnvTraceShape::Box:
			TracingHelper.DoSingleSourceMultiDestinations<EEnvTraceShape::Box>(CenterLocation, ItemCandidates);
			break;
		default:
			UE_VLOG(Cast<AActor>(OutQueryInstance.Owner.Get()), LogEQS, Warning, TEXT("UEnvQueryGenerator_OnCircle::CalcDirection failed to calc direction in %s. Using querier facing."), *OutQueryInstance.QueryName);
			break;
		}
	}

#if WITH_RECAST
	if (NavMesh)
	{
		ProjectAndFilterNavPoints(ItemCandidates, NavMesh);
		NavMesh->FinishBatchQuery();
	}
#endif

	for (int32 Step = 0; Step < ItemCandidates.Num(); ++Step)
	{
		OutQueryInstance.AddItemData<UEnvQueryItemType_Point>(ItemCandidates[Step]);
	}
}
bool AAIController::LineOfSightTo(const AActor* Other, FVector ViewPoint, bool bAlternateChecks) const
{
    if (Other == nullptr)
    {
        return false;
    }

    if (ViewPoint.IsZero())
    {
        FRotator ViewRotation;
        GetActorEyesViewPoint(ViewPoint, ViewRotation);

        // if we still don't have a view point we simply fail
        if (ViewPoint.IsZero())
        {
            return false;
        }
    }

    static FName NAME_LineOfSight = FName(TEXT("LineOfSight"));
    FVector TargetLocation = Other->GetTargetLocation(GetPawn());

    FCollisionQueryParams CollisionParams(NAME_LineOfSight, true, this->GetPawn());
    CollisionParams.AddIgnoredActor(Other);

    bool bHit = GetWorld()->LineTraceTestByChannel(ViewPoint, TargetLocation, ECC_Visibility, CollisionParams);
    if (!bHit)
    {
        return true;
    }

    // if other isn't using a cylinder for collision and isn't a Pawn (which already requires an accurate cylinder for AI)
    // then don't go any further as it likely will not be tracing to the correct location
    const APawn * OtherPawn = Cast<const APawn>(Other);
    if (!OtherPawn && Cast<UCapsuleComponent>(Other->GetRootComponent()) == NULL)
    {
        return false;
    }

    const FVector OtherActorLocation = Other->GetActorLocation();
    const float DistSq = (OtherActorLocation - ViewPoint).SizeSquared();
    if (DistSq > FARSIGHTTHRESHOLDSQUARED)
    {
        return false;
    }

    if (!OtherPawn && (DistSq > NEARSIGHTTHRESHOLDSQUARED))
    {
        return false;
    }

    float OtherRadius, OtherHeight;
    Other->GetSimpleCollisionCylinder(OtherRadius, OtherHeight);

    if (!bAlternateChecks || !bLOSflag)
    {
        //try viewpoint to head
        bHit = GetWorld()->LineTraceTestByChannel(ViewPoint, OtherActorLocation + FVector(0.f, 0.f, OtherHeight), ECC_Visibility, CollisionParams);
        if (!bHit)
        {
            return true;
        }
    }

    if (!bSkipExtraLOSChecks && (!bAlternateChecks || bLOSflag))
    {
        // only check sides if width of other is significant compared to distance
        if (OtherRadius * OtherRadius / (OtherActorLocation - ViewPoint).SizeSquared() < 0.0001f)
        {
            return false;
        }
        //try checking sides - look at dist to four side points, and cull furthest and closest
        FVector Points[4];
        Points[0] = OtherActorLocation - FVector(OtherRadius, -1 * OtherRadius, 0);
        Points[1] = OtherActorLocation + FVector(OtherRadius, OtherRadius, 0);
        Points[2] = OtherActorLocation - FVector(OtherRadius, OtherRadius, 0);
        Points[3] = OtherActorLocation + FVector(OtherRadius, -1 * OtherRadius, 0);
        int32 IndexMin = 0;
        int32 IndexMax = 0;
        float CurrentMax = (Points[0] - ViewPoint).SizeSquared();
        float CurrentMin = CurrentMax;
        for (int32 PointIndex = 1; PointIndex<4; PointIndex++)
        {
            const float NextSize = (Points[PointIndex] - ViewPoint).SizeSquared();
            if (NextSize > CurrentMin)
            {
                CurrentMin = NextSize;
                IndexMax = PointIndex;
            }
            else if (NextSize < CurrentMax)
            {
                CurrentMax = NextSize;
                IndexMin = PointIndex;
            }
        }

        for (int32 PointIndex = 0; PointIndex<4; PointIndex++)
        {
            if ((PointIndex != IndexMin) && (PointIndex != IndexMax))
            {
                bHit = GetWorld()->LineTraceTestByChannel(ViewPoint, Points[PointIndex], ECC_Visibility, CollisionParams);
                if (!bHit)
                {
                    return true;
                }
            }
        }
    }
    return false;
}
Esempio n. 22
0
void FAnimNode_TwoBoneIK::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, FCSPose<FCompactPose>& MeshBases, TArray<FBoneTransform>& OutBoneTransforms)
{
	check(OutBoneTransforms.Num() == 0);

	const FBoneContainer& BoneContainer = MeshBases.GetPose().GetBoneContainer();

	// Get indices of the lower and upper limb bones and check validity.
	bool bInvalidLimb = false;

	FCompactPoseBoneIndex IKBoneCompactPoseIndex = IKBone.GetCompactPoseIndex(BoneContainer);

	const FCompactPoseBoneIndex LowerLimbIndex = BoneContainer.GetParentBoneIndex(IKBoneCompactPoseIndex);
	if (LowerLimbIndex == INDEX_NONE)
	{
		bInvalidLimb = true;
	}

	const FCompactPoseBoneIndex UpperLimbIndex = BoneContainer.GetParentBoneIndex(LowerLimbIndex);
	if (UpperLimbIndex == INDEX_NONE)
	{
		bInvalidLimb = true;
	}

	const bool bInBoneSpace = (EffectorLocationSpace == BCS_ParentBoneSpace) || (EffectorLocationSpace == BCS_BoneSpace);
	const int32 EffectorBoneIndex = bInBoneSpace ? BoneContainer.GetPoseBoneIndexForBoneName(EffectorSpaceBoneName) : INDEX_NONE;
	const FCompactPoseBoneIndex EffectorSpaceBoneIndex = BoneContainer.MakeCompactPoseIndex(FMeshPoseBoneIndex(EffectorBoneIndex));

	if (bInBoneSpace && (EffectorSpaceBoneIndex == INDEX_NONE))
	{
		bInvalidLimb = true;
	}

	// If we walked past the root, this controlled is invalid, so return no affected bones.
	if( bInvalidLimb )
	{
		return;
	}

	// Get Local Space transforms for our bones. We do this first in case they already are local.
	// As right after we get them in component space. (And that does the auto conversion).
	// We might save one transform by doing local first...
	const FTransform EndBoneLocalTransform = MeshBases.GetLocalSpaceTransform(IKBoneCompactPoseIndex);

	// Now get those in component space...
	FTransform LowerLimbCSTransform = MeshBases.GetComponentSpaceTransform(LowerLimbIndex);
	FTransform UpperLimbCSTransform = MeshBases.GetComponentSpaceTransform(UpperLimbIndex);
	FTransform EndBoneCSTransform = MeshBases.GetComponentSpaceTransform(IKBoneCompactPoseIndex);

	// Get current position of root of limb.
	// All position are in Component space.
	const FVector RootPos = UpperLimbCSTransform.GetTranslation();
	const FVector InitialJointPos = LowerLimbCSTransform.GetTranslation();
	const FVector InitialEndPos = EndBoneCSTransform.GetTranslation();

	// Transform EffectorLocation from EffectorLocationSpace to ComponentSpace.
	FTransform EffectorTransform(EffectorLocation);
	FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, EffectorTransform, EffectorSpaceBoneIndex, EffectorLocationSpace);

	// This is our reach goal.
	FVector DesiredPos = EffectorTransform.GetTranslation();
	FVector DesiredDelta = DesiredPos - RootPos;
	float DesiredLength = DesiredDelta.Size();

	// Check to handle case where DesiredPos is the same as RootPos.
	FVector	DesiredDir;
	if (DesiredLength < (float)KINDA_SMALL_NUMBER)
	{
		DesiredLength = (float)KINDA_SMALL_NUMBER;
		DesiredDir = FVector(1,0,0);
	}
	else
	{
		DesiredDir = DesiredDelta / DesiredLength;
	}

	// Get joint target (used for defining plane that joint should be in).
	FTransform JointTargetTransform(JointTargetLocation);
	FCompactPoseBoneIndex JointTargetSpaceBoneIndex(INDEX_NONE);

	if (JointTargetLocationSpace == BCS_ParentBoneSpace || JointTargetLocationSpace == BCS_BoneSpace)
	{
		int32 Index = BoneContainer.GetPoseBoneIndexForBoneName(JointTargetSpaceBoneName);
		JointTargetSpaceBoneIndex = BoneContainer.MakeCompactPoseIndex(FMeshPoseBoneIndex(Index));
	}
	
	FAnimationRuntime::ConvertBoneSpaceTransformToCS(SkelComp, MeshBases, JointTargetTransform, JointTargetSpaceBoneIndex, JointTargetLocationSpace);

	FVector	JointTargetPos = JointTargetTransform.GetTranslation();
	FVector JointTargetDelta = JointTargetPos - RootPos;
	float JointTargetLength = JointTargetDelta.Size();

	// Same check as above, to cover case when JointTarget position is the same as RootPos.
	FVector JointPlaneNormal, JointBendDir;
	if (JointTargetLength < (float)KINDA_SMALL_NUMBER)
	{
		JointBendDir = FVector(0,1,0);
		JointPlaneNormal = FVector(0,0,1);
	}
	else
	{
		JointPlaneNormal = DesiredDir ^ JointTargetDelta;

		// If we are trying to point the limb in the same direction that we are supposed to displace the joint in, 
		// we have to just pick 2 random vector perp to DesiredDir and each other.
		if (JointPlaneNormal.Size() < (float)KINDA_SMALL_NUMBER)
		{
			DesiredDir.FindBestAxisVectors(JointPlaneNormal, JointBendDir);
		}
		else
		{
			JointPlaneNormal.Normalize();

			// Find the final member of the reference frame by removing any component of JointTargetDelta along DesiredDir.
			// This should never leave a zero vector, because we've checked DesiredDir and JointTargetDelta are not parallel.
			JointBendDir = JointTargetDelta - ((JointTargetDelta | DesiredDir) * DesiredDir);
			JointBendDir.Normalize();
		}
	}

	// Find lengths of upper and lower limb in the ref skeleton.
	// Use actual sizes instead of ref skeleton, so we take into account translation and scaling from other bone controllers.
	float LowerLimbLength = (InitialEndPos - InitialJointPos).Size();
	float UpperLimbLength = (InitialJointPos - RootPos).Size();
	float MaxLimbLength	= LowerLimbLength + UpperLimbLength;

	if (bAllowStretching)
	{
		const float ScaleRange = StretchLimits.Y - StretchLimits.X;
		if( ScaleRange > KINDA_SMALL_NUMBER && MaxLimbLength > KINDA_SMALL_NUMBER )
		{
			const float ReachRatio = DesiredLength / MaxLimbLength;
			const float ScalingFactor = (StretchLimits.Y - 1.f) * FMath::Clamp<float>((ReachRatio - StretchLimits.X) / ScaleRange, 0.f, 1.f);
			if (ScalingFactor > KINDA_SMALL_NUMBER)
			{
				LowerLimbLength *= (1.f + ScalingFactor);
				UpperLimbLength *= (1.f + ScalingFactor);
				MaxLimbLength	*= (1.f + ScalingFactor);
			}
		}
	}

	FVector OutEndPos = DesiredPos;
	FVector OutJointPos = InitialJointPos;

	// If we are trying to reach a goal beyond the length of the limb, clamp it to something solvable and extend limb fully.
	if (DesiredLength > MaxLimbLength)
	{
		OutEndPos = RootPos + (MaxLimbLength * DesiredDir);
		OutJointPos = RootPos + (UpperLimbLength * DesiredDir);
	}
	else
	{
		// So we have a triangle we know the side lengths of. We can work out the angle between DesiredDir and the direction of the upper limb
		// using the sin rule:
		const float TwoAB = 2.f * UpperLimbLength * DesiredLength;

		const float CosAngle = (TwoAB != 0.f) ? ((UpperLimbLength*UpperLimbLength) + (DesiredLength*DesiredLength) - (LowerLimbLength*LowerLimbLength)) / TwoAB : 0.f;

		// If CosAngle is less than 0, the upper arm actually points the opposite way to DesiredDir, so we handle that.
		const bool bReverseUpperBone = (CosAngle < 0.f);

		// If CosAngle is greater than 1.f, the triangle could not be made - we cannot reach the target.
		// We just have the two limbs double back on themselves, and EndPos will not equal the desired EffectorLocation.
		if ((CosAngle > 1.f) || (CosAngle < -1.f))
		{
			// Because we want the effector to be a positive distance down DesiredDir, we go back by the smaller section.
			if (UpperLimbLength > LowerLimbLength)
			{
				OutJointPos = RootPos + (UpperLimbLength * DesiredDir);
				OutEndPos = OutJointPos - (LowerLimbLength * DesiredDir);
			}
			else
			{
				OutJointPos = RootPos - (UpperLimbLength * DesiredDir);
				OutEndPos = OutJointPos + (LowerLimbLength * DesiredDir);
			}
		}
		else
		{
			// Angle between upper limb and DesiredDir
			const float Angle = FMath::Acos(CosAngle);

			// Now we calculate the distance of the joint from the root -> effector line.
			// This forms a right-angle triangle, with the upper limb as the hypotenuse.
			const float JointLineDist = UpperLimbLength * FMath::Sin(Angle);

			// And the final side of that triangle - distance along DesiredDir of perpendicular.
			// ProjJointDistSqr can't be neg, because JointLineDist must be <= UpperLimbLength because appSin(Angle) is <= 1.
			const float ProjJointDistSqr = (UpperLimbLength*UpperLimbLength) - (JointLineDist*JointLineDist);
			// although this shouldn't be ever negative, sometimes Xbox release produces -0.f, causing ProjJointDist to be NaN
			// so now I branch it. 						
			float ProjJointDist = (ProjJointDistSqr>0.f)? FMath::Sqrt(ProjJointDistSqr) : 0.f;
			if( bReverseUpperBone )
			{
				ProjJointDist *= -1.f;
			}

			// So now we can work out where to put the joint!
			OutJointPos = RootPos + (ProjJointDist * DesiredDir) + (JointLineDist * JointBendDir);
		}
	}

	// Update transform for upper bone.
	{
		// Get difference in direction for old and new joint orientations
		FVector const OldDir = (InitialJointPos - RootPos).GetSafeNormal();
		FVector const NewDir = (OutJointPos - RootPos).GetSafeNormal();
		// Find Delta Rotation take takes us from Old to New dir
		FQuat const DeltaRotation = FQuat::FindBetweenNormals(OldDir, NewDir);
		// Rotate our Joint quaternion by this delta rotation
		UpperLimbCSTransform.SetRotation( DeltaRotation * UpperLimbCSTransform.GetRotation() );
		// And put joint where it should be.
		UpperLimbCSTransform.SetTranslation( RootPos );

		// Order important. First bone is upper limb.
		OutBoneTransforms.Add( FBoneTransform(UpperLimbIndex, UpperLimbCSTransform) );
	}

	// Update transform for lower bone.
	{
		// Get difference in direction for old and new joint orientations
		FVector const OldDir = (InitialEndPos - InitialJointPos).GetSafeNormal();
		FVector const NewDir = (OutEndPos - OutJointPos).GetSafeNormal();

		// Find Delta Rotation take takes us from Old to New dir
		FQuat const DeltaRotation = FQuat::FindBetweenNormals(OldDir, NewDir);
		// Rotate our Joint quaternion by this delta rotation
		LowerLimbCSTransform.SetRotation( DeltaRotation * LowerLimbCSTransform.GetRotation() );
		// And put joint where it should be.
		LowerLimbCSTransform.SetTranslation( OutJointPos );

		// Order important. Second bone is lower limb.
		OutBoneTransforms.Add( FBoneTransform(LowerLimbIndex, LowerLimbCSTransform) );
	}

	// Update transform for end bone.
	{
		if( bTakeRotationFromEffectorSpace )
		{
			EndBoneCSTransform.SetRotation( EffectorTransform.GetRotation() );
		}
		else if( bMaintainEffectorRelRot )
		{
			EndBoneCSTransform = EndBoneLocalTransform * LowerLimbCSTransform;
		}

		// Set correct location for end bone.
		EndBoneCSTransform.SetTranslation(OutEndPos);

		// Order important. Third bone is End Bone.
		OutBoneTransforms.Add(FBoneTransform(IKBoneCompactPoseIndex, EndBoneCSTransform));
	}

	// Make sure we have correct number of bones
	check(OutBoneTransforms.Num() == 3);
}
Esempio n. 23
0
static double
logSum(const FVector &v)
{
  return logSum(v, v.size());
}
Esempio n. 24
0
void UVaOceanBuoyancyComponent::PerformWaveReaction(float DeltaTime)
{
	AActor* MyOwner = GetOwner();

	if (!UpdatedComponent || MyOwner == NULL)
	{
		return;
	}
	UPrimitiveComponent* OldPrimitive = Cast<UPrimitiveComponent>(UpdatedComponent);

	const FVector OldLocation = MyOwner->GetActorLocation();
	const FRotator OldRotation = MyOwner->GetActorRotation();
	const FVector OldLinearVelocity = OldPrimitive->GetPhysicsLinearVelocity();
	const FVector OldAngularVelocity = OldPrimitive->GetPhysicsAngularVelocity();
	const FVector OldCenterOfMassWorld = OldLocation + OldRotation.RotateVector(COMOffset);
	const FVector OwnerScale = MyOwner->GetActorScale();

	// XYZ === Throttle, Steering, Rise == Forwards, Sidewards, Upwards
	FVector X, Y, Z;
	GetAxes(OldRotation, X, Y, Z);

	// Process tension dots and get torque from wind/waves
	for (FVector TensionDot : TensionDots)
	{
		// Translate point to world coordinates
		FVector TensionDotDisplaced = OldRotation.RotateVector(TensionDot + COMOffset);
		FVector TensionDotWorld = OldLocation + TensionDotDisplaced;

		// Get point depth
		float DotAltitude = GetAltitude(TensionDotWorld);

		// Don't process dots above water
		if (DotAltitude > 0)
		{
			continue;
		}
		
		// Surface normal (not modified!)
		FVector DotSurfaceNormal = GetSurfaceNormal(TensionDotWorld) * GetSurfaceWavesNum();
		// Modify normal with real Z value and normalize it
		DotSurfaceNormal.Z = GetOceanLevel(TensionDotWorld);
		DotSurfaceNormal.Normalize();

		// Point dynamic pressure [http://en.wikipedia.org/wiki/Dynamic_pressure]
		// rho = 1.03f for ocean water
		FVector WaveVelocity = GetWaveVelocity(TensionDotWorld);
		float DotQ = 0.515f * FMath::Square(WaveVelocity.Size());
		FVector WaveForce = FVector(0.0,0.0,1.0) * DotQ /* DotSurfaceNormal*/ * (-DotAltitude) * TensionDepthFactor;
		
		// We don't want Z to be affected by DotQ
		WaveForce.Z /= DotQ;

		// Scale to DeltaTime to break FPS addiction
		WaveForce *= DeltaTime;

		// Apply actor scale
		WaveForce *= OwnerScale.X;// *OwnerScale.Y * OwnerScale.Z;

		OldPrimitive->AddForceAtLocation(WaveForce * Mass, TensionDotWorld);
	}

	// Static metacentric forces (can be useful on small waves)
	if (bUseMetacentricForces)
	{
		FVector TensionTorqueResult = FVector(0.0f, 0.0f, 0.0f);

		// Calc recovering torque (transverce)
		FRotator RollRot = FRotator(0.0f, 0.0f, 0.0f);
		RollRot.Roll = OldRotation.Roll;
		FVector MetacenterDisplaced = RollRot.RotateVector(TransverseMetacenter + COMOffset);
		TensionTorqueResult += X * FVector::DotProduct((TransverseMetacenter - MetacenterDisplaced), 
			FVector(0.0f, -1.0f, 0.0f)) * TensionTorqueRollFactor;

		// Calc recovering torque (longitude)
		FRotator PitchRot = FRotator(0.0f, 0.0f, 0.0f);
		PitchRot.Pitch = OldRotation.Pitch;
		MetacenterDisplaced = PitchRot.RotateVector(LongitudinalMetacenter + COMOffset);
		TensionTorqueResult += Y * FVector::DotProduct((LongitudinalMetacenter - MetacenterDisplaced), 
			FVector(1.0f, 0.0f, 0.0f)) * TensionTorquePitchFactor;

		// Apply torque
		TensionTorqueResult *= DeltaTime;
		TensionTorqueResult *= OwnerScale.X;// *OwnerScale.Y * OwnerScale.Z;
		OldPrimitive->AddTorque(TensionTorqueResult);
	}
}
Esempio n. 25
0
FVector UTKMathFunctionLibrary::ClosestPointOnSphereToLine(FVector SphereOrigin, float SphereRadius, FVector LineOrigin, FVector LineDir)
{
	static FVector OutClosestPoint;
	FMath::SphereDistToLine(SphereOrigin, SphereRadius, LineOrigin, LineDir.GetSafeNormal(), OutClosestPoint);
	return OutClosestPoint;
}
/** 
 *	Utility that ensures the verts supplied form a valid hull. Will modify the verts to remove any duplicates. 
 *	Positions should be in physics scale.
 *	Returns true is hull is valid.
 */
static bool EnsureHullIsValid(TArray<FVector>& InVerts)
{
	RemoveDuplicateVerts(InVerts);

	if(InVerts.Num() < 3)
	{
		return false;
	}

	// Take any vert. In this case - the first one.
	const FVector FirstVert = InVerts[0];

	// Now find vert furthest from this one.
	float FurthestDistSqr = 0.f;
	int32 FurthestVertIndex = INDEX_NONE;
	for(int32 i=1; i<InVerts.Num(); i++)
	{
		const float TestDistSqr = (InVerts[i] - FirstVert).SizeSquared();
		if(TestDistSqr > FurthestDistSqr)
		{
			FurthestDistSqr = TestDistSqr;
			FurthestVertIndex = i;
		}
	}

	// If smallest dimension is too small - hull is invalid.
	if(FurthestVertIndex == INDEX_NONE || FurthestDistSqr < FMath::Square(MIN_HULL_VALID_DIMENSION))
	{
		return false;
	}

	// Now find point furthest from line defined by these 2 points.
	float ThirdPointDist = 0.f;
	int32 ThirdPointIndex = INDEX_NONE;
	for(int32 i=1; i<InVerts.Num(); i++)
	{
		if(i != FurthestVertIndex)
		{
			const float TestDist = DistanceToLine(FirstVert, InVerts[FurthestVertIndex], InVerts[i]);
			if(TestDist > ThirdPointDist)
			{
				ThirdPointDist = TestDist;
				ThirdPointIndex = i;
			}
		}
	}

	// If this dimension is too small - hull is invalid.
	if(ThirdPointIndex == INDEX_NONE || ThirdPointDist < MIN_HULL_VALID_DIMENSION)
	{
		return false;
	}

	// Now we check each remaining point against this plane.

	// First find plane normal.
	const FVector Dir1 = InVerts[FurthestVertIndex] - InVerts[0];
	const FVector Dir2 = InVerts[ThirdPointIndex] - InVerts[0];
	FVector PlaneNormal = Dir1 ^ Dir2;
	const bool bNormalizedOk = PlaneNormal.Normalize();
	if(!bNormalizedOk)
	{
		return false;
	}

	// Now iterate over all remaining vertices.
	float MaxThickness = 0.f;
	for(int32 i=1; i<InVerts.Num(); i++)
	{
		if((i != FurthestVertIndex) && (i != ThirdPointIndex))
		{
			const float PointPlaneDist = FMath::Abs((InVerts[i] - InVerts[0]) | PlaneNormal);
			MaxThickness = FMath::Max(PointPlaneDist, MaxThickness);
		}
	}

	if(MaxThickness < MIN_HULL_VALID_DIMENSION)
	{
		return false;
	}

	return true;
}
Esempio n. 27
0
FVector UTKMathFunctionLibrary::SetVectorLength(FVector A, float size)
{
	return A.GetSafeNormal() * size;
}
bool FKConvexElem::HullFromPlanes(const TArray<FPlane>& InPlanes, const TArray<FVector>& SnapVerts)
{
	// Start by clearing this convex.
	Reset();

	float TotalPolyArea = 0;

	for(int32 i=0; i<InPlanes.Num(); i++)
	{
		FPoly Polygon;
		Polygon.Normal = InPlanes[i];

		FVector AxisX, AxisY;
		Polygon.Normal.FindBestAxisVectors(AxisX,AxisY);

		const FVector Base = InPlanes[i] * InPlanes[i].W;

		new(Polygon.Vertices) FVector(Base + AxisX * HALF_WORLD_MAX + AxisY * HALF_WORLD_MAX);
		new(Polygon.Vertices) FVector(Base - AxisX * HALF_WORLD_MAX + AxisY * HALF_WORLD_MAX);
		new(Polygon.Vertices) FVector(Base - AxisX * HALF_WORLD_MAX - AxisY * HALF_WORLD_MAX);
		new(Polygon.Vertices) FVector(Base + AxisX * HALF_WORLD_MAX - AxisY * HALF_WORLD_MAX);

		for(int32 j=0; j<InPlanes.Num(); j++)
		{
			if(i != j)
			{
				if(!Polygon.Split(-FVector(InPlanes[j]), InPlanes[j] * InPlanes[j].W))
				{
					Polygon.Vertices.Empty();
					break;
				}
			}
		}

		// Do nothing if poly was completely clipped away.
		if(Polygon.Vertices.Num() > 0)
		{
			TotalPolyArea += Polygon.Area();

			// Add vertices of polygon to convex primitive.
			for(int32 j=0; j<Polygon.Vertices.Num(); j++)
			{
				// We try and snap the vert to on of the ones supplied.
				int32 NearestVert = INDEX_NONE;
				float NearestDistSqr = BIG_NUMBER;

				for(int32 k=0; k<SnapVerts.Num(); k++)
				{
					const float DistSquared = (Polygon.Vertices[j] - SnapVerts[k]).SizeSquared();

					if( DistSquared < NearestDistSqr )
					{
						NearestVert = k;
						NearestDistSqr = DistSquared;
					}
				}

				// If we have found a suitably close vertex, use that
				if( NearestVert != INDEX_NONE && NearestDistSqr < LOCAL_EPS )
				{
					const FVector localVert = SnapVerts[NearestVert];
					AddVertexIfNotPresent(VertexData, localVert);
				}
				else
				{
					const FVector localVert = Polygon.Vertices[j];
					AddVertexIfNotPresent(VertexData, localVert);
				}
			}
		}
	}

	// If the collision volume isn't closed, return an error so the model can be discarded
	if(TotalPolyArea < 0.001f)
	{
		UE_LOG(LogPhysics, Log,  TEXT("Total Polygon Area invalid: %f"), TotalPolyArea );
		return false;
	}

	// We need at least 4 vertices to make a convex hull with non-zero volume.
	// We shouldn't have the same vertex multiple times (using AddVertexIfNotPresent above)
	if(VertexData.Num() < 4)
	{
		return true;
	}

	// Check that not all vertices lie on a line (ie. find plane)
	// Again, this should be a non-zero vector because we shouldn't have duplicate verts.
	bool bFound = false;
	FVector Dir2, Dir1;

	Dir1 = VertexData[1] - VertexData[0];
	Dir1.Normalize();

	for(int32 i=2; i<VertexData.Num() && !bFound; i++)
	{
		Dir2 = VertexData[i] - VertexData[0];
		Dir2.Normalize();

		// If line are non-parallel, this vertex forms our plane
		if((Dir1 | Dir2) < (1.f - LOCAL_EPS))
		{
			bFound = true;
		}
	}

	if(!bFound)
	{
		return true;
	}

	// Now we check that not all vertices lie on a plane, by checking at least one lies off the plane we have formed.
	FVector Normal = Dir1 ^ Dir2;
	Normal.Normalize();

	const FPlane Plane(VertexData[0], Normal);

	bFound = false;
	for(int32 i=2; i<VertexData.Num() ; i++)
	{
		if(Plane.PlaneDot(VertexData[i]) > LOCAL_EPS)
		{
			bFound = true;
			break;
		}
	}

	// If we did not find a vert off the line - discard this hull.
	if(!bFound)
	{
		return true;
	}

	// calc bounding box of verts
	UpdateElemBox();

	// We can continue adding primitives (mesh is not horribly broken)
	return true;
}
Esempio n. 29
0
	/** Check if this vertex is in the same place as given point */
	FORCEINLINE bool IsSame( const FVector &v )
	{
		const float eps = 0.01f;
		return v.Equals( Position, eps );
	}
	/**
	 * Quantizes floating point light samples down to byte samples with a scale applied to all samples
	 *
	 * @param InLightSamples Floating point light sample coefficients
	 * @param OutLightSamples Quantized light sample coefficients
	 * @param OutScale Scale applied to each quantized sample (to get it back near original floating point value)
	 * @param bUseMappedFlag Whether or not to pay attention to the bIsMapped flag for each sample when calculating max scale
	 *
	 * TODO Calculate residual after compression, not just quantization.
	 * TODO Factor out error from directionality compression and push it to color. This requires knowing a representative normal.
	 * Best way is probably to create a new texture compression type and do error correcting during compression.
	 */
	void QuantizeLightSamples(
		TArray<FLightSample>& InLightSamples, 
		TArray<FQuantizedLightSampleData>& OutLightSamples, 
		float OutMultiply[LM_NUM_STORED_LIGHTMAP_COEF][4], 
		float OutAdd[LM_NUM_STORED_LIGHTMAP_COEF][4],
		int32 DebugSampleIndex,
		bool bUseMappedFlag)
	{
		//const float EncodeExponent = .5f;
		const float LogScale = 11.5f;
		const float LogBlackPoint = FMath::Pow( 2.0f, -0.5f * LogScale );
		const float SimpleLogScale = 16.0f;
		const float SimpleLogBlackPoint = FMath::Pow( 2.0f, -0.5f * SimpleLogScale );

		float MinCoefficient[LM_NUM_STORED_LIGHTMAP_COEF][4];
		float MaxCoefficient[LM_NUM_STORED_LIGHTMAP_COEF][4];

		for( int32 CoefficientIndex = 0; CoefficientIndex < LM_NUM_STORED_LIGHTMAP_COEF; CoefficientIndex += 2 )
		{
			for( int32 ColorIndex = 0; ColorIndex < 4; ColorIndex++ )
			{
				// Color
				MinCoefficient[ CoefficientIndex ][ ColorIndex ] = 10000.0f;
				MaxCoefficient[ CoefficientIndex ][ ColorIndex ] = -10000.0f;

				// Direction
				MinCoefficient[ CoefficientIndex + 1 ][ ColorIndex ] = 10000.0f;
				MaxCoefficient[ CoefficientIndex + 1 ][ ColorIndex ] = -10000.0f;
			}
		}

		// go over all samples looking for min and max values
		for (int32 SampleIndex = 0; SampleIndex < InLightSamples.Num(); SampleIndex++)
		{
			const FLightSample& SourceSample = InLightSamples[SampleIndex];
#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
			if (SampleIndex == DebugSampleIndex)
			{
				int32 TempBreak = 0;
			}
#endif
			if(SourceSample.bIsMapped)
			{
				{
					// Complex
					float L, U, V, W;
					GetLUVW( SourceSample.Coefficients[0], L, U, V, W );

					float LogL = FMath::Log2( L + LogBlackPoint );

					MinCoefficient[0][0] = FMath::Min( MinCoefficient[0][0], U );
					MaxCoefficient[0][0] = FMath::Max( MaxCoefficient[0][0], U );

					MinCoefficient[0][1] = FMath::Min( MinCoefficient[0][1], V );
					MaxCoefficient[0][1] = FMath::Max( MaxCoefficient[0][1], V );
				
					MinCoefficient[0][2] = FMath::Min( MinCoefficient[0][2], W );
					MaxCoefficient[0][2] = FMath::Max( MaxCoefficient[0][2], W );
				
					MinCoefficient[0][3] = FMath::Min( MinCoefficient[0][3], LogL );
					MaxCoefficient[0][3] = FMath::Max( MaxCoefficient[0][3], LogL );

					// Dampen dark texel's contribution on the directionality min and max
					float DampenDirectionality = FMath::Clamp(L * 100, 0.0f, 1.0f);

					for( int32 ColorIndex = 0; ColorIndex < 3; ColorIndex++ )
					{
						MinCoefficient[1][ColorIndex] = FMath::Min( MinCoefficient[1][ColorIndex], DampenDirectionality * SourceSample.Coefficients[1][ColorIndex] );
						MaxCoefficient[1][ColorIndex] = FMath::Max( MaxCoefficient[1][ColorIndex], DampenDirectionality * SourceSample.Coefficients[1][ColorIndex] );
					}
				}

				{
					// Simple
					float L, U, V, W;
					GetLUVW( SourceSample.Coefficients[2], L, U, V, W );

					float LogL = FMath::Log2( L + SimpleLogBlackPoint ) / SimpleLogScale + 0.5f;

					float LogR = LogL * U;
					float LogG = LogL * V;
					float LogB = LogL * W;

					MinCoefficient[2][0] = FMath::Min( MinCoefficient[2][0], LogR );
					MaxCoefficient[2][0] = FMath::Max( MaxCoefficient[2][0], LogR );

					MinCoefficient[2][1] = FMath::Min( MinCoefficient[2][1], LogG );
					MaxCoefficient[2][1] = FMath::Max( MaxCoefficient[2][1], LogG );
				
					MinCoefficient[2][2] = FMath::Min( MinCoefficient[2][2], LogB );
					MaxCoefficient[2][2] = FMath::Max( MaxCoefficient[2][2], LogB );

					// Dampen dark texel's contribution on the directionality min and max
					float DampenDirectionality = FMath::Clamp(L * 100, 0.0f, 1.0f);

					for( int32 ColorIndex = 0; ColorIndex < 3; ColorIndex++ )
					{
						MinCoefficient[3][ColorIndex] = FMath::Min( MinCoefficient[3][ColorIndex], DampenDirectionality * SourceSample.Coefficients[3][ColorIndex] );
						MaxCoefficient[3][ColorIndex] = FMath::Max( MaxCoefficient[3][ColorIndex], DampenDirectionality * SourceSample.Coefficients[3][ColorIndex] );
					}
				}
			}
		}

		// If no sample mapped make range sane.
		// Or if very dark no directionality is added. Make range sane.
		for (int32 CoefficientIndex = 0; CoefficientIndex < LM_NUM_STORED_LIGHTMAP_COEF; CoefficientIndex++)
		{
			for( int32 ColorIndex = 0; ColorIndex < 4; ColorIndex++ )
			{
				if( MinCoefficient[CoefficientIndex][ColorIndex] > MaxCoefficient[CoefficientIndex][ColorIndex] )
				{
					MinCoefficient[CoefficientIndex][ColorIndex] = 0.0f;
					MaxCoefficient[CoefficientIndex][ColorIndex] = 0.0f;
				}
			}
		}

		// Calculate the scale/bias for the light-map coefficients.
		float CoefficientMultiply[LM_NUM_STORED_LIGHTMAP_COEF][4];
		float CoefficientAdd[LM_NUM_STORED_LIGHTMAP_COEF][4];

		for (int32 CoefficientIndex = 0; CoefficientIndex < LM_NUM_STORED_LIGHTMAP_COEF; CoefficientIndex++)
		{
			for (int32 ColorIndex = 0; ColorIndex < 4; ColorIndex++)
			{
				// Calculate scale and bias factors to pack into the desired range
				// y = (x - Min) / (Max - Min)
				// Mul = 1 / (Max - Min)
				// Add = -Min / (Max - Min)
				CoefficientMultiply[CoefficientIndex][ColorIndex] = 1.0f / FMath::Max<float>(MaxCoefficient[CoefficientIndex][ColorIndex] - MinCoefficient[CoefficientIndex][ColorIndex], DELTA);
				CoefficientAdd[CoefficientIndex][ColorIndex] = -MinCoefficient[CoefficientIndex][ColorIndex] / FMath::Max<float>(MaxCoefficient[CoefficientIndex][ColorIndex] - MinCoefficient[CoefficientIndex][ColorIndex], DELTA);

				// Output the values used to undo this packing
				OutMultiply[CoefficientIndex][ColorIndex] = 1.0f / CoefficientMultiply[CoefficientIndex][ColorIndex];
				OutAdd[CoefficientIndex][ColorIndex] = -CoefficientAdd[CoefficientIndex][ColorIndex] / CoefficientMultiply[CoefficientIndex][ColorIndex];
			}
		}

		// Bias to avoid divide by zero in shader
		for (int32 ColorIndex = 0; ColorIndex < 3; ColorIndex++)
		{
			OutAdd[2][ColorIndex] = FMath::Max( OutAdd[2][ColorIndex], 1e-2f );
		}

		// Force SH constant term to 0.282095f. Avoids add in shader.
		OutMultiply[1][3] = 0.0f;
		OutAdd[1][3] = 0.282095f;
		OutMultiply[3][3] = 0.0f;
		OutAdd[3][3] = 0.282095f;

		// allocate space in the output
		OutLightSamples.Empty(InLightSamples.Num());
		OutLightSamples.AddUninitialized(InLightSamples.Num());

		// quantize each sample using the above scaling
		for (int32 SampleIndex = 0; SampleIndex < InLightSamples.Num(); SampleIndex++)
		{
			const FLightSample& SourceSample = InLightSamples[SampleIndex];
			FQuantizedLightSampleData& DestCoefficients = OutLightSamples[SampleIndex];
#if ALLOW_LIGHTMAP_SAMPLE_DEBUGGING
			if (SampleIndex == DebugSampleIndex)
			{
				int32 TempBreak = 0;
			}
#endif
			DestCoefficients.Coverage = SourceSample.bIsMapped ? 255 : 0;

			const FVector BentNormal(SourceSample.SkyOcclusion[0], SourceSample.SkyOcclusion[1], SourceSample.SkyOcclusion[2]);
			const float BentNormalLength = BentNormal.Size();
			const FVector NormalizedBentNormal = BentNormal.GetSafeNormal() * FVector(.5f) + FVector(.5f);

			DestCoefficients.SkyOcclusion[0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( NormalizedBentNormal[0] * 255.0f ), 0, 255 );
			DestCoefficients.SkyOcclusion[1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( NormalizedBentNormal[1] * 255.0f ), 0, 255 );
			DestCoefficients.SkyOcclusion[2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( NormalizedBentNormal[2] * 255.0f ), 0, 255 );
			// Sqrt on length to allocate more precision near 0
			DestCoefficients.SkyOcclusion[3] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Sqrt(BentNormalLength) * 255.0f ), 0, 255 );

			// Sqrt to allocate more precision near 0
			DestCoefficients.AOMaterialMask = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Sqrt(SourceSample.AOMaterialMask) * 255.0f ), 0, 255 ); 

			{
				float L, U, V, W;
				GetLUVW( SourceSample.Coefficients[0], L, U, V, W );

				// LogLUVW encode color
				float LogL = FMath::Log2( L + LogBlackPoint );

				U = U * CoefficientMultiply[0][0] + CoefficientAdd[0][0];
				V = V * CoefficientMultiply[0][1] + CoefficientAdd[0][1];
				W = W * CoefficientMultiply[0][2] + CoefficientAdd[0][2];
				LogL = LogL * CoefficientMultiply[0][3] + CoefficientAdd[0][3];

				float Residual = LogL * 255.0f - FMath::RoundToFloat( LogL * 255.0f ) + 0.5f;

				// U, V, W, LogL
				// UVW stored in gamma space
				DestCoefficients.Coefficients[0][0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Pow( U, 1.0f / 2.2f ) * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[0][1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Pow( V, 1.0f / 2.2f ) * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[0][2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( FMath::Pow( W, 1.0f / 2.2f ) * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[0][3] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( LogL * 255.0f ), 0, 255 );
				
				float Dx = SourceSample.Coefficients[1][0] * CoefficientMultiply[1][0] + CoefficientAdd[1][0];
				float Dy = SourceSample.Coefficients[1][1] * CoefficientMultiply[1][1] + CoefficientAdd[1][1];
				float Dz = SourceSample.Coefficients[1][2] * CoefficientMultiply[1][2] + CoefficientAdd[1][2];

				// Dx, Dy, Dz, Residual
				DestCoefficients.Coefficients[1][0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dx * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[1][1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dy * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[1][2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dz * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[1][3] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Residual * 255.0f ), 0, 255 );
			}

			{
				float L, U, V, W;
				GetLUVW( SourceSample.Coefficients[2], L, U, V, W );

				// LogRGB encode color
				float LogL = FMath::Log2( L + SimpleLogBlackPoint ) / SimpleLogScale + 0.5f;

				float LogR = LogL * U * CoefficientMultiply[2][0] + CoefficientAdd[2][0];
				float LogG = LogL * V * CoefficientMultiply[2][1] + CoefficientAdd[2][1];
				float LogB = LogL * W * CoefficientMultiply[2][2] + CoefficientAdd[2][2];
				
				// LogR, LogG, LogB
				DestCoefficients.Coefficients[2][0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( LogR * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[2][1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( LogG * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[2][2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( LogB * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[2][3] = 255;

				float Dx = SourceSample.Coefficients[3][0] * CoefficientMultiply[3][0] + CoefficientAdd[3][0];
				float Dy = SourceSample.Coefficients[3][1] * CoefficientMultiply[3][1] + CoefficientAdd[3][1];
				float Dz = SourceSample.Coefficients[3][2] * CoefficientMultiply[3][2] + CoefficientAdd[3][2];

				// Dx, Dy, Dz
				DestCoefficients.Coefficients[3][0] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dx * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[3][1] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dy * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[3][2] = (uint8)FMath::Clamp<int32>( FMath::RoundToInt( Dz * 255.0f ), 0, 255 );
				DestCoefficients.Coefficients[3][3] = 255;
			}
		}

		InLightSamples.Empty();
	}