void APawnWithCamera::DoTrace()
{
	FVector Loc = CameraOne->GetActorLocation();
	UE_LOG(LogClass, Error, TEXT("Loc is %s"), *Loc.ToString());
	FRotator Rot = CameraOne->GetActorRotation();
	UE_LOG(LogClass, Error, TEXT("Rot is %s"), *Rot.ToString());
	FVector Start = Loc;
	UE_LOG(LogClass, Error, TEXT("Start is %s"), *Start.ToString());
	FVector End = Loc + (Rot.Vector() * Distance);
	UE_LOG(LogClass, Error, TEXT("End is %s"), *End.ToString());
	TempActor->SetActorLocation(End);

	FCollisionQueryParams TraceParam = FCollisionQueryParams(FName(TEXT("Trace")), true, this);
	TraceParam.bTraceComplex = true;
	TraceParam.bTraceAsyncScene = true;
	TraceParam.bReturnPhysicalMaterial = false;
	TraceParam.AddIgnoredActor(this);

	FHitResult Hit(ForceInit);

	GetWorld()->LineTraceSingle(Hit, Start, End, ECC_Pawn, TraceParam);
	DrawDebugLine(GetWorld(), Start, End, FColor(255, 0, 0), false, -1, 0, 12.33f);

	if (Hit.bBlockingHit)
	{
		UE_LOG(LogClass, Error, TEXT("Hit Something"));
	}
	else
	{
		UE_LOG(LogClass, Error, TEXT("No Hit"));
	}
}
void UFlareSpacecraftNavigationSystem::PushCommandRotation(const FVector& RotationTarget, const FVector& LocalShipAxis)
{
	if (RotationTarget.IsNearlyZero() || LocalShipAxis.IsNearlyZero())
	{
		return;
	}

	FFlareShipCommandData Command;
	Command.Type = EFlareCommandDataType::CDT_Rotation;
	Command.RotationTarget = RotationTarget;
	Command.LocalShipAxis = LocalShipAxis;
	FLOGV("UFlareSpacecraftNavigationSystem::PushCommandRotation RotationTarget '%s'", *RotationTarget.ToString());
	FLOGV("UFlareSpacecraftNavigationSystem::PushCommandRotation LocalShipAxis '%s'", *LocalShipAxis.ToString());
	PushCommand(Command);
}
void UTankMovementComponent::RequestDirectMove(const FVector& MoveVelocity, bool bForceMaxSpeed)
{
	//No need to call super() as we're replacing the functionality
	auto VelocityString = MoveVelocity.ToString();
	auto Name = GetOwner()->GetName();
	UE_LOG(LogTemp, Warning, TEXT("%s Requesting move: %s"), *Name,*VelocityString);
}
FQuat FAnimNode_RotationMultiplier::MultiplyQuatBasedOnSourceIndex(const FTransform& RefPoseTransform, const FTransform& LocalBoneTransform, const EBoneAxis Axis, float InMultiplier, const FQuat& ReferenceQuat)
{
	// Find delta angle for source bone.
	FQuat DeltaQuat = ExtractAngle(RefPoseTransform, LocalBoneTransform, Axis);

	// Turn to Axis and Angle
	FVector RotationAxis;
	float	RotationAngle;
	DeltaQuat.ToAxisAndAngle(RotationAxis, RotationAngle);

	const FVector DefaultAxis = GetAxisVector(Axis);

	// See if we need to invert angle - shortest path
	if( (RotationAxis | DefaultAxis) < 0.f )
	{
		RotationAxis = -RotationAxis;
		RotationAngle = -RotationAngle;
	}

	// Make sure it is the shortest angle.
	RotationAngle = FMath::UnwindRadians(RotationAngle);

	// New bone rotation
	FQuat OutQuat = ReferenceQuat * FQuat(RotationAxis, RotationAngle* InMultiplier);
	// Normalize resulting quaternion.
	OutQuat.Normalize();

#if 0 //DEBUG_TWISTBONECONTROLLER
	UE_LOG(LogSkeletalControl, Log, TEXT("\t RefQuat: %s, Rot: %s"), *ReferenceQuat.ToString(), *ReferenceQuat.Rotator().ToString() );
	UE_LOG(LogSkeletalControl, Log, TEXT("\t NewQuat: %s, Rot: %s"), *OutQuat.ToString(), *OutQuat.Rotator().ToString() );
	UE_LOG(LogSkeletalControl, Log, TEXT("\t RollAxis: %s, RollAngle: %f"), *RotationAxis.ToString(), RotationAngle );
#endif

	return OutQuat;
}
Beispiel #5
0
void UCheatManager::BugItStringCreator( FVector ViewLocation, FRotator ViewRotation, FString& GoString, FString& LocString )
{
	GoString = FString::Printf(TEXT("BugItGo %f %f %f %f %f %f"), ViewLocation.X, ViewLocation.Y, ViewLocation.Z, ViewRotation.Pitch, ViewRotation.Yaw, ViewRotation.Roll);
	UE_LOG(LogCheatManager, Log, TEXT("%s"), *GoString);

	LocString = FString::Printf(TEXT("?BugLoc=%s?BugRot=%s"), *ViewLocation.ToString(), *ViewRotation.ToString());
	UE_LOG(LogCheatManager, Log, TEXT("%s"), *LocString);
}
void UAURSmoothingFilterKalman::Measurement(FTransform const & MeasuredTransform)
{
	try
	{
		FVector translation = MeasuredTransform.GetTranslation();

		FString msg = "In: " + translation.ToString();
		//FQuat rotation = MeasuredTransform.GetRotation();

		TransformAsMat.at<float>(0) = translation.X;
		TransformAsMat.at<float>(1) = translation.Y;
		TransformAsMat.at<float>(2) = translation.Z;
		//TransformAsMat.at<float>(3) = rotation.X * rotation.W;
		//TransformAsMat.at<float>(4) = rotation.Y * rotation.W;
		//TransformAsMat.at<float>(5) = rotation.Z * rotation.W;

		cv::Mat FilterResult = Filter.predict();
		Filter.correct(TransformAsMat);

		translation.Set(FilterResult.at<float>(0), FilterResult.at<float>(1), FilterResult.at<float>(2));

		msg += "\nOut: " + translation.ToString();

		UE_LOG(LogAUR, Log, TEXT("%s"), *msg);

		//FVector rot_axis(FilterResult.at<float>(3), FilterResult.at<float>(4), FilterResult.at<float>(5));
		//float rot_magnitude = rot_axis.Size();
		//rot_axis.Normalize();

		//rotation.X = rot_axis.X;
		//rotation.Y = rot_axis.Y;
		//rotation.Z = rot_axis.Z;
		//rotation.W = rot_magnitude;
	
		CurrentTransform.SetComponents(MeasuredTransform.GetRotation(), translation, FVector(1, 1, 1));
	}
	catch (cv::Exception& e)
	{
		FString exception_msg(e.what());

		UE_LOG(LogAUR, Error, TEXT("Kalman: %s"), *exception_msg)

		CurrentTransform = MeasuredTransform;
	}
}
// Called when the game starts
void UPositionReport::BeginPlay()
{
	Super::BeginPlay();
	FString ObjectName = GetOwner()->GetName();
	FVector ObjectPos = GetOwner()->GetActorLocation();
	UE_LOG(LogTemp, Warning, TEXT("%s is at %s"), *ObjectName, *ObjectPos.ToString());
	// ...
	
}
Beispiel #8
0
// Called when the game starts or when spawned
void AMazeWalls::BeginPlay()
{
	Super::BeginPlay();
	
	// Spawn a wall. (Same idea as in the puzzle code!)
	const FVector WallLocation = FVector(10.f, 10.f, 0.f) + GetActorLocation();
	AWall* NewWall = GetWorld()->SpawnActor<AWall>(WallLocation, FRotator(0, 0, 0));

	 // Spits out the wall's position to the screen.
	if (GEngine)
		GEngine->AddOnScreenDebugMessage(1, 5.f, FColor::Red, "Start Point" + WallLocation.ToString());

}
void AHeatmapDataCollector::CollectData()
{
	//if (!FPlatformFileManager::Get().GetPlatformFile().DirectoryExists(*SaveDir))											// Dir exist?
	//{
	//	FPlatformFileManager::Get().GetPlatformFile().CreateDirectory(*SaveDir);											// create if it not exist
	//	if (!FPlatformFileManager::Get().GetPlatformFile().DirectoryExists(*SaveDir))										//still could not make directory?
	//	{
	//		return false;																									//Could not make the specified directory
	//	}
	//	auto FileToSave = SaveDir + "/" + FileName;
	//	if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*FileToSave))	return false;
	//}
	if (bCollectData)
	{
		if (CharCurrentPtr)
		{
			FVector CALoc = CharCurrentPtr->GetActorLocation();

			/*if (SplineDataSwitcher == ESplineDataSwitcher::ES_ArrayData)
			{
				auto FileToSaveArr = SaveDir + "/" + "ArrayLocationLogs.txt";
				static TArray<int16> ArrayToFile;
				ArrayToFile.Add(CALoc.X);
				ArrayToFile.Add(CALoc.Y);
				ArrayToFile.Add(CALoc.Z);
				SaveArrayToFile(ArrayToFile, *FileToSaveArr);
				return true;
			}*/
			if (SplineDataSwitcher == ESplineDataSwitcher::ES_StringData)
			{
				auto FileToSaveArr = SaveDirectoryPath + "/" + LogFileName + "_" + FString::FromInt(CharNumberInWorld) + ".txt";

				const FString& loc = CALoc.ToString();
				const TCHAR* StrPtr = *loc;

				StringOfCoords.Append(" line");
				StringOfCoords.Append(FString::FromInt(it));
				StringOfCoords.Append(" ");
				StringOfCoords.Append(StrPtr);
				StringOfCoords.Append("\n");
				it++;
				FFileHelper::SaveStringToFile(*StringOfCoords, *FileToSaveArr);//FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM

			}
		}
	}
	if (CharCurrentPtr)
	{
		GetWorldTimerManager().SetTimer(ChekTimeHandler, this, &AHeatmapDataCollector::CollectData, UpdateTimeBetweenChek, true);
	}
}
FString UKismetStringLibrary::BuildString_Vector(const FString& AppendTo, const FString& Prefix, FVector InVector, const FString& Suffix)
{
	// faster, preallocating method
	FString const VecStr = InVector.ToString();

	FString StringResult;
	StringResult.Empty(AppendTo.Len()+Prefix.Len()+VecStr.Len()+Suffix.Len()+1); // adding one for the string terminator
	StringResult += AppendTo;
	StringResult += Prefix;
	StringResult += VecStr;
	StringResult += Suffix;

	return StringResult;
}
Beispiel #11
0
void UCheatManager::BugItWorker( FVector TheLocation, FRotator TheRotation )
{
	UE_LOG(LogCheatManager, Log,  TEXT("BugItGo to: %s %s"), *TheLocation.ToString(), *TheRotation.ToString() );

	Ghost();

	APlayerController* const MyPlayerController = GetOuterAPlayerController();
	if (MyPlayerController->GetPawn())
	{
		MyPlayerController->GetPawn()->TeleportTo( TheLocation, TheRotation );
		MyPlayerController->GetPawn()->FaceRotation( TheRotation, 0.0f );
	}
	MyPlayerController->SetControlRotation(TheRotation);
}
FQuat FAnimNode_RotationMultiplier::ExtractAngle(const TArray<FTransform> & RefPoseTransforms, FA2CSPose & MeshBases, const EBoneAxis Axis,  int32 SourceBoneIndex)
{
	// local bone transform
	const FTransform & LocalBoneTransform = MeshBases.GetLocalSpaceTransform(SourceBoneIndex);
	// local bone transform with reference rotation
	FTransform ReferenceBoneTransform = RefPoseTransforms[SourceBoneIndex];
	ReferenceBoneTransform.SetTranslation(LocalBoneTransform.GetTranslation());

	// find delta angle between the two quaternions X Axis.
	const FVector RotationAxis = GetAxisVector(Axis);
	const FVector LocalRotationVector = LocalBoneTransform.GetRotation().RotateVector(RotationAxis);
	const FVector ReferenceRotationVector = ReferenceBoneTransform.GetRotation().RotateVector(RotationAxis);

	const FQuat LocalToRefQuat = FQuat::FindBetween(LocalRotationVector, ReferenceRotationVector);
	checkSlow( LocalToRefQuat.IsNormalized() );

	// Rotate parent bone atom from position in local space to reference skeleton
	// Since our rotation rotates both vectors with shortest arc
	// we're essentially left with a quaternion that has angle difference with reference skeleton version
	const FQuat BoneQuatAligned = LocalToRefQuat * LocalBoneTransform.GetRotation();
	checkSlow( BoneQuatAligned.IsNormalized() );

	// Find that delta angle
	const FQuat DeltaQuat = (ReferenceBoneTransform.GetRotation().Inverse()) * BoneQuatAligned;
	checkSlow( DeltaQuat.IsNormalized() );

#if 0 //DEBUG_TWISTBONECONTROLLER
	UE_LOG(LogSkeletalControl, Log, TEXT("\t ExtractAngle, Bone: %s (%d)"), 
		*SourceBone.BoneName.ToString(), SourceBoneIndex);
	UE_LOG(LogSkeletalControl, Log, TEXT("\t\t Bone Quat: %s, Rot: %s, AxisX: %s"), *LocalBoneTransform.GetRotation().ToString(), *LocalBoneTransform.GetRotation().Rotator().ToString(), *LocalRotationVector.ToString() );
	UE_LOG(LogSkeletalControl, Log, TEXT("\t\t BoneRef Quat: %s, Rot: %s, AxisX: %s"), *ReferenceBoneTransform.GetRotation().ToString(), *ReferenceBoneTransform.GetRotation().Rotator().ToString(), *ReferenceRotationVector.ToString() );
	UE_LOG(LogSkeletalControl, Log, TEXT("\t\t LocalToRefQuat Quat: %s, Rot: %s"), *LocalToRefQuat.ToString(), *LocalToRefQuat.Rotator().ToString() );

	const FVector BoneQuatAlignedX = LocalBoneTransform.GetRotation().RotateVector(RotationAxis);
	UE_LOG(LogSkeletalControl, Log, TEXT("\t\t BoneQuatAligned Quat: %s, Rot: %s, AxisX: %s"), *BoneQuatAligned.ToString(), *BoneQuatAligned.Rotator().ToString(), *BoneQuatAlignedX.ToString() );
	UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaQuat Quat: %s, Rot: %s"), *DeltaQuat.ToString(), *DeltaQuat.Rotator().ToString() );

	FTransform BoneAtomAligned(BoneQuatAligned, ReferenceBoneTransform.GetTranslation());
	const FQuat DeltaQuatAligned = FQuat::FindBetween(BoneAtomAligned.GetScaledAxis( EAxis::X ), ReferenceBoneTransform.GetScaledAxis( EAxis::X ));
	UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaQuatAligned Quat: %s, Rot: %s"), *DeltaQuatAligned.ToString(), *DeltaQuatAligned.Rotator().ToString() );
	FVector DeltaAxis;
	float	DeltaAngle;
	DeltaQuat.ToAxisAndAngle(DeltaAxis, DeltaAngle);
	UE_LOG(LogSkeletalControl, Log, TEXT("\t\t DeltaAxis: %s, DeltaAngle: %f"), *DeltaAxis.ToString(), DeltaAngle );
#endif

	return DeltaQuat;
}
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;
}
	void CheckVector( FVector ResultVector, FVector ExpectedVector, FString TestName, FString ParameterName, int32 TestIndex, float Tolerance = KINDA_SMALL_NUMBER )
	{
		const FVector Delta = ExpectedVector - ResultVector;
		if (Delta.SizeSquared() > FMath::Square(Tolerance))
		{
			//UE_LOG(CollisionAutomationTestLog, Log, TEXT("%d:HitResult=(%s)"), iTest+1, *OutHits[iHits].ToString());
			TestBase->AddError(FString::Printf(TEXT("Test %d:%s %s mismatch. Should be %s but is actually %s."), TestIndex, *TestName, *ParameterName, *ExpectedVector.ToString(), *ResultVector.ToString()));
		}
	}
FString UKismetStringLibrary::Conv_VectorToString(FVector InVec)
{
	return InVec.ToString();	
}
/** Util to find the normal of the face that we hit */
static bool FindGeomOpposingNormal(const PxLocationHit& PHit, FVector& OutNormal, FVector& OutPointOnGeom)
{
	PxTriangleMeshGeometry PTriMeshGeom;
	PxConvexMeshGeometry PConvexMeshGeom;
	if(	PHit.shape->getTriangleMeshGeometry(PTriMeshGeom) && 
		PTriMeshGeom.triangleMesh != NULL &&
		PHit.faceIndex < PTriMeshGeom.triangleMesh->getNbTriangles() )
	{
		const int32 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, and convert into world space
		PxVec3 PLocalTriNormal = ((V1 - V0).cross(V2 - V0)).getNormalized();
		TransformNormalToShapeSpace(PTriMeshGeom.scale, PLocalTriNormal, PLocalTriNormal);
	
		const PxTransform PShapeWorldPose = PxShapeExt::getGlobalPose(*PHit.shape, *PHit.actor); 

		const PxVec3 PWorldTriNormal = PShapeWorldPose.rotate(PLocalTriNormal);
		OutNormal = P2UVector(PWorldTriNormal);
		
		if (PTriMeshGeom.scale.isIdentity())
		{
			OutPointOnGeom = P2UVector(PShapeWorldPose.transform(V0));
		}

#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 true;
	}
	else if(PHit.shape->getConvexMeshGeometry(PConvexMeshGeom) && 
		PConvexMeshGeom.convexMesh != NULL &&
		PHit.faceIndex < PConvexMeshGeom.convexMesh->getNbPolygons() )
	{
		const int32 PolyIndex = PHit.faceIndex;
		PxHullPolygon PPoly;
		bool bSuccess = PConvexMeshGeom.convexMesh->getPolygonData(PolyIndex, PPoly);
		if(bSuccess)
		{
			PxVec3 PPlaneNormal(PPoly.mPlane[0], PPoly.mPlane[1], PPoly.mPlane[2]);

			// Convert to local space, taking scale into account.
			// TODO: can we just change the hit normal within the physx code where we choose the most opposing normal, and avoid doing this here again?
			PxVec3 PLocalPolyNormal;
			TransformNormalToShapeSpace(PConvexMeshGeom.scale, PPlaneNormal.getNormalized(), PLocalPolyNormal);			
			const PxTransform PShapeWorldPose = PHit.actor->getGlobalPose() * PHit.shape->getLocalPose(); 

			const PxVec3 PWorldPolyNormal = PShapeWorldPose.rotate(PLocalPolyNormal);
			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 true;
		}
	}

	return false;
}
bool UMovementComponent::ResolvePenetrationImpl(const FVector& ProposedAdjustment, const FHitResult& Hit, const FQuat& NewRotationQuat)
{
	// SceneComponent can't be in penetration, so this function really only applies to PrimitiveComponent.
	const FVector Adjustment = ConstrainDirectionToPlane(ProposedAdjustment);
	if (!Adjustment.IsZero() && UpdatedPrimitive)
	{
		// See if we can fit at the adjusted location without overlapping anything.
		AActor* ActorOwner = UpdatedComponent->GetOwner();
		if (!ActorOwner)
		{
			return false;
		}

		UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration: %s.%s at location %s inside %s.%s at location %s by %.3f (netmode: %d)"),
			   *ActorOwner->GetName(),
			   *UpdatedComponent->GetName(),
			   *UpdatedComponent->GetComponentLocation().ToString(),
			   *GetNameSafe(Hit.GetActor()),
			   *GetNameSafe(Hit.GetComponent()),
			   Hit.Component.IsValid() ? *Hit.GetComponent()->GetComponentLocation().ToString() : TEXT("<unknown>"),
			   Hit.PenetrationDepth,
			   (uint32)GetNetMode());

		// We really want to make sure that precision differences or differences between the overlap test and sweep tests don't put us into another overlap,
		// so make the overlap test a bit more restrictive.
		const float OverlapInflation = CVarPenetrationOverlapCheckInflation.GetValueOnGameThread();
		bool bEncroached = OverlapTest(Hit.TraceStart + Adjustment, NewRotationQuat, UpdatedPrimitive->GetCollisionObjectType(), UpdatedPrimitive->GetCollisionShape(OverlapInflation), ActorOwner);
		if (!bEncroached)
		{
			// Move without sweeping.
			MoveUpdatedComponent(Adjustment, NewRotationQuat, false, nullptr, ETeleportType::TeleportPhysics);
			UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration:   teleport by %s"), *Adjustment.ToString());
			return true;
		}
		else
		{
			// Disable MOVECOMP_NeverIgnoreBlockingOverlaps if it is enabled, otherwise we wouldn't be able to sweep out of the object to fix the penetration.
			TGuardValue<EMoveComponentFlags> ScopedFlagRestore(MoveComponentFlags, EMoveComponentFlags(MoveComponentFlags & (~MOVECOMP_NeverIgnoreBlockingOverlaps)));

			// Try sweeping as far as possible...
			FHitResult SweepOutHit(1.f);
			bool bMoved = MoveUpdatedComponent(Adjustment, NewRotationQuat, true, &SweepOutHit, ETeleportType::TeleportPhysics);
			UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration:   sweep by %s (success = %d)"), *Adjustment.ToString(), bMoved);
			
			// Still stuck?
			if (!bMoved && SweepOutHit.bStartPenetrating)
			{
				// Combine two MTD results to get a new direction that gets out of multiple surfaces.
				const FVector SecondMTD = GetPenetrationAdjustment(SweepOutHit);
				const FVector CombinedMTD = Adjustment + SecondMTD;
				if (SecondMTD != Adjustment && !CombinedMTD.IsZero())
				{
					bMoved = MoveUpdatedComponent(CombinedMTD, NewRotationQuat, true, nullptr, ETeleportType::TeleportPhysics);
					UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration:   sweep by %s (MTD combo success = %d)"), *CombinedMTD.ToString(), bMoved);
				}
			}

			// Still stuck?
			if (!bMoved)
			{
				// Try moving the proposed adjustment plus the attempted move direction. This can sometimes get out of penetrations with multiple objects
				const FVector MoveDelta = ConstrainDirectionToPlane(Hit.TraceEnd - Hit.TraceStart);
				if (!MoveDelta.IsZero())
				{
					bMoved = MoveUpdatedComponent(Adjustment + MoveDelta, NewRotationQuat, true, nullptr, ETeleportType::TeleportPhysics);
					UE_LOG(LogMovement, Verbose, TEXT("ResolvePenetration:   sweep by %s (adjusted attempt success = %d)"), *(Adjustment + MoveDelta).ToString(), bMoved);
				}
			}	

			return bMoved;
		}
	}

	return false;
}
void UInterpToMovementComponent::AddControlPointPosition(FVector Pos)
{
	UE_LOG(LogInterpToMovementComponent, Warning, TEXT("Pos:%s"),*Pos.ToString());
	ControlPoints.Add( FInterpControlPoint(Pos));
}
void UFlareSpacecraftNavigationSystem::PhysicSubTick(float DeltaSeconds)
{
	TArray<UActorComponent*> Engines = Spacecraft->GetComponentsByClass(UFlareEngine::StaticClass());
	if (Spacecraft->GetDamageSystem()->IsPowered())
	{
		// Linear physics
		FVector DeltaV = LinearTargetVelocity - Spacecraft->GetLinearVelocity();
		FVector DeltaVAxis = DeltaV;
		DeltaVAxis.Normalize();

		bool Log = false;
		if (Spacecraft->GetParent()->GetCompany() == Spacecraft->GetGame()->GetPC()->GetCompany())
		{
			//Log = true;
		}

		bool HasUsedOrbitalBoost = false;
		float LinearMasterAlpha = 0.f;
		float LinearMasterBoostAlpha = 0.f;

		if(Log) {
			FLOGV("PhysicSubTick DeltaSeconds=%f", DeltaSeconds);

			FLOGV("PhysicSubTick LinearTargetVelocity=%s", *LinearTargetVelocity.ToString());
			FLOGV("PhysicSubTick Spacecraft->GetLinearVelocity()=%s", *Spacecraft->GetLinearVelocity().ToString());

			FLOGV("PhysicSubTick DeltaV=%s", *DeltaV.ToString());
		}

		if (!DeltaV.IsNearlyZero())
		{
			// First, try without using the boost
			FVector Acceleration = DeltaVAxis * GetTotalMaxThrustInAxis(Engines, -DeltaVAxis, false).Size() / Spacecraft->GetSpacecraftMass();

			float AccelerationDeltaV = Acceleration.Size() * DeltaSeconds;

			LinearMasterAlpha = FMath::Clamp(DeltaV.Size()/ AccelerationDeltaV, 0.0f, 1.0f);
			// Second, if the not enought trust check with the boost
			if (UseOrbitalBoost && AccelerationDeltaV < DeltaV.Size() )
			{
				FVector AccelerationWithBoost = DeltaVAxis * GetTotalMaxThrustInAxis(Engines, -DeltaVAxis, true).Size() / Spacecraft->GetSpacecraftMass();
				if (AccelerationWithBoost.Size() > Acceleration.Size())
				{
					HasUsedOrbitalBoost = true;


					float BoostDeltaV = (AccelerationWithBoost.Size() - Acceleration.Size()) * DeltaSeconds;
					float DeltaVAfterClassicalAcceleration = DeltaV.Size() - AccelerationDeltaV;

					LinearMasterBoostAlpha = FMath::Clamp(DeltaVAfterClassicalAcceleration/ BoostDeltaV, 0.0f, 1.0f);

					Acceleration = AccelerationWithBoost;
				}
			}

			FVector ClampedAcceleration = Acceleration.GetClampedToMaxSize(DeltaV.Size() / DeltaSeconds);



			Spacecraft->Airframe->SetPhysicsLinearVelocity(ClampedAcceleration * DeltaSeconds * 100, true); // Multiply by 100 because UE4 works in cm
		}

		// Angular physics
		FVector DeltaAngularV = AngularTargetVelocity - Spacecraft->Airframe->GetPhysicsAngularVelocity();
		FVector DeltaAngularVAxis = DeltaAngularV;
		DeltaAngularVAxis.Normalize();

		if (!DeltaAngularV.IsNearlyZero())
		{
			FVector SimpleAcceleration = DeltaAngularVAxis * AngularAccelerationRate;

			// Scale with damages
			float TotalMaxTorqueInAxis = GetTotalMaxTorqueInAxis(Engines, DeltaAngularVAxis, false);
			if (!FMath::IsNearlyZero(TotalMaxTorqueInAxis))
			{
				float DamageRatio = GetTotalMaxTorqueInAxis(Engines, DeltaAngularVAxis, true) / TotalMaxTorqueInAxis;
				FVector DamagedSimpleAcceleration = SimpleAcceleration * DamageRatio;
				FVector ClampedSimplifiedAcceleration = DamagedSimpleAcceleration.GetClampedToMaxSize(DeltaAngularV.Size() / DeltaSeconds);

				Spacecraft->Airframe->SetPhysicsAngularVelocity(ClampedSimplifiedAcceleration  * DeltaSeconds, true);
			}
		}

		// Update engine alpha
		for (int32 EngineIndex = 0; EngineIndex < Engines.Num(); EngineIndex++)
		{
			UFlareEngine* Engine = Cast<UFlareEngine>(Engines[EngineIndex]);
			FVector ThrustAxis = Engine->GetThrustAxis();
			float LinearAlpha = 0;
			float AngularAlpha = 0;

			if (Spacecraft->IsPresentationMode())
			{
				LinearAlpha = true;
			}
			else if (!DeltaV.IsNearlyZero() || !DeltaAngularV.IsNearlyZero())
			{
				if(Engine->IsA(UFlareOrbitalEngine::StaticClass()))
				{
					if(HasUsedOrbitalBoost)
					{
						LinearAlpha = (-FVector::DotProduct(ThrustAxis, DeltaVAxis) + 0.2) * LinearMasterBoostAlpha;
					}
					AngularAlpha = 0;
				}
				else
				{
					LinearAlpha = -FVector::DotProduct(ThrustAxis, DeltaVAxis) * LinearMasterAlpha;
					FVector EngineOffset = (Engine->GetComponentLocation() - COM) / 100;
					FVector TorqueDirection = FVector::CrossProduct(EngineOffset, ThrustAxis);
					TorqueDirection.Normalize();

					if (!DeltaAngularV.IsNearlyZero() && !Engine->IsA(UFlareOrbitalEngine::StaticClass()))
					{
						AngularAlpha = -FVector::DotProduct(TorqueDirection, DeltaAngularVAxis);
					}
				}
			}

			Engine->SetAlpha(FMath::Clamp(LinearAlpha + AngularAlpha, 0.0f, 1.0f));
		}
	}
	else
	{
		// Shutdown engines
		for (int32 EngineIndex = 0; EngineIndex < Engines.Num(); EngineIndex++)
		{
			UFlareEngine* Engine = Cast<UFlareEngine>(Engines[EngineIndex]);
			Engine->SetAlpha(0);
		}
	}
}
void UBlendSpaceBase::TickAssetPlayerInstance(const FAnimTickRecord& Instance, class UAnimInstance* InstanceOwner, FAnimAssetTickContext& Context) const
{
	const float DeltaTime = Context.GetDeltaTime();
	float MoveDelta = Instance.PlayRateMultiplier * DeltaTime;

	// this happens even if MoveDelta == 0.f. This still should happen if it is being interpolated
	// since we allow setting position of blendspace, we can't ignore MoveDelta == 0.f
	// also now we don't have to worry about not following if DeltaTime = 0.f
	{
		// first filter input using blend filter
		const FVector BlendInput = FilterInput(Instance.BlendFilter, Instance.BlendSpacePosition, DeltaTime);
		EBlendSpaceAxis AxisToScale = GetAxisToScale();
		if (AxisToScale != BSA_None)
		{
			float FilterMultiplier = 1.f;
			// first use multiplier using new blendinput
			// new filtered input is going to be used for sampling animation
			// so we'll need to change playrate if you'd like to not slide foot
			if ( !Instance.BlendSpacePosition.Equals(BlendInput) )  
			{
				// apply speed change if you want, 
				if (AxisToScale == BSA_X)
				{
					if (BlendInput.X != 0.f)
					{
						FilterMultiplier = Instance.BlendSpacePosition.X / BlendInput.X;
					}
				}
				else if (AxisToScale == BSA_Y)
				{
					if (BlendInput.Y != 0.f)
					{
						FilterMultiplier = Instance.BlendSpacePosition.Y / BlendInput.Y;
					}
				}
			}

			// now find if clamped input is different
			// if different, then apply scale to fit in
			FVector ClampedInput = ClampBlendInput(BlendInput);
			if ( !ClampedInput.Equals(BlendInput) ) 
			{
				// apply speed change if you want, 
				if (AxisToScale == BSA_X)
				{
					if (ClampedInput.X != 0.f)
					{
						FilterMultiplier *= BlendInput.X / ClampedInput.X;
					}
				}
				else if (AxisToScale == BSA_Y)
				{
					if (ClampedInput.Y != 0.f)
					{
						FilterMultiplier *= BlendInput.Y / ClampedInput.Y;
					}
				}
			}


			MoveDelta *= FilterMultiplier;
			UE_LOG(LogAnimation, Log, TEXT("BlendSpace(%s) - BlendInput(%s) : FilteredBlendInput(%s), FilterMultiplier(%0.2f)"), *GetName(), *Instance.BlendSpacePosition.ToString(), *BlendInput.ToString(), FilterMultiplier );
		}

		check(Instance.BlendSampleDataCache);

		// For Target weight interpolation, we'll need to save old data, and interpolate to new data
		static TArray<FBlendSampleData> OldSampleDataList;
		static TArray<FBlendSampleData> NewSampleDataList;
		check(IsInGameThread() && !OldSampleDataList.Num() && !NewSampleDataList.Num()); // this must be called non-recursively on the game thread

		OldSampleDataList.Append(*Instance.BlendSampleDataCache);

		// get sample data based on new input
		// consolidate all samples and sort them, so that we can handle from biggest weight to smallest
		Instance.BlendSampleDataCache->Reset();
		// new sample data that will be used for evaluation
		TArray<FBlendSampleData> & SampleDataList = *Instance.BlendSampleDataCache;

		// get sample data from blendspace
		if (GetSamplesFromBlendInput(BlendInput, NewSampleDataList))
		{
			float NewAnimLength=0;

			// if target weight interpolation is set
			if (TargetWeightInterpolationSpeedPerSec > 0.f)
			{
				UE_LOG(LogAnimation, Verbose, TEXT("Target Weight Interpolation: Target Samples "));
				// recalculate AnimLength based on weight of target animations - this is used for scaling animation later (change speed)
				float PreInterpAnimLength = GetAnimationLengthFromSampleData(NewSampleDataList);
				UE_LOG(LogAnimation, Verbose, TEXT("BlendSpace(%s) - BlendInput(%s) : PreAnimLength(%0.5f) "), *GetName(), *BlendInput.ToString(), PreInterpAnimLength);

				// target weight interpolation
				if (InterpolateWeightOfSampleData(DeltaTime, OldSampleDataList, NewSampleDataList, SampleDataList))
				{
					// now I need to normalize
					NormalizeSampleDataWeight(SampleDataList);
				}
				else
				{
					// if interpolation failed, just copy new sample data tto sample data
					SampleDataList = NewSampleDataList;
				}

				// recalculate AnimLength based on weight of animations
				UE_LOG(LogAnimation, Verbose, TEXT("Target Weight Interpolation: Interp Samples "));
				NewAnimLength = GetAnimationLengthFromSampleData(SampleDataList);
				// now scale the animation
				if (NewAnimLength > 0.f)
				{
					MoveDelta *= PreInterpAnimLength/NewAnimLength;
				}
			}
			else
			{
				// when there is no target weight interpolation, just copy new to target
				SampleDataList.Append(NewSampleDataList);
				NewAnimLength = GetAnimationLengthFromSampleData(SampleDataList);
			}

			float& NormalizedCurrentTime = *(Instance.TimeAccumulator);
			const float NormalizedPreviousTime = NormalizedCurrentTime;

			if (Context.IsLeader())
			{
				// advance current time - blend spaces hold normalized time as when dealing with changing anim length it would be possible to go backwards
				UE_LOG(LogAnimation, Verbose, TEXT("BlendSpace(%s) - BlendInput(%s) : AnimLength(%0.5f) "), *GetName(), *BlendInput.ToString(), NewAnimLength);

				// Advance time using current/new anim length
				float CurrentTime = NormalizedCurrentTime * NewAnimLength;
				FAnimationRuntime::AdvanceTime(Instance.bLooping, MoveDelta, /*inout*/ CurrentTime, NewAnimLength);
				NormalizedCurrentTime = NewAnimLength ? (CurrentTime / NewAnimLength) : 0.0f;

				Context.SetSyncPoint(NormalizedCurrentTime);
			}
			else
			{
				NormalizedCurrentTime = Context.GetSyncPoint();
			}

			// generate notifies and sets time
			{
				TArray<const FAnimNotifyEvent*> Notifies;

				// now calculate time for each samples
				const float ClampedNormalizedPreviousTime = FMath::Clamp<float>(NormalizedPreviousTime, 0.f, 1.f);
				const float ClampedNormalizedCurrentTime = FMath::Clamp<float>(NormalizedCurrentTime, 0.f, 1.f);
				const bool bGenerateNotifies = Context.ShouldGenerateNotifies() && (NormalizedCurrentTime != NormalizedPreviousTime) && NotifyTriggerMode != ENotifyTriggerMode::None;
				int32 HighestWeightIndex = 0;

				// Get the index of the highest weight, assuming that the first is the highest until we find otherwise
				bool bTriggerNotifyHighestWeightedAnim = NotifyTriggerMode == ENotifyTriggerMode::HighestWeightedAnimation && SampleDataList.Num() > 0;
				if(bGenerateNotifies && bTriggerNotifyHighestWeightedAnim)
				{
					float HighestWeight = SampleDataList[HighestWeightIndex].GetWeight();
					for(int32 I = 1 ; I < SampleDataList.Num(); I++)
					{
						if(SampleDataList[I].GetWeight() > HighestWeight)
						{
							HighestWeightIndex = I;
							HighestWeight = SampleDataList[I].GetWeight();
						}
					}
				}
				
				for (int32 I = 0; I < SampleDataList.Num(); ++I)
				{
					FBlendSampleData& SampleEntry = SampleDataList[I];
					const int32 SampleDataIndex = SampleEntry.SampleDataIndex;

					// Skip SamplesPoints that has no relevant weight
					if( SampleData.IsValidIndex(SampleDataIndex) && (SampleEntry.TotalWeight > ZERO_ANIMWEIGHT_THRESH) )
					{
						const FBlendSample& Sample = SampleData[SampleDataIndex];
						if( Sample.Animation )
						{
							const float SampleNormalizedPreviousTime = Sample.Animation->RateScale >= 0.f ? ClampedNormalizedPreviousTime : 1.f - ClampedNormalizedPreviousTime;
							const float SampleNormalizedCurrentTime = Sample.Animation->RateScale >= 0.f ? ClampedNormalizedCurrentTime : 1.f - ClampedNormalizedCurrentTime;

							const float PrevSampleDataTime = SampleNormalizedPreviousTime * Sample.Animation->SequenceLength;
							float& CurrentSampleDataTime = SampleEntry.Time;
							CurrentSampleDataTime = SampleNormalizedCurrentTime * Sample.Animation->SequenceLength;

							// Figure out delta time 
							float DeltaTimePosition = CurrentSampleDataTime - PrevSampleDataTime;
							const float SampleMoveDelta = MoveDelta * Sample.Animation->RateScale;

							// if we went against play rate, then loop around.
							if ((SampleMoveDelta * DeltaTimePosition) < 0.f)
							{
								DeltaTimePosition += FMath::Sign<float>(SampleMoveDelta) * Sample.Animation->SequenceLength;
							}

							if( bGenerateNotifies && (!bTriggerNotifyHighestWeightedAnim || (I == HighestWeightIndex)))
							{
								// Harvest and record notifies
								Sample.Animation->GetAnimNotifies(PrevSampleDataTime, DeltaTimePosition, Instance.bLooping, Notifies);
							}

							if (Context.RootMotionMode == ERootMotionMode::RootMotionFromEverything && Sample.Animation->bEnableRootMotion)
							{
								Context.RootMotionMovementParams.AccumulateWithBlend(Sample.Animation->ExtractRootMotion(PrevSampleDataTime, DeltaTimePosition, Instance.bLooping), SampleEntry.GetWeight());
							}


							UE_LOG(LogAnimation, Verbose, TEXT("%d. Blending animation(%s) with %f weight at time %0.2f"), I+1, *Sample.Animation->GetName(), SampleEntry.GetWeight(), CurrentSampleDataTime);
						}
					}
				}

				if (bGenerateNotifies && Notifies.Num() > 0)
				{
					InstanceOwner->AddAnimNotifies(Notifies, Instance.EffectiveBlendWeight);
				}
			}
		}
		OldSampleDataList.Reset();
		NewSampleDataList.Reset();
	}
}
void UBlendSpaceBase::TickAssetPlayer(FAnimTickRecord& Instance, struct FAnimNotifyQueue& NotifyQueue, FAnimAssetTickContext& Context) const
{
	const float DeltaTime = Context.GetDeltaTime();
	float MoveDelta = Instance.PlayRateMultiplier * DeltaTime;

	// this happens even if MoveDelta == 0.f. This still should happen if it is being interpolated
	// since we allow setting position of blendspace, we can't ignore MoveDelta == 0.f
	// also now we don't have to worry about not following if DeltaTime = 0.f
	{
		// first filter input using blend filter
		const FVector BlendSpacePosition(Instance.BlendSpace.BlendSpacePositionX, Instance.BlendSpace.BlendSpacePositionY, 0.f);
		const FVector BlendInput = FilterInput(Instance.BlendSpace.BlendFilter, BlendSpacePosition, DeltaTime);
		EBlendSpaceAxis AxisToScale = GetAxisToScale();
		if (AxisToScale != BSA_None)
		{
			float FilterMultiplier = 1.f;
			// first use multiplier using new blendinput
			// new filtered input is going to be used for sampling animation
			// so we'll need to change playrate if you'd like to not slide foot
			if ( !BlendSpacePosition.Equals(BlendInput) )  
			{
				// apply speed change if you want, 
				if (AxisToScale == BSA_X)
				{
					if (BlendInput.X != 0.f)
					{
						FilterMultiplier = BlendSpacePosition.X / BlendInput.X;
					}
				}
				else if (AxisToScale == BSA_Y)
				{
					if (BlendInput.Y != 0.f)
					{
						FilterMultiplier = BlendSpacePosition.Y / BlendInput.Y;
					}
				}
			}

			// now find if clamped input is different
			// if different, then apply scale to fit in
			FVector ClampedInput = ClampBlendInput(BlendInput);
			if ( !ClampedInput.Equals(BlendInput) ) 
			{
				// apply speed change if you want, 
				if (AxisToScale == BSA_X)
				{
					if (ClampedInput.X != 0.f)
					{
						FilterMultiplier *= BlendInput.X / ClampedInput.X;
					}
				}
				else if (AxisToScale == BSA_Y)
				{
					if (ClampedInput.Y != 0.f)
					{
						FilterMultiplier *= BlendInput.Y / ClampedInput.Y;
					}
				}
			}


			MoveDelta *= FilterMultiplier;
			UE_LOG(LogAnimation, Log, TEXT("BlendSpace(%s) - BlendInput(%s) : FilteredBlendInput(%s), FilterMultiplier(%0.2f)"), *GetName(), *BlendSpacePosition.ToString(), *BlendInput.ToString(), FilterMultiplier );
		}

		check(Instance.BlendSpace.BlendSampleDataCache);

		// For Target weight interpolation, we'll need to save old data, and interpolate to new data
		TArray<FBlendSampleData>& OldSampleDataList = FBlendSpaceScratchData::Get().OldSampleDataList;
		TArray<FBlendSampleData>& NewSampleDataList = FBlendSpaceScratchData::Get().NewSampleDataList;
		check(!OldSampleDataList.Num() && !NewSampleDataList.Num()); // this must be called non-recursively

		OldSampleDataList.Append(*Instance.BlendSpace.BlendSampleDataCache);

		// get sample data based on new input
		// consolidate all samples and sort them, so that we can handle from biggest weight to smallest
		Instance.BlendSpace.BlendSampleDataCache->Reset();
		// new sample data that will be used for evaluation
		TArray<FBlendSampleData> & SampleDataList = *Instance.BlendSpace.BlendSampleDataCache;

		// get sample data from blendspace
		if (GetSamplesFromBlendInput(BlendInput, NewSampleDataList))
		{
			float NewAnimLength=0.f;
			float PreInterpAnimLength = 0.f;

			// if target weight interpolation is set
			if (TargetWeightInterpolationSpeedPerSec > 0.f)
			{
				UE_LOG(LogAnimation, Verbose, TEXT("Target Weight Interpolation: Target Samples "));
				// recalculate AnimLength based on weight of target animations - this is used for scaling animation later (change speed)
				PreInterpAnimLength = GetAnimationLengthFromSampleData(NewSampleDataList);
				UE_LOG(LogAnimation, Verbose, TEXT("BlendSpace(%s) - BlendInput(%s) : PreAnimLength(%0.5f) "), *GetName(), *BlendInput.ToString(), PreInterpAnimLength);

				// target weight interpolation
				if (InterpolateWeightOfSampleData(DeltaTime, OldSampleDataList, NewSampleDataList, SampleDataList))
				{
					// now I need to normalize
					FBlendSampleData::NormalizeDataWeight(SampleDataList);
				}
				else
				{
					// if interpolation failed, just copy new sample data tto sample data
					SampleDataList = NewSampleDataList;
				}

				// recalculate AnimLength based on weight of animations
				UE_LOG(LogAnimation, Verbose, TEXT("Target Weight Interpolation: Interp Samples "));
			}
			else
			{
				// when there is no target weight interpolation, just copy new to target
				SampleDataList.Append(NewSampleDataList);
			}

			bool bCanDoMarkerSync = (SampleIndexWithMarkers != INDEX_NONE) && (Context.IsSingleAnimationContext() || (Instance.bCanUseMarkerSync && Context.CanUseMarkerPosition()));

			if (bCanDoMarkerSync)
			{
				//Copy previous frame marker data to current frame
				for (FBlendSampleData& PrevBlendSampleItem : OldSampleDataList)
				{
					for (FBlendSampleData& CurrentBlendSampleItem : SampleDataList)
					{
						// it only can have one animation in the sample, make sure to copy Time
						if (PrevBlendSampleItem.Animation && PrevBlendSampleItem.Animation == CurrentBlendSampleItem.Animation)
						{
							CurrentBlendSampleItem.Time = PrevBlendSampleItem.Time;
							CurrentBlendSampleItem.PreviousTime = PrevBlendSampleItem.PreviousTime;
							CurrentBlendSampleItem.MarkerTickRecord = PrevBlendSampleItem.MarkerTickRecord;
						}
					}
				}
			}

			NewAnimLength = GetAnimationLengthFromSampleData(SampleDataList);

			if (PreInterpAnimLength > 0.f && NewAnimLength > 0.f)
			{
				MoveDelta *= PreInterpAnimLength / NewAnimLength;
			}

			float& NormalizedCurrentTime = *(Instance.TimeAccumulator);
			const float NormalizedPreviousTime = NormalizedCurrentTime;

			// @note for sync group vs non sync group
			// in blendspace, it will still sync even if only one node in sync group
			// so you're never non-sync group unless you have situation where some markers are relevant to one sync group but not all the time
			// here we save NormalizedCurrentTime as Highest weighted samples' position in sync group
			// if you're not in sync group, NormalizedCurrentTime is based on normalized length by sample weights
			// if you move between sync to non sync within blendspace, you're going to see pop because we'll have to jump
			// for now, our rule is to keep normalized time as highest weighted sample position within its own length
			// also MoveDelta doesn't work if you're in sync group. It will move according to sync group position
			// @todo consider using MoveDelta when  this is leader, but that can be scary because it's not matching with DeltaTime any more. 
			// if you have interpolation delay, that value can be applied, but the output might be unpredictable. 
			// 
			// to fix this better in the future, we should use marker sync position from last tick
			// but that still doesn't fix if you just join sync group, you're going to see pop since your animation doesn't fix

			if (Context.IsLeader())
			{
				// advance current time - blend spaces hold normalized time as when dealing with changing anim length it would be possible to go backwards
				UE_LOG(LogAnimation, Verbose, TEXT("BlendSpace(%s) - BlendInput(%s) : AnimLength(%0.5f) "), *GetName(), *BlendInput.ToString(), NewAnimLength);
				
				const int32 HighestMarkerSyncWeightIndex = bCanDoMarkerSync ? GetHighestWeightMarkerSyncSample(SampleDataList, SampleData) : -1;

				if (HighestMarkerSyncWeightIndex == -1)
				{
					bCanDoMarkerSync = false;
				}

				if (bCanDoMarkerSync)
				{
					FBlendSampleData& SampleDataItem = SampleDataList[HighestMarkerSyncWeightIndex];
					const FBlendSample& Sample = SampleData[SampleDataItem.SampleDataIndex];

					bool bResetMarkerDataOnFollowers = false;
					if (!Instance.MarkerTickRecord->IsValid())
					{
						SampleDataItem.MarkerTickRecord.Reset();
						bResetMarkerDataOnFollowers = true;
					}
					else if (!SampleDataItem.MarkerTickRecord.IsValid() && Context.MarkerTickContext.GetMarkerSyncStartPosition().IsValid())
					{
						Sample.Animation->GetMarkerIndicesForPosition(Context.MarkerTickContext.GetMarkerSyncStartPosition(), true, SampleDataItem.MarkerTickRecord.PreviousMarker, SampleDataItem.MarkerTickRecord.NextMarker, SampleDataItem.Time);
					}

					const float NewDeltaTime = Context.GetDeltaTime() * Instance.PlayRateMultiplier;
					if (!FMath::IsNearlyZero(NewDeltaTime))
					{
						Context.SetLeaderDelta(NewDeltaTime);
						Sample.Animation->TickByMarkerAsLeader(SampleDataItem.MarkerTickRecord, Context.MarkerTickContext, SampleDataItem.Time, SampleDataItem.PreviousTime, NewDeltaTime, true);
						check(Context.MarkerTickContext.IsMarkerSyncStartValid());
						TickFollowerSamples(SampleDataList, HighestMarkerSyncWeightIndex, Context, bResetMarkerDataOnFollowers);
					}
					NormalizedCurrentTime = SampleDataItem.Time / Sample.Animation->SequenceLength;
					*Instance.MarkerTickRecord = SampleDataItem.MarkerTickRecord;
				}
				else
				{
					// Advance time using current/new anim length
					float CurrentTime = NormalizedCurrentTime * NewAnimLength;
					FAnimationRuntime::AdvanceTime(Instance.bLooping, MoveDelta, /*inout*/ CurrentTime, NewAnimLength);
					NormalizedCurrentTime = NewAnimLength ? (CurrentTime / NewAnimLength) : 0.0f;
					UE_LOG(LogAnimMarkerSync, Log, TEXT("Leader (%s) (normal advance)  - PreviousTime (%0.2f), CurrentTime (%0.2f), MoveDelta (%0.2f) "), *GetName(), NormalizedPreviousTime, NormalizedCurrentTime, MoveDelta);
				}

				Context.SetAnimationPositionRatio(NormalizedCurrentTime);
			}
			else
			{
				if(!Context.MarkerTickContext.IsMarkerSyncStartValid())
				{
					bCanDoMarkerSync = false;
				}

				if (bCanDoMarkerSync)
				{
					const int32 HighestWeightIndex = GetHighestWeightSample(SampleDataList);
					FBlendSampleData& SampleDataItem = SampleDataList[HighestWeightIndex];
					const FBlendSample& Sample = SampleData[SampleDataItem.SampleDataIndex];

					if (Context.GetDeltaTime() != 0.f)
					{
						if(!Instance.MarkerTickRecord->IsValid())
						{
							SampleDataItem.Time = NormalizedCurrentTime * Sample.Animation->SequenceLength;
						}

						TickFollowerSamples(SampleDataList, -1, Context, false);
					}
					*Instance.MarkerTickRecord = SampleDataItem.MarkerTickRecord;
					NormalizedCurrentTime =  SampleDataItem.Time / Sample.Animation->SequenceLength;
				}
				else
				{
					NormalizedCurrentTime =  Context.GetAnimationPositionRatio();
					UE_LOG(LogAnimMarkerSync, Log, TEXT("Leader (%s) (normal advance)  - PreviousTime (%0.2f), CurrentTime (%0.2f), MoveDelta (%0.2f) "), *GetName(), NormalizedPreviousTime, NormalizedCurrentTime, MoveDelta);
				}
			}

			// generate notifies and sets time
			{
				TArray<const FAnimNotifyEvent*> Notifies;

				const float ClampedNormalizedPreviousTime = FMath::Clamp<float>(NormalizedPreviousTime, 0.f, 1.f);
				const float ClampedNormalizedCurrentTime = FMath::Clamp<float>(NormalizedCurrentTime, 0.f, 1.f);
				const bool bGenerateNotifies = Context.ShouldGenerateNotifies() && (NormalizedCurrentTime != NormalizedPreviousTime) && NotifyTriggerMode != ENotifyTriggerMode::None;
				
				// Get the index of the highest weight, assuming that the first is the highest until we find otherwise
				const bool bTriggerNotifyHighestWeightedAnim = NotifyTriggerMode == ENotifyTriggerMode::HighestWeightedAnimation && SampleDataList.Num() > 0;
				const int32 HighestWeightIndex = (bGenerateNotifies && bTriggerNotifyHighestWeightedAnim) ? GetHighestWeightSample(SampleDataList) : -1;

				for (int32 I = 0; I < SampleDataList.Num(); ++I)
				{
					FBlendSampleData& SampleEntry = SampleDataList[I];
					const int32 SampleDataIndex = SampleEntry.SampleDataIndex;

					// Skip SamplesPoints that has no relevant weight
					if( SampleData.IsValidIndex(SampleDataIndex) && (SampleEntry.TotalWeight > ZERO_ANIMWEIGHT_THRESH) )
					{
						const FBlendSample& Sample = SampleData[SampleDataIndex];
						if( Sample.Animation )
						{
							float PrevSampleDataTime;
							float& CurrentSampleDataTime = SampleEntry.Time;

							if (!bCanDoMarkerSync || Sample.Animation->AuthoredSyncMarkers.Num() == 0) //Have already updated time if we are doing marker sync
							{
								const float SampleNormalizedPreviousTime = Sample.Animation->RateScale >= 0.f ? ClampedNormalizedPreviousTime : 1.f - ClampedNormalizedPreviousTime;
								const float SampleNormalizedCurrentTime = Sample.Animation->RateScale >= 0.f ? ClampedNormalizedCurrentTime : 1.f - ClampedNormalizedCurrentTime;
								PrevSampleDataTime = SampleNormalizedPreviousTime * Sample.Animation->SequenceLength;
								CurrentSampleDataTime = SampleNormalizedCurrentTime * Sample.Animation->SequenceLength;
							}
							else
							{
								PrevSampleDataTime = SampleEntry.PreviousTime;
							}

							// Figure out delta time 
							float DeltaTimePosition = CurrentSampleDataTime - PrevSampleDataTime;
							const float SampleMoveDelta = MoveDelta * Sample.Animation->RateScale;

							// if we went against play rate, then loop around.
							if ((SampleMoveDelta * DeltaTimePosition) < 0.f)
							{
								DeltaTimePosition += FMath::Sign<float>(SampleMoveDelta) * Sample.Animation->SequenceLength;
							}

							if( bGenerateNotifies && (!bTriggerNotifyHighestWeightedAnim || (I == HighestWeightIndex)))
							{
								// Harvest and record notifies
								Sample.Animation->GetAnimNotifies(PrevSampleDataTime, DeltaTimePosition, Instance.bLooping, Notifies);
							}

							if (Context.RootMotionMode == ERootMotionMode::RootMotionFromEverything && Sample.Animation->bEnableRootMotion)
							{
								Context.RootMotionMovementParams.AccumulateWithBlend(Sample.Animation->ExtractRootMotion(PrevSampleDataTime, DeltaTimePosition, Instance.bLooping), SampleEntry.GetWeight());
							}


							UE_LOG(LogAnimation, Verbose, TEXT("%d. Blending animation(%s) with %f weight at time %0.2f"), I+1, *Sample.Animation->GetName(), SampleEntry.GetWeight(), CurrentSampleDataTime);
						}
					}
				}

				if (bGenerateNotifies && Notifies.Num() > 0)
				{
					NotifyQueue.AddAnimNotifies(Notifies, Instance.EffectiveBlendWeight);
				}
			}
		}
		OldSampleDataList.Reset();
		NewSampleDataList.Reset();
	}
}
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, LogNavigation, Log, TEXT("MoveToLocation: Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)"),
		*Dest.ToString(), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe);

	// Check input is valid
	if( Dest.ContainsNaN() )
	{
		UE_VLOG(this, LogNavigation, Error, TEXT("AAIController::MoveToLocation: Destination is not valid! Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)"),
			*Dest.ToString(), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe);
		
		ensure( !Dest.ContainsNaN() );
		bCanRequestMove = false;
	}

	FVector GoalLocation = Dest;

	// fail if projection to navigation is required but it either failed or there's not navigation component
	if (bCanRequestMove && bProjectDestinationToNavigation && (NavComponent == NULL || !NavComponent->ProjectPointToNavigation(Dest, GoalLocation)))
	{
		UE_VLOG_LOCATION(this, Dest, 30.f, FLinearColor::Red, TEXT("AAIController::MoveToLocation failed to project destination location to navmesh"));
		bCanRequestMove = false;
	}

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

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

	if (bCanRequestMove)
	{
		FNavPathSharedPtr Path = FindPath(GoalLocation, bUsePathfinding, UNavigationQueryFilter::GetQueryFilter(FilterClass));
	if (Path.IsValid())
	{
		bAllowStrafe = bCanStrafe;

		const uint32 RequestID = RequestMove(Path, NULL, AcceptanceRadius, bStopOnOverlap);
			Result = (RequestID != INVALID_MOVEREQUESTID) ? EPathFollowingRequestResult::RequestSuccessful : EPathFollowingRequestResult::Failed;
		}
	}

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

		OnMoveCompleted(INVALID_MOVEREQUESTID, EPathFollowingResult::Invalid);
	}

	return Result;
}
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;
}
Beispiel #24
0
inline FString convert(FVector x)
{
  return x.ToString();
}
/* 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 UTankAimingComponent::AimAt(FVector HitLocation) {
	auto TankName = GetOwner()->GetName();
	auto BarrelLocation = Barrel->GetComponentLocation().ToString();
	UE_LOG(LogTemp, Warning, TEXT("%s is aiming at %s from %s"), *TankName, *HitLocation.ToString(), *BarrelLocation);
}
// Called every frame
void APawnWithCamera::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

	Count += 1;

	FVector testLoc = GetActorLocation();
	UE_LOG(LogClass, Warning, TEXT("Test LOC %s"), *testLoc.ToString());	

	if (bZoomingin)
	{
		ZoomFactor += DeltaTime / 0.5f;
	}
	else
	{
		ZoomFactor -= DeltaTime / 0.25f;
	}

	ZoomFactor = FMath::Clamp<float>(ZoomFactor, 0.0f, 1.0f);
	OurCamera->FieldOfView = FMath::Lerp<float>(90.0f, 60.0f, ZoomFactor);
	OurCameraSpringArm->TargetArmLength = FMath::Lerp<float>(400.0f, 300.0f, ZoomFactor);

	{
		FRotator NewRotation = GetActorRotation();
		NewRotation.Yaw += CameraInput.X;
		SetActorRotation(NewRotation);
	}

	{
		FRotator NewRotation = OurCameraSpringArm->GetComponentRotation();
		NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + CameraInput.Y, -80.0f, -15.0f);
		OurCameraSpringArm->SetWorldRotation(NewRotation);
	}

	{
		if (!MovementInput.IsZero())
		{
			//Scale our movement input axis values by 100 units per second
			MovementInput = MovementInput.SafeNormal() * 100.0f;
			FVector NewLocation = GetActorLocation();
			
			//Print Actor Location.
			//UE_LOG(LogClass, Error	, TEXT("This is a testing statement. %s"), *NewLocation.ToString());
			//UE_LOG(LogClass, Warning, TEXT("Delta Time %f"), DeltaTime);

			NewLocation += GetActorForwardVector() * MovementInput.X * DeltaTime;
			NewLocation += GetActorRightVector() * MovementInput.Y * DeltaTime;
			this->SetActorLocation(NewLocation);
		}
	}

	//FHitResult HitCall(ForceInit);
	//FCollisionQueryParams ParamCall = FCollisionQueryParams(FName(TEXT("Trace")), true, this);
	//DoTrace(&HitCall, &ParamCall);

	//DoTrace();

	//Get keyboard input
	if (GetWorld()->GetFirstPlayerController()->WasInputKeyJustPressed(EKeys::X))
	{
		bShowLog = true;
	}
	else if (GetWorld()->GetFirstPlayerController()->WasInputKeyJustReleased(EKeys::X))
	{
		bShowLog = false;
	}
}
AActor* UWorld::SpawnActor( UClass* Class, FTransform const* Transform, const FActorSpawnParameters& SpawnParameters )
{
	SCOPE_CYCLE_COUNTER(STAT_SpawnActorTime);
	check( CurrentLevel ); 	
	check(GIsEditor || (CurrentLevel == PersistentLevel));

	// Make sure this class is spawnable.
	if( !Class )
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because no class was specified") );
		return NULL;
	}
	if( Class->HasAnyClassFlags(CLASS_Deprecated) )
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because class %s is deprecated"), *Class->GetName() );
		return NULL;
	}
	if( Class->HasAnyClassFlags(CLASS_Abstract) )
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because class %s is abstract"), *Class->GetName() );
		return NULL;
	}
	else if( !Class->IsChildOf(AActor::StaticClass()) )
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because %s is not an actor class"), *Class->GetName() );
		return NULL;
	}
	else if (SpawnParameters.Template != NULL && SpawnParameters.Template->GetClass() != Class)
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because template class (%s) does not match spawn class (%s)"), *SpawnParameters.Template->GetClass()->GetName(), *Class->GetName());
		if (!SpawnParameters.bNoFail)
		{
			return NULL;
		}
	}
	else if (bIsRunningConstructionScript && !SpawnParameters.bAllowDuringConstructionScript)
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because we are running a ConstructionScript (%s)"), *Class->GetName() );
		return NULL;
	}
	else if (bIsTearingDown)
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because we are in the process of tearing down the world"));
		return NULL;
	}
	else if (Transform && Transform->ContainsNaN())
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because the given transform (%s) is invalid"), *Transform->ToString());
		return NULL;
	}

	ULevel* LevelToSpawnIn = SpawnParameters.OverrideLevel;
	if (LevelToSpawnIn == NULL)
	{
		// Spawn in the same level as the owner if we have one. @warning: this relies on the outer of an actor being the level.
		LevelToSpawnIn = (SpawnParameters.Owner != NULL) ? CastChecked<ULevel>(SpawnParameters.Owner->GetOuter()) : CurrentLevel;
	}

	FName NewActorName = SpawnParameters.Name;
	AActor* Template = SpawnParameters.Template;
	// Use class's default actor as a template.
	if( !Template )
	{
		Template = Class->GetDefaultObject<AActor>();
	}
	else if (NewActorName.IsNone() && !Template->HasAnyFlags(RF_ClassDefaultObject))
	{
		NewActorName = MakeUniqueObjectName(LevelToSpawnIn, Template->GetClass(), *Template->GetFName().GetPlainNameString());
	}
	check(Template!=NULL);

	// See if we can spawn on ded.server/client only etc (check NeedsLoadForClient & NeedsLoadForServer)
	if(!CanCreateInCurrentContext(Template))
	{
		UE_LOG(LogSpawn, Warning, TEXT("Unable to spawn class '%s' due to client/server context."), *Class->GetName() );
		return NULL;
	}

	FVector NewLocation = Transform ? Transform->GetLocation() : (Template->GetRootComponent() ? Template->GetRootComponent()->RelativeLocation : FVector::ZeroVector);
	FRotator NewRotation = Transform ? Transform->GetRotation().Rotator() :	(Template->GetRootComponent() ? Template->GetRootComponent()->RelativeRotation : FRotator::ZeroRotator);
	FVector NewScale = Transform ? Transform->GetScale3D() :	(Template->GetRootComponent() ? Template->GetRootComponent()->RelativeScale3D : FVector(1.f) );

	PRAGMA_DISABLE_DEPRECATION_WARNINGS;
	// handle existing (but deprecated) uses of bNoCollisionFail where user set it to true
	ESpawnActorCollisionHandlingMethod CollisionHandlingOverride = SpawnParameters.SpawnCollisionHandlingOverride;
	if ((CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::Undefined) && SpawnParameters.bNoCollisionFail)
	{
		CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
	}
	PRAGMA_ENABLE_DEPRECATION_WARNINGS;

	// "no fail" take preedence over collision handling settings that include fails
	if (SpawnParameters.bNoFail)
	{
		// maybe upgrade to disallow fail
		if (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding)
		{
			CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
		}
		else if (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding)
		{
			CollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
		}
	}

	// use override if set, else fall back to actor's preference
	ESpawnActorCollisionHandlingMethod const CollisionHandlingMethod = (CollisionHandlingOverride == ESpawnActorCollisionHandlingMethod::Undefined) ? Template->SpawnCollisionHandlingMethod : CollisionHandlingOverride;

	// see if we can avoid spawning altogether by checking native components
	// note: we can't handle all cases here, since we don't know the full component hierarchy until after the actor is spawned
	if (CollisionHandlingMethod == ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding)
	{
		if (EncroachingBlockingGeometry(Template, NewLocation, NewRotation))
		{
			// a native component is colliding, that's enough to reject spawning
			UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because of collision at the spawn location [%s] for [%s]"), *NewLocation.ToString(), *Class->GetName());
			return nullptr;
		}
	}

	// actually make the actor object
	AActor* const Actor = NewObject<AActor>(LevelToSpawnIn, Class, NewActorName, SpawnParameters.ObjectFlags, Template);
	check(Actor);

#if WITH_EDITOR
	Actor->ClearActorLabel(); // Clear label on newly spawned actors
#endif // WITH_EDITOR

	if ( GUndo )
	{
		ModifyLevel( LevelToSpawnIn );
	}
	LevelToSpawnIn->Actors.Add( Actor );

	// Add this newly spawned actor to the network actor list
	AddNetworkActor( Actor );

#if PERF_SHOW_MULTI_PAWN_SPAWN_FRAMES
	if( Cast<APawn>(Actor) )
	{
		FString PawnName = FString::Printf(TEXT("%d: %s"), ThisFramePawnSpawns.Num(), *Actor->GetPathName());
		ThisFramePawnSpawns.Add(PawnName);
	}
#endif

	// tell the actor what method to use, in case it was overridden
	Actor->SpawnCollisionHandlingMethod = CollisionHandlingMethod;

	Actor->PostSpawnInitialize(FTransform(NewRotation, NewLocation, NewScale), SpawnParameters.Owner, SpawnParameters.Instigator, SpawnParameters.bRemoteOwned, SpawnParameters.bNoFail, SpawnParameters.bDeferConstruction);

	if (Actor->IsPendingKill() && !SpawnParameters.bNoFail)
	{
		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because the spawned actor IsPendingKill"));
		return NULL;
	}
// 
// 	// actor should have all of its components now, do any collision checking and handling that we need to do
// 	switch (CollisionHandlingMethod)
// 	{
// 	case ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn:
// 	 	// Try to find a spawn position
// 		{
// 			FVector AdjustedLocation = NewLocation;
// 			FRotator AdjustedRotation = NewRotation;
// 			if (FindTeleportSpot(Actor, AdjustedLocation, AdjustedRotation))
// 			{
// 				Actor->SetActorLocationAndRotation(AdjustedLocation, AdjustedRotation);
// 			}
// 		}
// 	 	break;
// 	case ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding:
// 	 	// Try to find a spawn position
// 		{
// 			FVector AdjustedLocation = NewLocation;
// 			FRotator AdjustedRotation = NewRotation;
// 			if (FindTeleportSpot(Actor, AdjustedLocation, AdjustedRotation))
// 			{
// 				Actor->SetActorLocationAndRotation(AdjustedLocation, AdjustedRotation);
// 			}
// 			else
// 			{
// 				UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because of collision at the spawn location [%s] for [%s]"), *NewLocation.ToString(), *Class->GetName());
// 				DestroyActor(Actor);
// 				return nullptr;
// 			}
// 		}
// 	 	break;
// 	case ESpawnActorCollisionHandlingMethod::DontSpawnIfColliding:
// 		// #todo: don't recheck components checked above?
// 		if (EncroachingBlockingGeometry(Actor, NewLocation, NewRotation))
// 	 	{
// 	 		UE_LOG(LogSpawn, Warning, TEXT("SpawnActor failed because of collision at the spawn location [%s] for [%s]"), *NewLocation.ToString(), *Class->GetName());
// 			DestroyActor(Actor);
// 			return nullptr;
// 	 	}
// 	 	break;
// 	// note we use "always spawn" as default, so treat undefined as that
// 	case ESpawnActorCollisionHandlingMethod::Undefined:
// 	case ESpawnActorCollisionHandlingMethod::AlwaysSpawn:
// 	default:
// 	 	// nothing to do, just proceed as normal
// 	 	break;
// 	}

	Actor->CheckDefaultSubobjects();

	// Broadcast notification of spawn
	OnActorSpawned.Broadcast(Actor);

#if WITH_EDITOR
	if (GIsEditor)
	{
		GEngine->BroadcastLevelActorAdded(Actor);
	}
#endif

	return Actor;
}