void SAnimSegmentsPanel::Construct(const FArguments& InArgs)
{
	bDragging = false;
	const int32 NumTracks = 2;

	AnimTrack = InArgs._AnimTrack;
	ViewInputMin = InArgs._ViewInputMin;
	ViewInputMax = InArgs._ViewInputMax;

	OnAnimSegmentNodeClickedDelegate = InArgs._OnAnimSegmentNodeClicked;
	OnPreAnimUpdateDelegate			 = InArgs._OnPreAnimUpdate;
	OnPostAnimUpdateDelegate		 = InArgs._OnPostAnimUpdate;

	// Animation Segment tracks
	TArray<TSharedPtr<STrack>> AnimSTracks;
	TArray<TSharedPtr<STrackNode>> AnimNodes;

	FLinearColor SelectedColor = FLinearColor(1.0f,0.65,0.0f);

	TSharedPtr<SVerticalBox> AnimSegmentTracks;

	ChildSlot
	[
		SAssignNew(AnimSegmentTracks, SVerticalBox)
	];

	for (int32 TrackIdx=0; TrackIdx < NumTracks; TrackIdx++)
	{
		TSharedPtr<STrack> AnimSegmentTrack;

		AnimSegmentTracks->AddSlot()
			.AutoHeight()
			.VAlign(VAlign_Center)
			.Padding( FMargin(0.5f, 0.5f) )
			[
				SAssignNew(AnimSegmentTrack, STrack)							
				.TrackColor( InArgs._ColorTracker->GetNextColor() )
				.ViewInputMin(ViewInputMin)
				.ViewInputMax(ViewInputMax)
				.TrackMaxValue( InArgs._TrackMaxValue)
				//section stuff
				.OnBarDrag(InArgs._OnBarDrag)
				.OnBarDrop(InArgs._OnBarDrop)
				.OnBarClicked(InArgs._OnBarClicked)
				.DraggableBars(InArgs._DraggableBars)
				.DraggableBarSnapPositions(InArgs._DraggableBarSnapPositions)
				.TrackNumDiscreteValues(InArgs._TrackNumDiscreteValues)
				.OnTrackRightClickContextMenu( InArgs._OnTrackRightClickContextMenu )
				.ScrubPosition( InArgs._ScrubPosition )
				.OnTrackDragDrop( this, &SAnimSegmentsPanel::OnTrackDragDrop )
			];

		AnimSTracks.Add(AnimSegmentTrack);
	}

	// Generate Nodes and map them to tracks
	for ( int32 SegmentIdx=0; SegmentIdx < AnimTrack->AnimSegments.Num(); SegmentIdx++ )
	{
		AnimSTracks[ SegmentIdx % AnimSTracks.Num() ]->AddTrackNode(
			SNew(STrackNode)
			.ViewInputMax(this->ViewInputMax)
			.ViewInputMin(this->ViewInputMin)
			.NodeColor(InArgs._NodeColor)
			.SelectedNodeColor(SelectedColor)
			.DataLength(this, &SAnimSegmentsPanel::GetSegmentLength, SegmentIdx)
			.DataStartPos(this, &SAnimSegmentsPanel::GetSegmentStartPos, SegmentIdx)
			.NodeName(this, &SAnimSegmentsPanel::GetAnimSegmentName, SegmentIdx)
			.ToolTipText(this, &SAnimSegmentsPanel::GetAnimSegmentDetailedInfo, SegmentIdx)
			.OnTrackNodeDragged( this, &SAnimSegmentsPanel::SetSegmentStartPos, SegmentIdx )
			.OnTrackNodeDropped( this, &SAnimSegmentsPanel::OnSegmentDropped, SegmentIdx)
			.OnNodeRightClickContextMenu( this, &SAnimSegmentsPanel::SummonSegmentNodeContextMenu, SegmentIdx)
			.OnTrackNodeClicked( this, &SAnimSegmentsPanel::OnAnimSegmentNodeClicked, SegmentIdx )
			.NodeSelectionSet(InArgs._NodeSelectionSet)
			);
	}
}
Example #2
0
void FAnimNode_TwoBoneIK::EvaluateBoneTransforms(USkeletalMeshComponent* SkelComp, const FBoneContainer& RequiredBones, FA2CSPose& MeshBases, TArray<FBoneTransform>& OutBoneTransforms)
{
	check(OutBoneTransforms.Num() == 0);

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

	const int32 EndBoneIndex = IKBone.BoneIndex;
	const int32 LowerLimbIndex = RequiredBones.GetParentBoneIndex(EndBoneIndex);
	if (LowerLimbIndex == INDEX_NONE)
	{
		bInvalidLimb = true;
	}

	const int32 UpperLimbIndex = RequiredBones.GetParentBoneIndex(LowerLimbIndex);
	if (UpperLimbIndex == INDEX_NONE)
	{
		bInvalidLimb = true;
	}

	const bool bInBoneSpace = (EffectorLocationSpace == BCS_ParentBoneSpace) || (EffectorLocationSpace == BCS_BoneSpace);
	const int32 EffectorSpaceBoneIndex = bInBoneSpace ? RequiredBones.GetPoseBoneIndexForBoneName(EffectorSpaceBoneName) : INDEX_NONE;

	if (bInBoneSpace && ((EffectorSpaceBoneIndex == INDEX_NONE) || !RequiredBones.Contains(EffectorSpaceBoneIndex)))
	{
		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(IKBone.BoneIndex);

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

	// 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);
	const int32 JointTargetSpaceBoneIndex = (JointTargetLocationSpace == BCS_ParentBoneSpace || JointTargetLocationSpace == BCS_BoneSpace) ? RequiredBones.GetPoseBoneIndexForBoneName(JointTargetSpaceBoneName) : INDEX_NONE;
	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::FindBetween(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::FindBetween(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(IKBone.BoneIndex, EndBoneCSTransform) );
	}

	// Make sure we have correct number of bones
	check(OutBoneTransforms.Num() == 3);
}
Example #3
0
void FPhysxSharedData::DumpSharedMemoryUsage(FOutputDevice* Ar)
{
	struct FSharedResourceEntry
	{
		uint64 MemorySize;
		uint64 Count;
	};

	struct FSortBySize
	{
		FORCEINLINE bool operator()( const FSharedResourceEntry& A, const FSharedResourceEntry& B ) const 
		{ 
			// Sort descending
			return B.MemorySize < A.MemorySize;
		}
	};

	TMap<FString, FSharedResourceEntry> AllocationsByType;

	uint64 OverallSize = 0;
	int32 OverallCount = 0;

	TMap<FString, TArray<PxBase*> > ObjectsByType;

	for (int32 i=0; i < (int32)SharedObjects->getNbObjects(); ++i)
	{
		PxBase& Obj = SharedObjects->getObject(i);
		FString TypeName = ANSI_TO_TCHAR(Obj.getConcreteTypeName());

		TArray<PxBase*>* ObjectsArray = ObjectsByType.Find(TypeName);
		if (ObjectsArray == NULL)
		{
			ObjectsByType.Add(TypeName, TArray<PxBase*>());
			ObjectsArray = ObjectsByType.Find(TypeName);
		}

		check(ObjectsArray);
		ObjectsArray->Add(&Obj);
	}

	TArray<FString> TypeNames;
	ObjectsByType.GetKeys(TypeNames);

	for (int32 TypeIdx=0; TypeIdx < TypeNames.Num(); ++TypeIdx)
	{
		const FString& TypeName = TypeNames[TypeIdx];
		
		TArray<PxBase*>* ObjectsArray = ObjectsByType.Find(TypeName);
		check(ObjectsArray);

		PxSerializationRegistry* Sr = PxSerialization::createSerializationRegistry(*GPhysXSDK);
		PxCollection* Collection = PxCreateCollection();
		
		for (int32 i=0; i < ObjectsArray->Num(); ++i)
		{
			Collection->add(*((*ObjectsArray)[i]));;
		}

		PxSerialization::complete(*Collection, *Sr);	// chase all other stuff (shared shaps, materials, etc) needed to serialize this collection

		FPhysXCountMemoryStream Out;
		PxSerialization::serializeCollectionToBinary(Out, *Collection, *Sr);

		Collection->release();
		Sr->release();

		OverallSize += Out.UsedMemory;
		OverallCount += ObjectsArray->Num();

		FSharedResourceEntry NewEntry;
		NewEntry.Count = ObjectsArray->Num();
		NewEntry.MemorySize = Out.UsedMemory;

		AllocationsByType.Add(TypeName, NewEntry);
	}

	Ar->Logf(TEXT(""));
	Ar->Logf(TEXT("Shared Resources:"));
	Ar->Logf(TEXT(""));

	AllocationsByType.ValueSort(FSortBySize());
	
	Ar->Logf(TEXT("%-10d %s (%d)"), OverallSize, TEXT("Overall"), OverallCount );
	
	for( auto It=AllocationsByType.CreateConstIterator(); It; ++It )
	{
		Ar->Logf(TEXT("%-10d %s (%d)"), It.Value().MemorySize, *It.Key(), It.Value().Count );
	}
}
void FMovieSceneSkeletalAnimationTrackInstance::Update( EMovieSceneUpdateData& UpdateData, const TArray<TWeakObjectPtr<UObject>>& RuntimeObjects, class IMovieScenePlayer& Player, FMovieSceneSequenceInstance& SequenceInstance )
{
	if (UpdateData.UpdatePass == MSUP_PreUpdate)
	{
		UpdateRefreshBones(RuntimeObjects);
		return;
	}

	// @todo Sequencer gameplay update has a different code path than editor update for animation
	for ( TWeakObjectPtr<UObject> RuntimeObjectPtr : RuntimeObjects )
	{
		USkeletalMeshComponent* SkeletalMeshComponent = GetSkeletalMeshComponentFromRuntimeObjectPtr( RuntimeObjectPtr );
		if ( SkeletalMeshComponent )
		{
			TArray<UMovieSceneSection*> AnimSections = AnimationTrack->GetAnimSectionsAtTime( UpdateData.Position );

			// cbb: If there is no overlapping section, evaluate the closest section only if the current time is before it.
			if (AnimSections.Num() == 0)
			{
				UMovieSceneSection* NearestSection = MovieSceneHelpers::FindNearestSectionAtTime( AnimationTrack->GetAllSections(), UpdateData.Position );
				if (NearestSection)
				{
					AnimSections.Add(NearestSection);
				}
			}

			for (int32 AnimSectionIndex = 0; AnimSectionIndex < AnimSections.Num(); ++AnimSectionIndex)
			{
				UMovieSceneSkeletalAnimationSection* AnimSection = Cast<UMovieSceneSkeletalAnimationSection>( AnimSections[AnimSectionIndex] );

				if ( AnimSection && AnimSection->IsActive() )
				{
					UAnimSequenceBase* AnimSequence = AnimSection->GetAnimSequence();

					if ( AnimSequence )
					{
						float EvalTime = MapTimeToAnimation( UpdateData.Position, AnimSection );

						int32 ChannelIndex = 0;

						const bool bLooping = false;

						if ( ShouldUsePreviewPlayback( Player, SkeletalMeshComponent ) )
						{
							// If the playback status is jumping, ie. one such occurrence is setting the time for thumbnail generation, disable anim notifies updates because it could fire audio
							const bool bFireNotifies = Player.GetPlaybackStatus() == EMovieScenePlayerStatus::Jumping || Player.GetPlaybackStatus() == EMovieScenePlayerStatus::Stopped ? false : true;

							float DeltaTime = UpdateData.Position > UpdateData.LastPosition ? UpdateData.Position - UpdateData.LastPosition : 0.f;

							// When jumping from one cut to another cut, the delta time should be 0 so that anim notifies before the current position are not evaluated. Note, anim notifies at the current time should still be evaluated.
							if ( UpdateData.bJumpCut )
							{
								DeltaTime = 0.f;
							}

							const bool bResetDynamics = Player.GetPlaybackStatus() == EMovieScenePlayerStatus::Stepping ||
								Player.GetPlaybackStatus() == EMovieScenePlayerStatus::Jumping ||
								Player.GetPlaybackStatus() == EMovieScenePlayerStatus::Scrubbing ||
								( DeltaTime == 0.0f && Player.GetPlaybackStatus() != EMovieScenePlayerStatus::Stopped );

							PreviewSetAnimPosition( SkeletalMeshComponent, AnimSection->GetSlotName(), ChannelIndex, AnimSequence, EvalTime, bLooping, bFireNotifies, DeltaTime, Player.GetPlaybackStatus() == EMovieScenePlayerStatus::Playing, bResetDynamics );
						}
						else
						{
							// Don't fire notifies at runtime since they will be fired through the standard animation tick path.
							const bool bFireNotifies = false;
							SetAnimPosition( SkeletalMeshComponent, AnimSection->GetSlotName(), ChannelIndex, AnimSequence, EvalTime, bLooping, bFireNotifies );
						}
					}
				}
			}
		}
	}
}
Example #5
0
void FogWorker::Update(float time)
{
    // Debug stuff
    static const FName TraceTag(TEXT("FOWTrace"));

    const UWorld* world = Manager.GetWorld();
    if(!world)
    {
        return;
    }

    UpdateRenderOrigin();

    FVector origin, SurfaceExtent;
    Manager.GetActorBounds(false, origin, SurfaceExtent);
    if(FMath::IsNearlyZero(SurfaceExtent.Size2D()))
    {
        return;
    }

    ForgetOldLocations(time);

    // Cache observers location
    TArray<FVector> observers;
    observers.Reserve(Manager.Observers.Num());
    for(const auto& o : Manager.Observers)
    {
        if(o->IsValidLowLevel())
        {
            observers.Add(o->GetActorLocation());
        }
    }

    // iterate through observers to unveil fog
    for(auto& observerLocation : observers)
    {
        FVector2D observerTexLoc(observerLocation - Manager.CameraPosition);
        TArray<FVector2D> sightShape;

        observerTexLoc /= FVector2D(SurfaceExtent);
        observerTexLoc *= TextureSize / 2.0f;
        observerTexLoc += FVector2D(TextureSize / 2.0f, TextureSize / 2.0f);

        FCollisionQueryParams queryParams(TraceTag, true);

        for(float i = 0; i < 2 * PI; i += HALF_PI / 100.0f)
        {
            auto x = Manager.SightRange * FMath::Cos(i);
            auto y = Manager.SightRange * FMath::Sin(i);

            FVector sightLoc = observerLocation + FVector(x, y, 0);

            FHitResult hit;
            if(world->LineTraceSingleByChannel(hit, observerLocation, sightLoc, ECC_GameTraceChannel2, queryParams))
            {
                sightLoc = hit.Location;
            }

            FVector2D hitTexLoc;
            if(FSceneView::ProjectWorldToScreen(sightLoc, FIntRect(0, 0, 1024, 768), FMatrix::Identity, hitTexLoc))
            {
                // hitTexLoc = FVector2D(sightLoc - Manager.CameraPosition);
                hitTexLoc /= FVector2D(SurfaceExtent);
                hitTexLoc *= TextureSize / 2.0f;
                hitTexLoc += FVector2D(TextureSize / 2.0f, TextureSize / 2.0f);

                sightShape.AddUnique(hitTexLoc);
            }
        }
        // draw a unveil shape
        DrawUnveilShape(observerTexLoc, sightShape);
        // flood fill area
        // FloodFill(observerTexLoc.X, observerTexLoc.Y);
    }

    UpdateTextureData();
}
void MaterialExpressionClasses::InitMaterialExpressionClasses()
{
	if( !bInitialized )
	{
		UMaterialEditorOptions* TempEditorOptions = ConstructObject<UMaterialEditorOptions>( UMaterialEditorOptions::StaticClass() );
		UClass* BaseType = UMaterialExpression::StaticClass();
		if( BaseType )
		{
			TArray<UStructProperty*>	ExpressionInputs;
			const UStruct*				ExpressionInputStruct = GetExpressionInputStruct();

			for( TObjectIterator<UClass> It ; It ; ++It )
			{
				UClass* Class = *It;
				if( !Class->HasAnyClassFlags(CLASS_Abstract | CLASS_Deprecated) )
				{
					if( Class->IsChildOf(UMaterialExpression::StaticClass()) )
					{
						ExpressionInputs.Empty();

						// Exclude comments from the expression list, as well as the base parameter expression, as it should not be used directly
						if ( Class != UMaterialExpressionComment::StaticClass() 
							&& Class != UMaterialExpressionParameter::StaticClass() )
						{
							FMaterialExpression MaterialExpression;
							// Trim the material expression name and add it to the list used for filtering.
							MaterialExpression.Name = FString(*Class->GetName()).Mid(FCString::Strlen(TEXT("MaterialExpression")));
							MaterialExpression.MaterialClass = Class;

							AllExpressionClasses.Add(MaterialExpression);

							// Initialize the expression class input map.							
							for( TFieldIterator<UStructProperty> InputIt(Class) ; InputIt ; ++InputIt )
							{
								UStructProperty* StructProp = *InputIt;
								if( StructProp->Struct == ExpressionInputStruct )
								{
									ExpressionInputs.Add( StructProp );
								}
							}

							// See if it is in the favorites array...
							for (int32 FavoriteIndex = 0; FavoriteIndex < TempEditorOptions->FavoriteExpressions.Num(); FavoriteIndex++)
							{
								if (Class->GetName() == TempEditorOptions->FavoriteExpressions[FavoriteIndex])
								{
									FavoriteExpressionClasses.AddUnique(MaterialExpression);
								}
							}

							// Category fill...
							UMaterialExpression* TempObject = Cast<UMaterialExpression>(Class->GetDefaultObject());
							if (TempObject)
							{
								if (TempObject->MenuCategories.Num() == 0)
								{
									UnassignedExpressionClasses.Add(MaterialExpression);
								}
								else
								{
									for (int32 CategoryIndex = 0; CategoryIndex < TempObject->MenuCategories.Num(); CategoryIndex++)
									{
										FCategorizedMaterialExpressionNode* CategoryNode = GetCategoryNode(TempObject->MenuCategories[CategoryIndex], true);
										check(CategoryNode);

										CategoryNode->MaterialExpressions.AddUnique(MaterialExpression);
									}
								}
							}
						}
					}
				}
			}
		}

		struct FCompareFMaterialExpression
		{
			FORCEINLINE bool operator()( const FMaterialExpression& A, const FMaterialExpression& B ) const
			{
				return A.Name < B.Name;
			}
		};
		AllExpressionClasses.Sort(FCompareFMaterialExpression());
		struct FCompareFCategorizedMaterialExpressionNode
		{
			FORCEINLINE bool operator()( const FCategorizedMaterialExpressionNode& A, const FCategorizedMaterialExpressionNode& B ) const
			{
				return A.CategoryName < B.CategoryName;
			}
		};
		CategorizedExpressionClasses.Sort( FCompareFCategorizedMaterialExpressionNode() );

		bInitialized = true;
	}
}
void D3DObjectImage::EndCache(D3DDevice *d3d)
{
   if (!_cache_enabled || _cache.Length() == 0)
      return;
   D3DShaderPack::Current()->SetVDecl(d3d, D3DShaderPack::VDECL_XYUVC);
   D3DShaderPack::Current()->SetVS(d3d, D3DShaderPack::VS_COPY_UV_COLOR);
   D3DShaderPack::Current()->SetPS(d3d, D3DShaderPack::PS_TEX_COLOR_FILTER);

   static TArray<Vertex> sorted;
   static TArray<GroupDesc> groups;
   sorted.Allocate(0);
   groups.Allocate(0);

   bool found = true;
   while (found)
   {
      found = false;
      int cur_id = -1;
      int num = 0;
      for (int i = 0; i < _cache.Length(); i++)
      {
         // We have processed this
         if (_cache[i]->_image_id < 0)
            continue;
         found = true;
         if (cur_id < 0)
            cur_id = _cache[i]->_image_id;
         if (_cache[i]->_image_id == cur_id)
         {
            if (!_cache[i]->_with_border)
            {
               Vertex *data = _cache[i]->MakeData();
               sorted.Add(data[0]);
               sorted.Add(data[1]);
               sorted.Add(data[2]);
               sorted.Add(data[3]);
               sorted.Add(data[4]);
               sorted.Add(data[5]);
               _cache[i]->_image_id = -_cache[i]->_image_id - 1;
               num++;
            }
            else
            {
               Vertex *data = _cache[i]->MakeDataBorder();
               int last_len = sorted.Length();
               sorted.Allocate(last_len + 6 * 9);
               CopyMemory(&sorted[last_len], data, sizeof(Vertex) * 6 * 9);
               _cache[i]->_image_id = -_cache[i]->_image_id - 1;
               num += 9;
            }
         }
      }
      if (num > 0)
      {
         GroupDesc gd = {num, cur_id};
         groups.Add(gd);
      }
   }

   // Restore ids
   for (int i = 0; i < _cache.Length(); i++)
      _cache[i]->_image_id = -_cache[i]->_image_id - 1;

   D3DVertexBufferCache::CacheEntryInfo ce_info;
   if (!D3DVertexBufferCache::Current()->InitBuffer(d3d, (BYTE *)sorted.Data(),
      sorted.Length() * sizeof(Vertex), ce_info))
   {
      return;
   }
   D3DVertexBufferCache::Current()->SelectBufferToDevice(d3d, ce_info.id, sizeof(Vertex));

   HRESULT hr;
   for (int i = 0, cur = 0; i < groups.Length(); i++)
   {
      if (FAILED(hr = D3DImageCache::Current()->SelectImageToDevice(d3d, groups[i].id)))
      {
         Log("Failed to select texture: %X", (DWORD)hr);
      }
//      d3d->GetDevice()->DrawPrimitiveUP(D3DPT_TRIANGLELIST, groups[i].num * 2,
//         &sorted[cur], sizeof(Vertex));
      d3d->GetDevice()->DrawPrimitive(D3DPT_TRIANGLELIST, cur, groups[i].num * 2);
      cur += groups[i].num * 6;
   }

   DBG("Image cache drawn: %d items, %d groups", _cache.Length(), groups.Length());
   _cache_enabled = false;
}
/** UI_COMMAND takes long for the compile to optimize */
PRAGMA_DISABLE_OPTIMIZATION
void FLevelViewportCommands::RegisterCommands()
{
	UI_COMMAND( ToggleMaximize, "Maximize Viewport", "Toggles the Maximize state of the current viewport", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ToggleGameView, "Game View", "Toggles game view.  Game view shows the scene as it appears in game", EUserInterfaceActionType::ToggleButton, FInputChord( EKeys::G ) );
	UI_COMMAND( ToggleImmersive, "Immersive Mode", "Switches this viewport between immersive mode and regular mode", EUserInterfaceActionType::ToggleButton, FInputChord( EKeys::F11 ) );

	UI_COMMAND( CreateCamera, "Create Camera Here", "Creates a new camera actor at the current location of this viewport's camera", EUserInterfaceActionType::Button, FInputChord() );
	UI_COMMAND( HighResScreenshot, "High Resolution Screenshot...", "Opens the control panel for high resolution screenshots", EUserInterfaceActionType::Button, FInputChord() );
	
	UI_COMMAND( UseDefaultShowFlags, "Use Defaults", "Resets all show flags to default", EUserInterfaceActionType::Button, FInputChord() );

	UI_COMMAND( PilotSelectedActor, "Pilot Selected Actor", "Move the selected actor around using the viewport controls, and bind the viewport to the actor's location and orientation.", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Control | EModifierKey::Shift, EKeys::P ) );
	UI_COMMAND( EjectActorPilot, "Eject from Actor Pilot", "Stop piloting an actor with the current viewport. Unlocks the viewport's position and orientation from the actor the viewport is currently piloting.", EUserInterfaceActionType::Button, FInputChord() );
	UI_COMMAND( ToggleActorPilotCameraView, "Actor Pilot Camera View", "Toggles showing the exact camera view when using the viewport to pilot a camera", EUserInterfaceActionType::ToggleButton, FInputChord( EModifierKey::Control | EModifierKey::Shift, EKeys::C ) );

	UI_COMMAND( ViewportConfig_OnePane, "Layout One Pane", "Changes the viewport arrangement to one pane", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_TwoPanesH, "Layout Two Panes (horizontal)", "Changes the viewport arrangement to two panes, side-by-side", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_TwoPanesV, "Layout Two Panes (vertical)", "Changes the viewport arrangement to two panes, one above the other", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_ThreePanesLeft, "Layout Three Panes (one left, two right)", "Changes the viewport arrangement to three panes, one on the left, two on the right", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_ThreePanesRight, "Layout Three Panes (one right, two left)", "Changes the viewport arrangement to three panes, one on the right, two on the left", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_ThreePanesTop, "Layout Three Panes (one top, two bottom)", "Changes the viewport arrangement to three panes, one on the top, two on the bottom", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_ThreePanesBottom, "Layout Three Panes (one bottom, two top)", "Changes the viewport arrangement to three panes, one on the bottom, two on the top", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_FourPanesLeft, "Layout Four Panes (one left, three right)", "Changes the viewport arrangement to four panes, one on the left, three on the right", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_FourPanesRight, "Layout Four Panes (one right, three left)", "Changes the viewport arrangement to four panes, one on the right, three on the left", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_FourPanesTop, "Layout Four Panes (one top, three bottom)", "Changes the viewport arrangement to four panes, one on the top, three on the bottom", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_FourPanesBottom, "Layout Four Panes (one bottom, three top)", "Changes the viewport arrangement to four panes, one on the bottom, three on the top", EUserInterfaceActionType::ToggleButton, FInputChord() );
	UI_COMMAND( ViewportConfig_FourPanes2x2, "Layout Four Panes (2x2)", "Changes the viewport arrangement to four panes, in a 2x2 grid", EUserInterfaceActionType::ToggleButton, FInputChord() );

	UI_COMMAND( ApplyMaterialToActor, "Apply Material", "Attempts to apply a dropped material to this object", EUserInterfaceActionType::Button, FInputChord() );

	UI_COMMAND( ToggleCinematicPreview, "Toggles Cinematic Preview", "If enabled, allows Matinee or Sequencer previews to play in this viewport", EUserInterfaceActionType::ToggleButton, FInputChord() );

	UI_COMMAND( FindInLevelScriptBlueprint, "Find In Level Script", "Finds references of a selected actor in the level script blueprint", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control, EKeys::K) );
	UI_COMMAND( AdvancedSettings, "Advanced Settings...", "Opens the advanced viewport settings", EUserInterfaceActionType::Button, FInputChord());

	// Generate a command for each buffer visualization mode
	{
		struct FMaterialIterator
		{
			const TSharedRef<class FBindingContext> Parent;
			FLevelViewportCommands::TBufferVisualizationModeCommandMap& CommandMap;

			FMaterialIterator(const TSharedRef<class FBindingContext> InParent, FLevelViewportCommands::TBufferVisualizationModeCommandMap& InCommandMap)
				: Parent(InParent)
				, CommandMap(InCommandMap)
			{
			}

			void ProcessValue(const FString& InMaterialName, const UMaterial* InMaterial, const FText& InDisplayName)
			{
				FName ViewportCommandName = *(FString(TEXT("BufferVisualizationMenu")) + InMaterialName);

				FBufferVisualizationRecord& Record = CommandMap.Add(ViewportCommandName, FBufferVisualizationRecord());
				Record.Name = *InMaterialName;
				const FText MaterialNameText = FText::FromString( InMaterialName );
				Record.Command = FUICommandInfoDecl( Parent, ViewportCommandName, MaterialNameText, MaterialNameText )
					.UserInterfaceType( EUserInterfaceActionType::RadioButton )
					.DefaultChord( FInputChord() );
			}
		};


		BufferVisualizationModeCommands.Empty();
		FName ViewportCommandName = *(FString(TEXT("BufferVisualizationOverview")));
		FBufferVisualizationRecord& OverviewRecord = BufferVisualizationModeCommands.Add(ViewportCommandName, FBufferVisualizationRecord());
		OverviewRecord.Name = NAME_None;
		OverviewRecord.Command = FUICommandInfoDecl( this->AsShared(), ViewportCommandName, LOCTEXT("BufferVisualization", "Overview"), LOCTEXT("BufferVisualization", "Overview") )
			.UserInterfaceType( EUserInterfaceActionType::RadioButton )
			.DefaultChord( FInputChord() );

		FMaterialIterator It(this->AsShared(), BufferVisualizationModeCommands);
		GetBufferVisualizationData().IterateOverAvailableMaterials(It);
	}

	const TArray<FShowFlagData>& ShowFlagData = GetShowFlagMenuItems();

	// Generate a command for each show flag
	for( int32 ShowFlag = 0; ShowFlag < ShowFlagData.Num(); ++ShowFlag )
	{
		const FShowFlagData& SFData = ShowFlagData[ShowFlag];

		FFormatNamedArguments Args;
		Args.Add( TEXT("ShowFlagName"), SFData.DisplayName );
		FText LocalizedName;
		switch( SFData.Group )
		{
		case SFG_Visualize:
			LocalizedName = FText::Format( LOCTEXT("VisualizeFlagLabel", "Visualize {ShowFlagName}"), Args );
			break;
		default:
			LocalizedName = FText::Format( LOCTEXT("ShowFlagLabel", "Show {ShowFlagName}"), Args );
			break;
		}

		//@todo Slate: The show flags system does not support descriptions currently
		const FText ShowFlagDesc;

		TSharedPtr<FUICommandInfo> ShowFlagCommand 
			= FUICommandInfoDecl( this->AsShared(), SFData.ShowFlagName, LocalizedName, ShowFlagDesc )
			.UserInterfaceType( EUserInterfaceActionType::ToggleButton )
			.DefaultChord( SFData.InputChord )
			.Icon(SFData.Group == EShowFlagGroup::SFG_Normal ?
						FSlateIcon(FEditorStyle::GetStyleSetName(), FEditorStyle::Join( GetContextName(), TCHAR_TO_ANSI( *FString::Printf( TEXT(".%s"), *SFData.ShowFlagName.ToString() ) ) ) ) :
						FSlateIcon());

		ShowFlagCommands.Add( FLevelViewportCommands::FShowMenuCommand( ShowFlagCommand, SFData.DisplayName ) );
	}

	// Generate a command for each volume class
	{
		UI_COMMAND( ShowAllVolumes, "Show All Volumes", "Shows all volumes", EUserInterfaceActionType::Button, FInputChord() );
		UI_COMMAND( HideAllVolumes, "Hide All Volumes", "Hides all volumes", EUserInterfaceActionType::Button, FInputChord() );

		TArray< UClass* > VolumeClasses;
		UUnrealEdEngine::GetSortedVolumeClasses(&VolumeClasses);

		for( int32 VolumeClassIndex = 0; VolumeClassIndex < VolumeClasses.Num(); ++VolumeClassIndex )
		{
			//@todo Slate: The show flags system does not support descriptions currently
			const FText VolumeDesc;
			const FName VolumeName = VolumeClasses[VolumeClassIndex]->GetFName();

			FText DisplayName;
			FEngineShowFlags::FindShowFlagDisplayName( VolumeName.ToString(), DisplayName );

			FFormatNamedArguments Args;
			Args.Add( TEXT("ShowFlagName"), DisplayName );
			const FText LocalizedName = FText::Format( LOCTEXT("ShowFlagLabel_Visualize", "Visualize {ShowFlagName}"), Args );

			TSharedPtr<FUICommandInfo> ShowVolumeCommand 
				= FUICommandInfoDecl( this->AsShared(), VolumeName, LocalizedName, VolumeDesc )
				.UserInterfaceType( EUserInterfaceActionType::ToggleButton );

			ShowVolumeCommands.Add( FLevelViewportCommands::FShowMenuCommand( ShowVolumeCommand, DisplayName ) );
		}
	}

	// Generate a command for show/hide all layers
	{
		UI_COMMAND( ShowAllLayers, "Show All Layers", "Shows all layers", EUserInterfaceActionType::Button, FInputChord() );
		UI_COMMAND( HideAllLayers, "Hide All Layers", "Hides all layers", EUserInterfaceActionType::Button, FInputChord() );
	}

	// Generate a command for each sprite category
	{
		UI_COMMAND( ShowAllSprites, "Show All Sprites", "Shows all sprites", EUserInterfaceActionType::Button, FInputChord() );
		UI_COMMAND( HideAllSprites, "Hide All Sprites", "Hides all sprites", EUserInterfaceActionType::Button, FInputChord() );

		// get all the known layers
		// Get a fresh list as GUnrealEd->SortedSpriteInfo may not yet be built.
		TArray<FSpriteCategoryInfo> SortedSpriteInfo;
		UUnrealEdEngine::MakeSortedSpriteInfo(SortedSpriteInfo);

		FString SpritePrefix = TEXT("ShowSprite_");
		for( int32 InfoIndex = 0; InfoIndex < SortedSpriteInfo.Num(); ++InfoIndex )
		{
			const FSpriteCategoryInfo& SpriteInfo = SortedSpriteInfo[InfoIndex];

			const FName CommandName = FName( *(SpritePrefix + SpriteInfo.Category.ToString()) );

			FFormatNamedArguments Args;
			Args.Add( TEXT("SpriteName"), SpriteInfo.DisplayName );
			const FText LocalizedName = FText::Format( NSLOCTEXT("UICommands", "SpriteShowFlagName", "Show {SpriteName} Sprites"), Args );

			TSharedPtr<FUICommandInfo> ShowSpriteCommand 
				= FUICommandInfoDecl( this->AsShared(), CommandName, LocalizedName, SpriteInfo.Description )
				.UserInterfaceType( EUserInterfaceActionType::ToggleButton );

			ShowSpriteCommands.Add( FLevelViewportCommands::FShowMenuCommand( ShowSpriteCommand, SpriteInfo.DisplayName ) );
		}
	}

	// Generate a command for each Stat category
	{
		UI_COMMAND(HideAllStats, "Hide All Stats", "Hides all Stats", EUserInterfaceActionType::Button, FInputChord());

		// Bind a listener here for any additional stat commands that get registered later.
		UEngine::NewStatDelegate.AddRaw(this, &FLevelViewportCommands::HandleNewStat);
#if STATS
		FStatGroupGameThreadNotifier::Get().NewStatGroupDelegate.BindRaw(this, &FLevelViewportCommands::HandleNewStatGroup);
#endif
	}

	// Map the bookmark index to default key.
	// If the max bookmark number ever increases the new bookmarks will not have default keys
	TArray< FKey > NumberKeyNames;
	NumberKeyNames.Add( EKeys::Zero );
	NumberKeyNames.Add( EKeys::One );
	NumberKeyNames.Add( EKeys::Two );
	NumberKeyNames.Add( EKeys::Three );
	NumberKeyNames.Add( EKeys::Four );
	NumberKeyNames.Add( EKeys::Five );
	NumberKeyNames.Add( EKeys::Six );
	NumberKeyNames.Add( EKeys::Seven );
	NumberKeyNames.Add( EKeys::Eight );
	NumberKeyNames.Add( EKeys::Nine );

	for( int32 BookmarkIndex = 0; BookmarkIndex < AWorldSettings::MAX_BOOKMARK_NUMBER; ++BookmarkIndex )
	{
		TSharedRef< FUICommandInfo > JumpToBookmark =
			FUICommandInfoDecl(
			this->AsShared(), //Command class
			FName( *FString::Printf( TEXT( "JumpToBookmark%i" ), BookmarkIndex ) ), //CommandName
			FText::Format( NSLOCTEXT("LevelEditorCommands", "JumpToBookmark", "Jump to Bookmark {0}"), FText::AsNumber( BookmarkIndex ) ), //Localized label
			FText::Format( NSLOCTEXT("LevelEditorCommands", "JumpToBookmark_ToolTip", "Moves the viewport to the location and orientation stored at bookmark {0}"), FText::AsNumber( BookmarkIndex ) ) )//Localized tooltip
			.UserInterfaceType( EUserInterfaceActionType::Button ) //interface type
			.DefaultChord( FInputChord( NumberKeyNames.IsValidIndex( BookmarkIndex ) ? NumberKeyNames[BookmarkIndex] : EKeys::Invalid ) ); //default chord

		JumpToBookmarkCommands.Add( JumpToBookmark );

		TSharedRef< FUICommandInfo > SetBookmark =
			FUICommandInfoDecl(
			this->AsShared(), //Command class
			FName( *FString::Printf( TEXT( "SetBookmark%i" ), BookmarkIndex ) ), //CommandName
			FText::Format( NSLOCTEXT("LevelEditorCommands", "SetBookmark", "Set Bookmark {0}"), FText::AsNumber( BookmarkIndex ) ), //Localized label
			FText::Format( NSLOCTEXT("LevelEditorCommands", "SetBookmark_ToolTip", "Stores the viewports location and orientation in bookmark {0}"), FText::AsNumber( BookmarkIndex ) ) )//Localized tooltip
			.UserInterfaceType( EUserInterfaceActionType::Button ) //interface type
			.DefaultChord( FInputChord( EModifierKey::Control, NumberKeyNames.IsValidIndex( BookmarkIndex ) ? NumberKeyNames[BookmarkIndex] : EKeys::Invalid ) ); //default chord

		SetBookmarkCommands.Add( SetBookmark );

		TSharedRef< FUICommandInfo > ClearBookMark =
			FUICommandInfoDecl(
			this->AsShared(), //Command class
			FName( *FString::Printf( TEXT( "ClearBookmark%i" ), BookmarkIndex ) ), //CommandName
			FText::Format( NSLOCTEXT("LevelEditorCommands", "ClearBookmark", "Clear Bookmark {0}"), FText::AsNumber( BookmarkIndex ) ), //Localized label
			FText::Format( NSLOCTEXT("LevelEditorCommands", "ClearBookmark_ToolTip", "Clears the viewports location and orientation in bookmark {0}"), FText::AsNumber( BookmarkIndex ) ) )//Localized tooltip
			.UserInterfaceType( EUserInterfaceActionType::Button ) //interface type
			.DefaultChord( FInputChord() ); //default chord 

		ClearBookmarkCommands.Add( ClearBookMark );
	}
	UI_COMMAND( ClearAllBookMarks, "Clear All Bookmarks", "Clears all the bookmarks", EUserInterfaceActionType::Button, FInputChord() );

	UI_COMMAND( EnablePreviewMesh, "Hold To Enable Preview Mesh", "When held down a preview mesh appears under the cursor", EUserInterfaceActionType::Button, FInputChord(EKeys::Backslash) );
	UI_COMMAND( CyclePreviewMesh, "Cycles Preview Mesh", "Cycles available preview meshes", EUserInterfaceActionType::Button, FInputChord( EModifierKey::Shift, EKeys::Backslash ) );

}
void AActor::RerunConstructionScripts()
{
	// don't allow (re)running construction scripts on dying actors
	bool bAllowReconstruction = !IsPendingKill() && !HasAnyFlags(RF_BeginDestroyed|RF_FinishDestroyed);
#if WITH_EDITOR
	if(bAllowReconstruction && GIsEditor)
	{
		// Generate the blueprint hierarchy for this actor
		TArray<UBlueprint*> ParentBPStack;
		bAllowReconstruction = UBlueprint::GetBlueprintHierarchyFromClass(GetClass(), ParentBPStack);
		if(bAllowReconstruction)
		{
			for(int i = ParentBPStack.Num() - 1; i > 0 && bAllowReconstruction; --i)
			{
				const UBlueprint* ParentBP = ParentBPStack[i];
				if(ParentBP && ParentBP->bBeingCompiled)
				{
					// don't allow (re)running construction scripts if a parent BP is being compiled
					bAllowReconstruction = false;
				}
			}
		}
	}
#endif
	if(bAllowReconstruction)
	{
		// Temporarily suspend the undo buffer; we don't need to record reconstructed component objects into the current transaction
		ITransaction* CurrentTransaction = GUndo;
		GUndo = NULL;
		
		// Create cache to store component data across rerunning construction scripts
		FComponentInstanceDataCache InstanceDataCache(this);

		// If there are attached objects detach them and store the socket names
		TArray<AActor*> AttachedActors;
		GetAttachedActors(AttachedActors);

		// Struct to store info about attached actors
		struct FAttachedActorInfo
		{
			AActor* AttachedActor;
			FName AttachedToSocket;
		};

		// Save info about attached actors
		TArray<FAttachedActorInfo> AttachedActorInfos;
		for( AActor* AttachedActor : AttachedActors)
		{
			USceneComponent* EachRoot = AttachedActor->GetRootComponent();
			// If the component we are attached to is about to go away...
			if( EachRoot && EachRoot->AttachParent && EachRoot->AttachParent->bCreatedByConstructionScript )
			{
				// Save info about actor to reattach
				FAttachedActorInfo Info;
				Info.AttachedActor = AttachedActor;
				Info.AttachedToSocket = EachRoot->AttachSocketName;
				AttachedActorInfos.Add(Info);

				// Now detach it
				AttachedActor->Modify();
				EachRoot->DetachFromParent(true);					
			}
		}

		// Save off original pose of the actor
		FTransform OldTransform = FTransform::Identity;
		FName  SocketName;
		AActor* Parent = NULL;
		if (RootComponent != NULL)
		{
			// Do not need to detach if root component is not going away
			if(RootComponent->AttachParent != NULL && RootComponent->bCreatedByConstructionScript)
			{
				Parent = RootComponent->AttachParent->GetOwner();
				// Root component should never be attached to another component in the same actor!
				if(Parent == this)
				{
					UE_LOG(LogActor, Warning, TEXT("RerunConstructionScripts: RootComponent (%s) attached to another component in this Actor (%s)."), *RootComponent->GetPathName(), *Parent->GetPathName());
					Parent = NULL;
				}

				SocketName = RootComponent->AttachSocketName;
				//detach it to remove any scaling 
				RootComponent->DetachFromParent(true);
			}
			OldTransform = RootComponent->ComponentToWorld;
		}

		// Destroy existing components
		DestroyConstructedComponents();

		// Reset random streams
		ResetPropertiesForConstruction();

		// Run the construction scripts
		OnConstruction(OldTransform);

		if(Parent)
		{
			USceneComponent* ChildRoot =	GetRootComponent();
			USceneComponent* ParentRoot =	Parent->GetRootComponent();
			if(ChildRoot != NULL && ParentRoot != NULL)
			{
				ChildRoot->AttachTo( ParentRoot, SocketName, EAttachLocation::KeepWorldPosition );
			}
		}

		// Apply per-instance data.
		InstanceDataCache.ApplyToActor(this);

		// If we had attached children reattach them now - unless they are already attached
		for(FAttachedActorInfo& Info : AttachedActorInfos)
		{
			// If this actor is no longer attached to anything, reattach
			if (Info.AttachedActor->GetAttachParentActor() == NULL)
			{
				USceneComponent* ChildRoot = Info.AttachedActor->GetRootComponent();
				if (ChildRoot && ChildRoot->AttachParent != RootComponent)
				{
					ChildRoot->AttachTo(RootComponent, Info.AttachedToSocket, EAttachLocation::KeepWorldPosition);
					ChildRoot->UpdateComponentToWorld();
				}
			}
		}

		// Restore the undo buffer
		GUndo = CurrentTransaction;
	}
}
void FMoveSection::OnDrag( const FPointerEvent& MouseEvent, const FVector2D& LocalMousePos, const FTimeToPixel& TimeToPixelConverter, TSharedPtr<FTrackNode> SequencerNode )
{
	if( Section.IsValid() )
	{
		auto Sections = SequencerNode->GetSections();
		TArray<UMovieSceneSection*> MovieSceneSections;
		for (int32 i = 0; i < Sections.Num(); ++i)
		{
			MovieSceneSections.Add(Sections[i]->GetSectionObject());
		}
		

		FVector2D TotalDelta = LocalMousePos - DragOffset;
		float DistanceMoved = TotalDelta.X / TimeToPixelConverter.GetPixelsPerInput();
		
		float DeltaTime = DistanceMoved;
		
		if ( Settings->GetIsSnapEnabled() )
		{
			bool bSnappedToSection = false;
			if ( Settings->GetSnapSectionTimesToSections() )
			{
				TArray<float> TimesToSnapTo;
				GetSectionSnapTimes(TimesToSnapTo, Section.Get(), SequencerNode, true);

				TArray<float> TimesToSnap;
				TimesToSnap.Add(DistanceMoved + Section->GetStartTime());
				TimesToSnap.Add(DistanceMoved + Section->GetEndTime());

				float OutSnappedTime = 0.f;
				float OutNewTime = 0.f;
				if (SnapToTimes(TimesToSnap, TimesToSnapTo, TimeToPixelConverter, OutSnappedTime, OutNewTime))
				{
					DeltaTime = OutNewTime - (OutSnappedTime - DistanceMoved);
					bSnappedToSection = true;
				}
			}

			if ( bSnappedToSection == false && Settings->GetSnapSectionTimesToInterval() )
			{
				float NewStartTime = DistanceMoved + Section->GetStartTime();
				DeltaTime = Settings->SnapTimeToInterval( NewStartTime ) - Section->GetStartTime();
			}
		}

		int32 TargetRowIndex = Section->GetRowIndex();

		// vertical dragging - master tracks only
		if (SequencerNode->GetTrack()->SupportsMultipleRows() && Sections.Num() > 1)
		{
			float TrackHeight = Sections[0]->GetSectionHeight();

			if (LocalMousePos.Y < 0.f || LocalMousePos.Y > TrackHeight)
			{
				int32 MaxRowIndex = 0;
				for (int32 i = 0; i < Sections.Num(); ++i)
				{
					if (Sections[i]->GetSectionObject() != Section.Get())
					{
						MaxRowIndex = FMath::Max(MaxRowIndex, Sections[i]->GetSectionObject()->GetRowIndex());
					}
				}

				TargetRowIndex = FMath::Clamp(Section->GetRowIndex() + FMath::FloorToInt(LocalMousePos.Y / TrackHeight),
					0, MaxRowIndex + 1);
			}
		}
		
		bool bDeltaX = !FMath::IsNearlyZero(TotalDelta.X);
		bool bDeltaY = TargetRowIndex != Section->GetRowIndex();

		if (bDeltaX && bDeltaY &&
			!Section->OverlapsWithSections(MovieSceneSections, TargetRowIndex - Section->GetRowIndex(), DeltaTime))
		{
			Section->MoveSection(DeltaTime, DraggedKeyHandles);
			Section->SetRowIndex(TargetRowIndex);
		}
		else
		{
			if (bDeltaY &&
				!Section->OverlapsWithSections(MovieSceneSections, TargetRowIndex - Section->GetRowIndex(), 0.f))
			{
				Section->SetRowIndex(TargetRowIndex);
			}

			if (bDeltaX)
			{
				if (!Section->OverlapsWithSections(MovieSceneSections, 0, DeltaTime))
				{
					Section->MoveSection(DeltaTime, DraggedKeyHandles);
				}
				else
				{
					// Find the borders of where you can move to
					TRange<float> SectionBoundaries = GetSectionBoundaries(Section.Get(), SequencerNode);

					float LeftMovementMaximum = SectionBoundaries.GetLowerBoundValue() - Section->GetStartTime();
					float RightMovementMaximum = SectionBoundaries.GetUpperBoundValue() - Section->GetEndTime();

					// Tell the section to move itself and any data it has
					Section->MoveSection( FMath::Clamp(DeltaTime, LeftMovementMaximum, RightMovementMaximum), DraggedKeyHandles );
				}
			}
		}
	}
}
void FMoveKeys::OnDrag( const FPointerEvent& MouseEvent, const FVector2D& LocalMousePos, const FTimeToPixel& TimeToPixelConverter, TSharedPtr<FTrackNode> SequencerNode )
{
	// Convert the delta position to a delta time amount that was moved
	float MouseTime = TimeToPixelConverter.PixelToTime(LocalMousePos.X);
	float SelectedKeyTime = DraggedKey.KeyArea->GetKeyTime(DraggedKey.KeyHandle.GetValue());
	float DistanceMoved = MouseTime - SelectedKeyTime;

	if( DistanceMoved != 0.0f )
	{
		float TimeDelta = DistanceMoved;
		// Snapping
		if ( Settings->GetIsSnapEnabled() )
		{
			bool bSnappedToKeyTime = false;
			if ( Settings->GetSnapKeyTimesToKeys() )
			{
				TArray<float> OutSnapTimes;
				GetKeySnapTimes(OutSnapTimes, SequencerNode);

				TArray<float> InitialTimes;
				for ( FSelectedKey SelectedKey : SelectedKeys )
				{
					InitialTimes.Add(SelectedKey.KeyArea->GetKeyTime(SelectedKey.KeyHandle.GetValue()) + DistanceMoved);
				}
				float OutInitialTime = 0.f;
				float OutSnapTime = 0.f;
				if ( SnapToTimes( InitialTimes, OutSnapTimes, TimeToPixelConverter, OutInitialTime, OutSnapTime ) )
				{
					bSnappedToKeyTime = true;
					TimeDelta = OutSnapTime - (OutInitialTime - DistanceMoved);
				}
			}

			if ( bSnappedToKeyTime == false && Settings->GetSnapKeyTimesToInterval() )
			{
				TimeDelta = Settings->SnapTimeToInterval( MouseTime ) - SelectedKeyTime;
			}
		}

		for( FSelectedKey SelectedKey : SelectedKeys )
		{
			UMovieSceneSection* Section = SelectedKey.Section;

			TSharedPtr<IKeyArea>& KeyArea = SelectedKey.KeyArea;


			// Tell the key area to move the key.  We reset the key index as a result of the move because moving a key can change it's internal index 
			KeyArea->MoveKey( SelectedKey.KeyHandle.GetValue(), TimeDelta );

			// Update the key that moved
			float NewKeyTime = KeyArea->GetKeyTime( SelectedKey.KeyHandle.GetValue() );

			// If the key moves outside of the section resize the section to fit the key
			// @todo Sequencer - Doesn't account for hitting other sections 
			if( NewKeyTime > Section->GetEndTime() )
			{
				Section->SetEndTime( NewKeyTime );
			}
			else if( NewKeyTime < Section->GetStartTime() )
			{
				Section->SetStartTime( NewKeyTime );
			}
		}
	}
}
TSharedRef< SWidget > FLevelEditorToolBar::GenerateQuickSettingsMenu( TSharedRef<FUICommandList> InCommandList )
{
#define LOCTEXT_NAMESPACE "LevelToolBarViewMenu"

	// Get all menu extenders for this context menu from the level editor module
	FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( TEXT("LevelEditor") );
	TArray<FLevelEditorModule::FLevelEditorMenuExtender> MenuExtenderDelegates = LevelEditorModule.GetAllLevelEditorToolbarViewMenuExtenders();

	TArray<TSharedPtr<FExtender>> Extenders;
	for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
	{
		if (MenuExtenderDelegates[i].IsBound())
		{
			Extenders.Add(MenuExtenderDelegates[i].Execute(InCommandList));
		}
	}
	TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);

	const bool bShouldCloseWindowAfterMenuSelection = true;
	FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList, MenuExtender );

	MenuBuilder.BeginSection("LevelEditorSelection", LOCTEXT("SelectionHeading","Selection") );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AllowTranslucentSelection );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().AllowGroupSelection );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().StrictBoxSelect );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorEditing", LOCTEXT("EditingHeading", "Editing") );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ShowTransformWidget );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().DrawBrushMarkerPolys );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorPreview", LOCTEXT("PreviewHeading", "Previewing") );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().OnlyLoadVisibleInPIE );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ToggleParticleSystemLOD );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ToggleParticleSystemHelpers );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ToggleFreezeParticleSimulation );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ToggleLODViewLocking );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LevelStreamingVolumePrevis );

		MenuBuilder.AddSubMenu(
			LOCTEXT( "ScalabilitySubMenu", "Engine Scalability Settings" ),
			LOCTEXT( "ScalabilitySubMenu_ToolTip", "Open the engine scalability settings" ),
			FNewMenuDelegate::CreateStatic( &MakeScalabilityMenu ) );

		MenuBuilder.AddSubMenu(
			LOCTEXT( "MaterialQualityLevelSubMenu", "Material Quality Level" ),
			LOCTEXT( "MaterialQualityLevelSubMenu_ToolTip", "Sets the value of the CVar \"r.MaterialQualityLevel\" (low=0, high=1). This affects materials via the QualitySwitch material expression." ),
			FNewMenuDelegate::CreateStatic( &MakeMaterialQualityLevelMenu ) );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorAudio", LOCTEXT("AudioHeading", "Real Time Audio") );
	{
		TSharedRef<SWidget> VolumeItem = SNew(SHorizontalBox)
											+SHorizontalBox::Slot()
											.FillWidth(0.9f)
											.Padding( FMargin(2.0f, 0.0f, 0.0f, 0.0f) )
											[
												SNew(SVolumeControl)
												.ToolTipText_Static(&FLevelEditorActionCallbacks::GetAudioVolumeToolTip)
												.Volume_Static(&FLevelEditorActionCallbacks::GetAudioVolume)
												.OnVolumeChanged_Static(&FLevelEditorActionCallbacks::OnAudioVolumeChanged)
												.Muted_Static(&FLevelEditorActionCallbacks::GetAudioMuted)
												.OnMuteChanged_Static(&FLevelEditorActionCallbacks::OnAudioMutedChanged)
											]
											+SHorizontalBox::Slot()
											.FillWidth(0.1f);

		MenuBuilder.AddWidget(VolumeItem, LOCTEXT("VolumeControlLabel","Volume"));
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorActorSnap", LOCTEXT("ActorSnapHeading","Actor Snap") );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().EnableActorSnap );
		TSharedRef<SWidget> SnapItem = 
		SNew(SHorizontalBox)
	          +SHorizontalBox::Slot()
	          .FillWidth(0.9f)
	          [
		          SNew(SSlider)
		          .ToolTipText_Static(&FLevelEditorActionCallbacks::GetActorSnapTooltip)
		          .Value_Static(&FLevelEditorActionCallbacks::GetActorSnapSetting)
		          .OnValueChanged_Static(&FLevelEditorActionCallbacks::SetActorSnapSetting)
	          ]
	          +SHorizontalBox::Slot()
	          .FillWidth(0.1f);
		MenuBuilder.AddWidget(SnapItem, LOCTEXT("ActorSnapLabel","Distance"));
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection( "Snapping", LOCTEXT("SnappingHeading","Snapping") );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ToggleSocketSnapping );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().EnableVertexSnap );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorViewport", LOCTEXT("ViewportHeading", "Viewport") );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().ToggleHideViewportUI );
	}
	MenuBuilder.EndSection();

#undef LOCTEXT_NAMESPACE

	return MenuBuilder.MakeWidget();
}
TSharedRef< SWidget > FLevelEditorToolBar::GenerateBuildMenuContent( TSharedRef<FUICommandList> InCommandList )
{
#define LOCTEXT_NAMESPACE "LevelToolBarBuildMenu"

	// Get all menu extenders for this context menu from the level editor module
	FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>( TEXT("LevelEditor") );
	TArray<FLevelEditorModule::FLevelEditorMenuExtender> MenuExtenderDelegates = LevelEditorModule.GetAllLevelEditorToolbarBuildMenuExtenders();

	TArray<TSharedPtr<FExtender>> Extenders;
	for (int32 i = 0; i < MenuExtenderDelegates.Num(); ++i)
	{
		if (MenuExtenderDelegates[i].IsBound())
		{
			Extenders.Add(MenuExtenderDelegates[i].Execute(InCommandList));
		}
	}
	TSharedPtr<FExtender> MenuExtender = FExtender::Combine(Extenders);

	const bool bShouldCloseWindowAfterMenuSelection = true;
	FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList, MenuExtender );

	struct FLightingMenus
	{

		/** Generates a lighting quality sub-menu */
		static void MakeLightingQualityMenu( FMenuBuilder& InMenuBuilder )
		{
			InMenuBuilder.BeginSection("LevelEditorBuildLightingQuality", LOCTEXT( "LightingQualityHeading", "Quality Level" ) );
			{
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingQuality_Production );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingQuality_High );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingQuality_Medium );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingQuality_Preview );
			}
			InMenuBuilder.EndSection();
		}

		/** Generates a lighting tools sub-menu */
		static void MakeLightingToolsMenu( FMenuBuilder& InMenuBuilder )
		{
			InMenuBuilder.BeginSection("LevelEditorBuildLightingTools", LOCTEXT( "LightingToolsHeading", "Light Environments" ) );
			{
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingTools_ShowBounds );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingTools_ShowTraces );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingTools_ShowDirectOnly );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingTools_ShowIndirectOnly );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingTools_ShowIndirectSamples );
			}
			InMenuBuilder.EndSection();
		}

		/** Generates a lighting density sub-menu */
		static void MakeLightingDensityMenu( FMenuBuilder& InMenuBuilder )
		{
			InMenuBuilder.BeginSection("LevelEditorBuildLightingDensity", LOCTEXT( "LightingDensityHeading", "Density Rendering" ) );
			{
				TSharedRef<SWidget> Ideal =		SNew(SHorizontalBox)
												+SHorizontalBox::Slot()
												.Padding( FMargin( 27.0f, 0.0f, 0.0f, 0.0f ) )
												.FillWidth(1.0f)
												[
													SNew(SSpinBox<float>)
													.MinValue(0.f)
													.MaxValue(100.f)
													.Value(FLevelEditorActionCallbacks::GetLightingDensityIdeal())
													.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingDensityIdeal)
												];
				InMenuBuilder.AddWidget(Ideal, LOCTEXT("LightingDensity_Ideal","Ideal Density"));
				
				TSharedRef<SWidget> Maximum =	SNew(SHorizontalBox)
												+SHorizontalBox::Slot()
												.FillWidth(1.0f)
												[
													SNew(SSpinBox<float>)
													.MinValue(0.01f)
													.MaxValue(100.01f)
													.Value(FLevelEditorActionCallbacks::GetLightingDensityMaximum())
													.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingDensityMaximum)
												];
				InMenuBuilder.AddWidget(Maximum, LOCTEXT("LightingDensity_Maximum","Maximum Density"));

				TSharedRef<SWidget> ClrScale =	SNew(SHorizontalBox)
												+SHorizontalBox::Slot()
												.Padding( FMargin( 35.0f, 0.0f, 0.0f, 0.0f ) )
												.FillWidth(1.0f)
												[
													SNew(SSpinBox<float>)
													.MinValue(0.f)
													.MaxValue(10.f)
													.Value(FLevelEditorActionCallbacks::GetLightingDensityColorScale())
													.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingDensityColorScale)
												];
				InMenuBuilder.AddWidget(ClrScale, LOCTEXT("LightingDensity_ColorScale","Color Scale"));

				TSharedRef<SWidget> GrayScale =	SNew(SHorizontalBox)
												+SHorizontalBox::Slot()
												.Padding( FMargin( 11.0f, 0.0f, 0.0f, 0.0f ) )
												.FillWidth(1.0f)
												[
													SNew(SSpinBox<float>)
													.MinValue(0.f)
													.MaxValue(10.f)
													.Value(FLevelEditorActionCallbacks::GetLightingDensityGrayscaleScale())
													.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingDensityGrayscaleScale)
												];
				InMenuBuilder.AddWidget(GrayScale, LOCTEXT("LightingDensity_GrayscaleScale","Grayscale Scale"));

				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingDensity_RenderGrayscale );
			}
			InMenuBuilder.EndSection();
		}

		/** Generates a lighting resolution sub-menu */
		static void MakeLightingResolutionMenu( FMenuBuilder& InMenuBuilder )
		{
			InMenuBuilder.BeginSection("LevelEditorBuildLightingResolution1", LOCTEXT( "LightingResolutionHeading1", "Primitive Types" ) );
			{
				TSharedRef<SWidget> Meshes =	SNew(SHorizontalBox)
												+SHorizontalBox::Slot()
												.AutoWidth()
												[
													SNew( SCheckBox )
													.Style( FEditorStyle::Get(), "Menu.CheckBox" )
													.ToolTipText(LOCTEXT( "StaticMeshesToolTip", "Static Meshes will be adjusted if checked." ))
													.IsChecked_Static(&FLevelEditorActionCallbacks::IsLightingResolutionStaticMeshesChecked)
													.OnCheckStateChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionStaticMeshes)
													.Content()
													[
														SNew( STextBlock )
														.Text( LOCTEXT("StaticMeshes", "Static Meshes") )
													]
												]
												+SHorizontalBox::Slot()
												.AutoWidth()
												.Padding( FMargin( 4.0f, 0.0f, 11.0f, 0.0f ) )
												[
													SNew(SSpinBox<float>)
													.MinValue(4.f)
													.MaxValue(4096.f)
													.ToolTipText(LOCTEXT( "LightingResolutionStaticMeshesMinToolTip", "The minimum lightmap resolution for static mesh adjustments. Anything outside of Min/Max range will not be touched when adjusting." ))
													.Value(FLevelEditorActionCallbacks::GetLightingResolutionMinSMs())
													.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionMinSMs)
												]
												+SHorizontalBox::Slot()
												.AutoWidth()
												[
													SNew(SSpinBox<float>)
													.MinValue(4.f)
													.MaxValue(4096.f)
													.ToolTipText(LOCTEXT( "LightingResolutionStaticMeshesMaxToolTip", "The maximum lightmap resolution for static mesh adjustments. Anything outside of Min/Max range will not be touched when adjusting." ))
													.Value(FLevelEditorActionCallbacks::GetLightingResolutionMaxSMs())
													.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionMaxSMs)
												];
				InMenuBuilder.AddWidget(Meshes, FText::GetEmpty(), true);
				
				TSharedRef<SWidget> BSPs =		SNew(SHorizontalBox)
												+SHorizontalBox::Slot()
												.AutoWidth()
												[
													SNew( SCheckBox )
													.Style(FEditorStyle::Get(), "Menu.CheckBox")
													.ToolTipText(LOCTEXT( "BSPSurfacesToolTip", "BSP Surfaces will be adjusted if checked." ))
													.IsChecked_Static(&FLevelEditorActionCallbacks::IsLightingResolutionBSPSurfacesChecked)
													.OnCheckStateChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionBSPSurfaces)
													.Content()
													[
														SNew( STextBlock )
														.Text( LOCTEXT("BSPSurfaces", "BSP Surfaces") )
													]
												]
												+SHorizontalBox::Slot()
												.AutoWidth()
												.Padding( FMargin( 6.0f, 0.0f, 4.0f, 0.0f ) )
												[
													SNew(SSpinBox<float>)
													.MinValue(1.f)
													.MaxValue(63556.f)
													.ToolTipText(LOCTEXT( "LightingResolutionBSPsMinToolTip", "The minimum lightmap resolution of a BSP surface to adjust. When outside of the Min/Max range, the BSP surface will no be altered." ))
													.Value(FLevelEditorActionCallbacks::GetLightingResolutionMinBSPs())
													.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionMinBSPs)
												]
												+SHorizontalBox::Slot()
												.AutoWidth()
												[
													SNew(SSpinBox<float>)
													.MinValue(1.f)
													.MaxValue(63556.f)
													.ToolTipText(LOCTEXT( "LightingResolutionBSPsMaxToolTip", "The maximum lightmap resolution of a BSP surface to adjust. When outside of the Min/Max range, the BSP surface will no be altered." ))
													.Value(FLevelEditorActionCallbacks::GetLightingResolutionMaxBSPs())
													.OnValueChanged_Static(&FLevelEditorActionCallbacks::SetLightingResolutionMaxBSPs)
												];
				InMenuBuilder.AddWidget(BSPs, FText::GetEmpty(), true);
			}
			InMenuBuilder.EndSection(); //LevelEditorBuildLightingResolution1

			InMenuBuilder.BeginSection("LevelEditorBuildLightingResolution2", LOCTEXT( "LightingResolutionHeading2", "Select Options" ) );
			{
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingResolution_CurrentLevel );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingResolution_SelectedLevels );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingResolution_AllLoadedLevels );
				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingResolution_SelectedObjectsOnly );
			}
			InMenuBuilder.EndSection();

			InMenuBuilder.BeginSection("LevelEditorBuildLightingResolution3", LOCTEXT( "LightingResolutionHeading3", "Ratio" ) );
			{
				TSharedRef<SWidget> Ratio =		SNew(SSpinBox<int32>)
												.MinValue(0)
												.MaxValue(400)
												.ToolTipText(LOCTEXT( "LightingResolutionRatioToolTip", "Ratio to apply (New Resolution = Ratio / 100.0f * CurrentResolution)." ))
												.Value(FLevelEditorActionCallbacks::GetLightingResolutionRatio())
												.OnEndSliderMovement_Static(&FLevelEditorActionCallbacks::SetLightingResolutionRatio)
												.OnValueCommitted_Static(&FLevelEditorActionCallbacks::SetLightingResolutionRatioCommit);
				InMenuBuilder.AddWidget(Ratio, LOCTEXT( "LightingResolutionRatio", "Ratio" ));
			}
			InMenuBuilder.EndSection();
		}

		/** Generates a lighting info dialogs sub-menu */
		static void MakeLightingInfoMenu( FMenuBuilder& InMenuBuilder )
		{
			InMenuBuilder.BeginSection("LevelEditorBuildLightingInfo", LOCTEXT( "LightingInfoHeading", "Lighting Info Dialogs" ) );
			{
				InMenuBuilder.AddSubMenu(
					LOCTEXT( "LightingToolsSubMenu", "Lighting Tools" ),
					LOCTEXT( "LightingToolsSubMenu_ToolTip", "Shows the Lighting Tools options." ),
					FNewMenuDelegate::CreateStatic( &FLightingMenus::MakeLightingToolsMenu ) );
					
				InMenuBuilder.AddSubMenu(
					LOCTEXT( "LightingDensityRenderingSubMenu", "LightMap Density Rendering Options" ),
					LOCTEXT( "LightingDensityRenderingSubMenu_ToolTip", "Shows the LightMap Density Rendering viewmode options." ),
					FNewMenuDelegate::CreateStatic( &FLightingMenus::MakeLightingDensityMenu ) );

				InMenuBuilder.AddSubMenu(
					LOCTEXT( "LightingResolutionAdjustmentSubMenu", "LightMap Resolution Adjustment" ),
					LOCTEXT( "LightingResolutionAdjustmentSubMenu_ToolTip", "Shows the LightMap Resolution Adjustment options." ),
					FNewMenuDelegate::CreateStatic( &FLightingMenus::MakeLightingResolutionMenu ) );

				InMenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingStaticMeshInfo, NAME_None, LOCTEXT( "BuildLightingInfo_LightingStaticMeshInfo", "Lighting StaticMesh Info..." ) );
			}
			InMenuBuilder.EndSection();
		}
	};

	MenuBuilder.BeginSection("LevelEditorLighting", LOCTEXT( "LightingHeading", "Lighting" ) );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildLightingOnly, NAME_None, LOCTEXT( "BuildLightingOnlyHeading", "Build Lighting Only" ) );

		MenuBuilder.AddSubMenu(
			LOCTEXT( "LightingQualitySubMenu", "Lighting Quality" ),
			LOCTEXT( "LightingQualitySubMenu_ToolTip", "Allows you to select the quality level for precomputed lighting" ),
			FNewMenuDelegate::CreateStatic( &FLightingMenus::MakeLightingQualityMenu ) );

		MenuBuilder.AddSubMenu(
			LOCTEXT( "BuildLightingInfoSubMenu", "Lighting Info" ),
			LOCTEXT( "BuildLightingInfoSubMenu_ToolTip", "Access the lighting info dialogs" ),
			FNewMenuDelegate::CreateStatic( &FLightingMenus::MakeLightingInfoMenu ) );

		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingBuildOptions_UseErrorColoring );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LightingBuildOptions_ShowLightingStats );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorReflections", LOCTEXT( "ReflectionHeading", "Reflections" ) );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildReflectionCapturesOnly );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorVisibility", LOCTEXT( "VisibilityHeading", "Visibility" ) );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildLightingOnly_VisibilityOnly );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorGeometry", LOCTEXT( "GeometryHeading", "Geometry" ) );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildGeometryOnly );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildGeometryOnly_OnlyCurrentLevel );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorNavigation", LOCTEXT( "NavigationHeading", "Navigation" ) );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildPathsOnly );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LevelEditorAutomation", LOCTEXT( "AutomationHeading", "Automation" ) );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().BuildAndSubmitToSourceControl );
	}
	MenuBuilder.EndSection();


	// Texture Stats
	MenuBuilder.BeginSection("LevelEditorStatistics", LOCTEXT( "Statistics", "Statistics" ) );
	{
	    MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().SceneStats, NAME_None, LOCTEXT("OpenSceneStats", "Scene Stats") );
	    MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().TextureStats, NAME_None, LOCTEXT("OpenTextureStats", "TextureStats Stats") );
	}
	MenuBuilder.EndSection();

	// Map Check
	MenuBuilder.BeginSection("LevelEditorVerification", LOCTEXT( "VerificationHeading", "Verification" ) );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().MapCheck, NAME_None, LOCTEXT("OpenMapCheck", "Map Check") );
	}
	MenuBuilder.EndSection();

#undef LOCTEXT_NAMESPACE

	return MenuBuilder.MakeWidget();
}
	virtual void GetSupportedFormats(TArray<FName>& OutFormats) const
	{
		OutFormats.Add(NAME_SF_METAL);
		OutFormats.Add(NAME_SF_METAL_MRT);
		OutFormats.Add(NAME_SF_METAL_SM5);
	}
int32 FAnimationSection::OnPaintSection( const FGeometry& AllottedGeometry, const FSlateRect& SectionClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, bool bParentEnabled ) const
{
	UMovieSceneAnimationSection* AnimSection = Cast<UMovieSceneAnimationSection>(&Section);
	
	FTimeToPixel TimeToPixelConverter( AllottedGeometry, TRange<float>( Section.GetStartTime(), Section.GetEndTime() ) );

	// Add a box for the section
	FSlateDrawElement::MakeBox(
		OutDrawElements,
		LayerId,
		AllottedGeometry.ToPaintGeometry(),
		FEditorStyle::GetBrush("Sequencer.GenericSection.Background"),
		SectionClippingRect,
		ESlateDrawEffect::None,
		FLinearColor(0.7f, 0.4f, 0.7f, 1.f)
	);

	// Darken the part that doesn't have animation
	if (AnimSection->GetAnimationStartTime() > AnimSection->GetStartTime())
	{
		float StartDarkening = AnimSection->GetStartTime();
		float EndDarkening = FMath::Min(AnimSection->GetAnimationStartTime(), AnimSection->GetEndTime());
		
		float StartPixels = TimeToPixelConverter.TimeToPixel(StartDarkening);
		float EndPixels = TimeToPixelConverter.TimeToPixel(EndDarkening);

		FSlateDrawElement::MakeBox(
			OutDrawElements,
			LayerId + 1,
			AllottedGeometry.ToPaintGeometry(FVector2D(StartPixels, 0), FVector2D(EndPixels - StartPixels, AllottedGeometry.Size.Y)),
			FEditorStyle::GetBrush("WhiteTexture"),
			SectionClippingRect,
			ESlateDrawEffect::None,
			FLinearColor(0.f, 0.f, 0.f, 0.3f)
		);
	}

	// Add lines where the animation starts and ends/loops
	float CurrentTime = AnimSection->GetAnimationStartTime();
	while (CurrentTime < AnimSection->GetEndTime())
	{
		if (CurrentTime > AnimSection->GetStartTime())
		{
			float CurrentPixels = TimeToPixelConverter.TimeToPixel(CurrentTime);

			TArray<FVector2D> Points;
			Points.Add(FVector2D(CurrentPixels, 0));
			Points.Add(FVector2D(CurrentPixels, AllottedGeometry.Size.Y));

			FSlateDrawElement::MakeLines(
				OutDrawElements,
				LayerId + 2,
				AllottedGeometry.ToPaintGeometry(),
				Points,
				SectionClippingRect
			);
		}
		CurrentTime += AnimSection->GetAnimationDuration();
	}

	return LayerId+3;
}
void FSoundCueGraphConnectionDrawingPolicy::BuildAudioFlowRoadmap()
{
	UAudioComponent* PreviewAudioComponent = GEditor->GetPreviewAudioComponent();
	FAudioDevice* AudioDevice = PreviewAudioComponent ? PreviewAudioComponent->GetAudioDevice() : nullptr;

	if (AudioDevice)
	{
		USoundCueGraph* SoundCueGraph = CastChecked<USoundCueGraph>(GraphObj);
		USoundCue* SoundCue = SoundCueGraph->GetSoundCue();


		if (PreviewAudioComponent && PreviewAudioComponent->IsPlaying() && PreviewAudioComponent->Sound == SoundCue)
		{
			TArray<FWaveInstance*> WaveInstances;
			const int32 FirstActiveIndex = AudioDevice->GetSortedActiveWaveInstances(WaveInstances, ESortedActiveWaveGetType::QueryOnly);

			// Run through the active instances and cull out anything that isn't related to this graph
			if (FirstActiveIndex > 0)
			{
				WaveInstances.RemoveAt(0, FirstActiveIndex + 1);
			}

			for (int32 WaveIndex = WaveInstances.Num() - 1; WaveIndex >= 0 ; --WaveIndex)
			{
				UAudioComponent* WaveInstanceAudioComponent = UAudioComponent::GetAudioComponentFromID(WaveInstances[WaveIndex]->ActiveSound->GetAudioComponentID());
				if (WaveInstanceAudioComponent != PreviewAudioComponent)
				{
					WaveInstances.RemoveAtSwap(WaveIndex);
				}
			}

			for (int32 WaveIndex = 0; WaveIndex < WaveInstances.Num(); ++WaveIndex)
			{
				TArray<USoundNode*> PathToWaveInstance;
				if (SoundCue->FindPathToNode(WaveInstances[WaveIndex]->WaveInstanceHash, PathToWaveInstance))
				{
					TArray<USoundCueGraphNode_Root*> RootNode;
					TArray<UEdGraphNode*> GraphNodes;
					SoundCueGraph->GetNodesOfClass<USoundCueGraphNode_Root>(RootNode);
					check(RootNode.Num() == 1);
					GraphNodes.Add(RootNode[0]);

					TArray<double> NodeTimes;
					NodeTimes.Add(FApp::GetCurrentTime()); // Time for the root node

					for (int32 i = 0; i < PathToWaveInstance.Num(); ++i)
					{
						const double ObservationTime = FApp::GetCurrentTime() + 1.f;

						NodeTimes.Add(ObservationTime);
						GraphNodes.Add(PathToWaveInstance[i]->GraphNode);
					}

					// Record the unique node->node pairings, keeping only the most recent times for each pairing
					for (int32 i = GraphNodes.Num() - 1; i >= 1; --i)
					{
						UEdGraphNode* CurNode = GraphNodes[i];
						double CurNodeTime = NodeTimes[i];
						UEdGraphNode* NextNode = GraphNodes[i-1];
						double NextNodeTime = NodeTimes[i-1];

						FExecPairingMap& Predecessors = PredecessorNodes.FindOrAdd(NextNode);

						// Update the timings if this is a more recent pairing
						FTimePair& Timings = Predecessors.FindOrAdd(CurNode);
						if (Timings.ThisExecTime < NextNodeTime)
						{
							Timings.PredExecTime = CurNodeTime;
							Timings.ThisExecTime = NextNodeTime;
						}
					}
				}
			}
		}
	}
}
Example #17
0
void FIOSTargetPlatform::GetTextureFormats( const UTexture* Texture, TArray<FName>& OutFormats ) const
{
	check(Texture);

	// we remap some of the defaults (with PVRTC and ASTC formats)
	static FName FormatRemap[] =
	{
		// original				PVRTC						ASTC
		FName(TEXT("DXT1")),	FName(TEXT("PVRTC2")),		FName(TEXT("ASTC_RGB")),
		FName(TEXT("DXT5")),	FName(TEXT("PVRTC4")),		FName(TEXT("ASTC_RGBA")),
		FName(TEXT("DXT5n")),	FName(TEXT("PVRTCN")),		FName(TEXT("ASTC_NormalAG")),
		FName(TEXT("BC5")),		FName(TEXT("PVRTCN")),		FName(TEXT("ASTC_NormalRG")),
		FName(TEXT("AutoDXT")),	FName(TEXT("AutoPVRTC")),	FName(TEXT("ASTC_RGBAuto")),
	};
	static FName NameBGRA8(TEXT("BGRA8"));
	static FName NamePOTERROR(TEXT("POTERROR"));

	FName TextureFormatName = NAME_None;

	// forward rendering only needs one channel for shadow maps
	if (Texture->LODGroup == TEXTUREGROUP_Shadowmap && !SupportsMetalMRT())
	{
		TextureFormatName = FName(TEXT("G8"));
	}

	// if we didn't assign anything specially, then use the defaults
	if (TextureFormatName == NAME_None)
	{
		TextureFormatName = GetDefaultTextureFormatName(Texture, EngineSettings, false);
	}

	// perform any remapping away from defaults
	bool bFoundRemap = false;
	bool bIncludePVRTC = CookPVRTC();
	bool bIncludeASTC = CookASTC();
	for (int32 RemapIndex = 0; RemapIndex < ARRAY_COUNT(FormatRemap); RemapIndex += 3)
	{
		if (TextureFormatName == FormatRemap[RemapIndex])
		{
			// we found a remapping
			bFoundRemap = true;
			// include the formats we want (use ASTC first so that it is preferred at runtime if they both exist and it's supported)
			if (bIncludeASTC)
			{
				OutFormats.AddUnique(FormatRemap[RemapIndex + 2]);
			}
			if (bIncludePVRTC)
			{
				// handle non-power of 2 textures
				if (!Texture->Source.IsPowerOfTwo())
				{
					// option 1: Uncompress, but users will get very large textures unknowningly
					// OutFormats.AddUnique(NameBGRA8);
					// option 2: Use an "error message" texture so they see it in game
					OutFormats.AddUnique(NamePOTERROR);
				}
				else
				{
					OutFormats.AddUnique(FormatRemap[RemapIndex + 1]);
				}
			}
		}
	}

	// if we didn't already remap above, add it now
	if (!bFoundRemap)
	{
		OutFormats.Add(TextureFormatName);
	}
}
TSharedRef<FAssetDragDropOp> FAssetDragDropOp::New(const FAssetData& InAssetData, UActorFactory* ActorFactory /*= NULL*/)
{
	TArray<FAssetData> AssetDataArray;
	AssetDataArray.Add(InAssetData);
	return New(AssetDataArray, ActorFactory);
}
Example #19
0
int32 FPoly::Triangulate( ABrush* InOwnerBrush, TArray<FPoly>& OutTriangles )
{
#if WITH_EDITOR

    if( Vertices.Num() < 3 )
    {
        return 0;
    }

    FClipSMPolygon Polygon(0);

    for( int32 v = 0 ; v < Vertices.Num() ; ++v )
    {
        FClipSMVertex vtx;
        vtx.Pos = Vertices[v];

        // Init other data so that VertsAreEqual won't compare garbage
        vtx.TangentX = FVector::ZeroVector;
        vtx.TangentY = FVector::ZeroVector;
        vtx.TangentZ = FVector::ZeroVector;
        vtx.Color = FColor(0, 0, 0);
        for( int32 uvIndex=0; uvIndex<ARRAY_COUNT(vtx.UVs); ++uvIndex )
        {
            vtx.UVs[uvIndex] = FVector2D(0.f, 0.f);
        }


        Polygon.Vertices.Add( vtx );
    }

    Polygon.FaceNormal = Normal;

    // Attempt to triangulate this polygon
    TArray<FClipSMTriangle> Triangles;
    if( TriangulatePoly( Triangles, Polygon ) )
    {
        // Create a set of FPolys from the triangles

        OutTriangles.Empty();
        FPoly TrianglePoly;

        for( int32 p = 0 ; p < Triangles.Num() ; ++p )
        {
            FClipSMTriangle* tri = &(Triangles[p]);

            TrianglePoly.Init();
            TrianglePoly.Base = tri->Vertices[0].Pos;

            TrianglePoly.Vertices.Add( tri->Vertices[0].Pos );
            TrianglePoly.Vertices.Add( tri->Vertices[1].Pos );
            TrianglePoly.Vertices.Add( tri->Vertices[2].Pos );

            if( TrianglePoly.Finalize( InOwnerBrush, 0 ) == 0 )
            {
                OutTriangles.Add( TrianglePoly );
            }
        }
    }

#endif

    return OutTriangles.Num();
}
void FSpriteAssetTypeActions::ExecuteCreateFlipbook(TArray<TWeakObjectPtr<UPaperSprite>> Objects)
{
	FAssetToolsModule& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools");
	FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
	TArray<UPaperSprite*> AllSprites;

	for (auto ObjIt = Objects.CreateConstIterator(); ObjIt; ++ObjIt)
	{
		UPaperSprite *Object = (*ObjIt).Get();
		if (Object && Object->IsValidLowLevel())
		{
			AllSprites.Add(Object);
		}
	}

	TMap<FString, TArray<UPaperSprite*> > SpriteFlipbookMap;
	FPaperFlipbookHelpers::ExtractFlipbooksFromSprites(/*out*/SpriteFlipbookMap, AllSprites, TArray<FString>());
	TArray<UObject*> ObjectsToSync;

	if (SpriteFlipbookMap.Num() > 0)
	{
		GWarn->BeginSlowTask(NSLOCTEXT("Paper2D", "Paper2D_CreateFlipbooks", "Creating flipbooks from selection"), true, true);

		int Progress = 0;
		int TotalProgress = SpriteFlipbookMap.Num();

		// Create the flipbook
		bool bOneFlipbookCreated = SpriteFlipbookMap.Num() == 1;
		for (auto Iter : SpriteFlipbookMap)
		{
			GWarn->UpdateProgress(Progress++, TotalProgress);

			const FString& FlipbookName = Iter.Key;
			TArray<UPaperSprite*> Sprites = Iter.Value;

			const FString SpritePathName = AllSprites[0]->GetOutermost()->GetPathName();
			const FString LongPackagePath = FPackageName::GetLongPackagePath(AllSprites[0]->GetOutermost()->GetPathName());

			const FString NewFlipBookDefaultPath = LongPackagePath + TEXT("/") + FlipbookName;
			FString DefaultSuffix;
			FString AssetName;
			FString PackageName;

			UPaperFlipbookFactory* FlipbookFactory = NewObject<UPaperFlipbookFactory>();
			for (int32 SpriteIndex = 0; SpriteIndex < Sprites.Num(); ++SpriteIndex)
			{
				UPaperSprite* Sprite = Sprites[SpriteIndex];
				FPaperFlipbookKeyFrame* KeyFrame = new (FlipbookFactory->KeyFrames) FPaperFlipbookKeyFrame();
				KeyFrame->Sprite = Sprite;
				KeyFrame->FrameRun = 1;
			}

			AssetToolsModule.Get().CreateUniqueAssetName(NewFlipBookDefaultPath, /*out*/ DefaultSuffix, /*out*/ PackageName, /*out*/ AssetName);
			const FString PackagePath = FPackageName::GetLongPackagePath(PackageName);
			if (bOneFlipbookCreated)
			{
				ContentBrowserModule.Get().CreateNewAsset(AssetName, PackagePath, UPaperFlipbook::StaticClass(), FlipbookFactory);
			}
			else
			{
				if (UObject* NewAsset = AssetToolsModule.Get().CreateAsset(AssetName, PackagePath, UPaperFlipbook::StaticClass(), FlipbookFactory))
				{
					ObjectsToSync.Add(NewAsset);
				}
			}

			if (GWarn->ReceivedUserCancel())
			{
				break;
			}
		}

		GWarn->EndSlowTask();

		if (ObjectsToSync.Num() > 0)
		{
			ContentBrowserModule.Get().SyncBrowserToAssets(ObjectsToSync);
		}
	}

}
/**
 * Fill an FSkeletalMeshImportData with data from an APEX Destructible Asset.
 *
 * @param ImportData - SkeletalMesh import data into which we are extracting information
 * @param ApexDestructibleAsset - the Apex Destructible Asset
 * @param bHaveAllNormals - if the function is successful, this value is true iff every submesh has a normal channel
 * @param bHaveAllTangents - if the function is successful, this value is true iff every submesh has a tangent channel
 *
 * @return Boolean true iff the operation is successful
 */
static bool FillSkelMeshImporterFromApexDestructibleAsset(FSkeletalMeshImportData& ImportData, const NxDestructibleAsset& ApexDestructibleAsset, bool& bHaveAllNormals, bool& bHaveAllTangents)
{
	// The APEX Destructible Asset contains an APEX Render Mesh Asset, get a pointer to this
	const physx::NxRenderMeshAsset* ApexRenderMesh = ApexDestructibleAsset.getRenderMeshAsset();
	if (ApexRenderMesh == NULL)
	{
		return false;
	}

	if (ApexDestructibleAsset.getChunkCount() != ApexRenderMesh->getPartCount())
	{
		UE_LOG(LogApexDestructibleAssetImport, Warning,TEXT("Chunk count does not match part count.  APEX Destructible Asset with chunk instancing not yet supported."));
		return false;
	}

	// Apex Render Mesh uses triangle lists only, currently.  No need to triangulate.

	// Assume there are no vertex colors
	ImportData.bHasVertexColors = false;

	// Different submeshes can have different UV counts.  Get the max.
	uint32 UniqueUVCount = 0;

	// Count vertices and triangles
	uint32 VertexCount = 0;
	uint32 TriangleCount = 0;

	for (uint32 SubmeshIndex = 0; SubmeshIndex < ApexRenderMesh->getSubmeshCount(); ++SubmeshIndex)
	{
		const NxRenderSubmesh& Submesh = ApexRenderMesh->getSubmesh(SubmeshIndex);
		const NxVertexBuffer& VB = Submesh.getVertexBuffer();
		const NxVertexFormat& VBFormat = VB.getFormat();

		// Count UV channels in this VB
		uint32 UVNum;
		for (UVNum = 0; UVNum < NxVertexFormat::MAX_UV_COUNT; ++UVNum)
		{
			const NxVertexFormat::BufferID BufferID = VBFormat.getSemanticID((NxRenderVertexSemantic::Enum)(NxRenderVertexSemantic::TEXCOORD0 + UVNum));
			if (VBFormat.getBufferIndexFromID(BufferID) < 0)
			{
				break;
			}
		}
		UniqueUVCount = FMath::Max<uint32>( UniqueUVCount, UVNum );

		// See if this VB has a color channel
		const NxVertexFormat::BufferID BufferID = VBFormat.getSemanticID(NxRenderVertexSemantic::COLOR);
		if (VBFormat.getBufferIndexFromID(BufferID) >= 0)
		{
			ImportData.bHasVertexColors = true;
		}

		// Count vertices
		VertexCount += VB.getVertexCount();

		// Count triangles
		uint32 IndexCount = 0;
		for (uint32 PartIndex = 0; PartIndex < ApexRenderMesh->getPartCount(); ++PartIndex)
		{
			IndexCount += Submesh.getIndexCount(PartIndex);
		}
		check(IndexCount%3 == 0);
		TriangleCount += IndexCount/3;
	}

	// One UV set is required but only import up to MAX_TEXCOORDS number of uv layers
	ImportData.NumTexCoords = FMath::Clamp<uint32>(UniqueUVCount, 1, MAX_TEXCOORDS);

	// Expand buffers in ImportData:
	ImportData.Points.AddUninitialized(VertexCount);
	ImportData.Influences.AddUninitialized(VertexCount);

	ImportData.Wedges.AddUninitialized(3*TriangleCount);
	uint32 WedgeIndex = 0;

	ImportData.Faces.AddUninitialized(TriangleCount);
	uint32 TriangleIndex = 0;

	uint32 VertexIndexBase = 0;

	// True until proven otherwise
	bHaveAllNormals = true;
	bHaveAllTangents = true;

	TArray<VMaterial> UniqueMaterials;

	for (int32 CurMatIdx=0; CurMatIdx < ImportData.Materials.Num(); ++CurMatIdx)
	{
		bool bHasMaterial = false;

		for (int32 CurUniqueMatIdx=0; CurUniqueMatIdx < UniqueMaterials.Num(); ++CurUniqueMatIdx)
		{
			if (ImportData.Materials[CurMatIdx].MaterialName == UniqueMaterials[CurUniqueMatIdx].MaterialName)
			{
				bHasMaterial = true;
				break;
			}
		}

		if (!bHasMaterial)
		{
			UniqueMaterials.Add(ImportData.Materials[CurMatIdx]);
		}
		
	}

	// APEX render meshes are organized by submesh (render elements)
	// Looping through submeshes first, can be done either way
	for (uint32 SubmeshIndex = 0; SubmeshIndex < ApexRenderMesh->getSubmeshCount(); ++SubmeshIndex)
	{
		// Submesh data
		const NxRenderSubmesh& Submesh = ApexRenderMesh->getSubmesh(SubmeshIndex);
		const NxVertexBuffer& VB = Submesh.getVertexBuffer();
		const NxVertexFormat& VBFormat = VB.getFormat();
		const physx::PxU32 SubmeshVertexCount = VB.getVertexCount();

		// Get VB data semantic indices:

		// Positions
		const PxI32 PositionBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::POSITION));
		if (!VB.getBufferData(&ImportData.Points[VertexIndexBase], physx::NxRenderDataFormat::FLOAT3, sizeof(FVector), PositionBufferIndex, 0, SubmeshVertexCount))
		{
			return false;	// Need a position buffer!
		}

#if INVERT_Y_AND_V
		for (uint32 VertexNum = 0; VertexNum < SubmeshVertexCount; ++VertexNum)
		{
			ImportData.Points[VertexIndexBase + VertexNum].Y *= -1.0f;
		}
#endif

		// Normals
		const PxI32 NormalBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::NORMAL));
		TArray<FVector> Normals;
		Normals.AddUninitialized(SubmeshVertexCount);
		const bool bHaveNormals = VB.getBufferData(Normals.GetTypedData(), physx::NxRenderDataFormat::FLOAT3, sizeof(FVector), NormalBufferIndex, 0, SubmeshVertexCount);
		if (!bHaveNormals)
		{
			FMemory::Memset(Normals.GetTypedData(), 0, SubmeshVertexCount*sizeof(FVector));	// Fill with zeros
		}

		// Tangents
		const PxI32 TangentBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::TANGENT));
		TArray<FVector> Tangents;
		Tangents.AddUninitialized(SubmeshVertexCount);
		const bool bHaveTangents = VB.getBufferData(Tangents.GetTypedData(), physx::NxRenderDataFormat::FLOAT3, sizeof(FVector), TangentBufferIndex, 0, SubmeshVertexCount);
		if (!bHaveTangents)
		{
			FMemory::Memset(Tangents.GetTypedData(), 0, SubmeshVertexCount*sizeof(FVector));	// Fill with zeros
		}

		// Update bHaveAllNormals and bHaveAllTangents
		bHaveAllNormals = bHaveAllNormals && bHaveNormals;
		bHaveAllTangents = bHaveAllTangents && bHaveTangents;

		// Binromals
		const PxI32 BinormalBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::BINORMAL));
		TArray<FVector> Binormals;
		Binormals.AddUninitialized(SubmeshVertexCount);
		bool bHaveBinormals = VB.getBufferData(Binormals.GetTypedData(), physx::NxRenderDataFormat::FLOAT3, sizeof(FVector), BinormalBufferIndex, 0, SubmeshVertexCount);
		if (!bHaveBinormals)
		{
			bHaveBinormals = bHaveNormals && bHaveTangents;
			for (uint32 i = 0; i < SubmeshVertexCount; ++i)
			{
				Binormals[i] = Normals[i]^Tangents[i];	// Build from normals and tangents.  If one of these doesn't exist we'll get (0,0,0)'s
			}
		}

		// Colors
		const PxI32 ColorBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID(NxRenderVertexSemantic::COLOR));
		TArray<FColor> Colors;
		Colors.AddUninitialized(SubmeshVertexCount);
		const bool bHaveColors = VB.getBufferData(Colors.GetTypedData(), physx::NxRenderDataFormat::B8G8R8A8, sizeof(FColor), ColorBufferIndex, 0, SubmeshVertexCount);
		if (!bHaveColors)
		{
			FMemory::Memset(Colors.GetTypedData(), 0xFF, SubmeshVertexCount*sizeof(FColor));	// Fill with 0xFF
		}

		// UVs
		TArray<FVector2D> UVs[NxVertexFormat::MAX_UV_COUNT];
		for (uint32 UVNum = 0; UVNum < ImportData.NumTexCoords; ++UVNum)
		{
			const PxI32 UVBufferIndex = VBFormat.getBufferIndexFromID(VBFormat.getSemanticID((NxRenderVertexSemantic::Enum)(NxRenderVertexSemantic::TEXCOORD0 + UVNum)));
			UVs[UVNum].AddUninitialized(SubmeshVertexCount);
			if (!VB.getBufferData(&UVs[UVNum][0].X, physx::NxRenderDataFormat::FLOAT2, sizeof(FVector2D), UVBufferIndex, 0, SubmeshVertexCount))
			{
				FMemory::Memset(&UVs[UVNum][0].X, 0, SubmeshVertexCount*sizeof(FVector2D));	// Fill with zeros
			}
		}

		// Bone indices will not be imported - they're implicitly the PartIndex

		// Each submesh is partitioned into parts.  Currently we're assuming a 1-1 correspondence between chunks and parts,
		// which means that instanced chunks are not supported.  However, we will not assume that the chunk and part ordering is the same.
		// Therefore, instead of looping through parts, we loop through chunks here, and get the part index.
		for (uint32 ChunkIndex = 0; ChunkIndex < ApexDestructibleAsset.getChunkCount(); ++ChunkIndex)
		{
			const physx::PxU32 PartIndex = ApexDestructibleAsset.getPartIndex(ChunkIndex);
			const physx::PxU32* PartIndexBuffer = Submesh.getIndexBuffer(PartIndex);
			const physx::PxU32* PartIndexBufferStop = PartIndexBuffer + Submesh.getIndexCount(PartIndex);
			while (PartIndexBuffer < PartIndexBufferStop)
			{
				physx::PxU32 SubmeshVertexIndex[3];
#if !INVERT_Y_AND_V
				SubmeshVertexIndex[2] = *PartIndexBuffer++;
				SubmeshVertexIndex[1] = *PartIndexBuffer++;
				SubmeshVertexIndex[0] = *PartIndexBuffer++;
#else
				SubmeshVertexIndex[0] = *PartIndexBuffer++;
				SubmeshVertexIndex[1] = *PartIndexBuffer++;
				SubmeshVertexIndex[2] = *PartIndexBuffer++;
#endif

				// Fill triangle
				VTriangle& Triangle = ImportData.Faces[TriangleIndex++];

				// set the face smoothing by default. It could be any number, but not zero
				Triangle.SmoothingGroups = 255; 

				// Material index
				int32 MatIdx = 0;

				check(ImportData.Materials.Num() > 0);
				
				if (SubmeshIndex < (uint32)ImportData.Materials.Num())
				{
					const FString& MaterialName = ImportData.Materials[SubmeshIndex].MaterialName;

					for (int32 UniqueMaterialIdx = 0; UniqueMaterialIdx < UniqueMaterials.Num(); ++UniqueMaterialIdx)
					{
						if (UniqueMaterials[UniqueMaterialIdx].MaterialName == MaterialName)
						{
							MatIdx = UniqueMaterialIdx;
							break;
						}
					}
				}
				
				Triangle.MatIndex = MatIdx;
				Triangle.AuxMatIndex = 0;

				// Per-vertex
				for (uint32 V = 0; V < 3; ++V)
				{
					// Tangent basis
					Triangle.TangentX[V] = Binormals[SubmeshVertexIndex[V]];
					Triangle.TangentY[V] = Tangents[SubmeshVertexIndex[V]];
					Triangle.TangentZ[V] = Normals[SubmeshVertexIndex[V]];
#if INVERT_Y_AND_V
					Triangle.TangentX[V].Y *= -1.0f;
					Triangle.TangentY[V].Y *= -1.0f;
					Triangle.TangentZ[V].Y *= -1.0f;
#endif

					// Wedges
					Triangle.WedgeIndex[V] = WedgeIndex;
					VVertex& Wedge = ImportData.Wedges[WedgeIndex++];
					Wedge.VertexIndex = VertexIndexBase + SubmeshVertexIndex[V];
					Wedge.MatIndex = Triangle.MatIndex;
					Wedge.Color = Colors[SubmeshVertexIndex[V]];
					Wedge.Reserved = 0;
					for (uint32 UVNum = 0; UVNum < ImportData.NumTexCoords; ++UVNum)
					{
						const FVector2D& UV = UVs[UVNum][SubmeshVertexIndex[V]];
#if !INVERT_Y_AND_V
						Wedge.UVs[UVNum] = UV;
#else
						Wedge.UVs[UVNum] = FVector2D(UV.X, 1.0f-UV.Y);
#endif
					}
				}
			}

			// Bone influences
			const physx::PxU32 PartVertexStart = Submesh.getFirstVertexIndex(PartIndex);
			const physx::PxU32 PartVertexStop = PartVertexStart + Submesh.getVertexCount(PartIndex);
			for (uint32 PartVertexIndex = PartVertexStart; PartVertexIndex < PartVertexStop; ++PartVertexIndex)
			{
				const physx::PxU32 VertexIndex = VertexIndexBase + PartVertexIndex;
				// Note, by using ChunkIndex instead of PartInedx we are effectively setting PartIndex = ChunkIndex, which is OK since we won't be supporting instancing with the SkeletalMesh.
				ImportData.Influences[VertexIndex].BoneIndex = ChunkIndex + 1;	// Adding 1, since the 0 bone will have no geometry from the Apex Destructible Asset.
				ImportData.Influences[VertexIndex].Weight = 1.0;
				ImportData.Influences[VertexIndex].VertexIndex = VertexIndex;
			}
		}

		VertexIndexBase += SubmeshVertexCount;
	}

	// Switch material arrays so we have a list with unique materials
	ImportData.Materials = UniqueMaterials;

	// Create mapping from import to raw- @TODO trivial at the moment, do we need this info for destructibles?
	ImportData.PointToRawMap.AddUninitialized(ImportData.Points.Num());
	for(int32 PointIdx=0; PointIdx<ImportData.PointToRawMap.Num(); PointIdx++)
	{
		ImportData.PointToRawMap[PointIdx] = PointIdx;
	}

	return true;
}
TSharedRef<SWidget> FLevelViewportLayout2x2::MakeViewportLayout(const FString& LayoutString)
{
	FString SpecificLayoutString = GetTypeSpecificLayoutString(LayoutString);

	TSharedPtr< SLevelViewport > ViewportWidgetTL;
	TSharedPtr< SLevelViewport > ViewportWidgetTR;
	TSharedPtr< SLevelViewport > ViewportWidgetBL;
	TSharedPtr< SLevelViewport > ViewportWidgetBR;

	FString TopLeftKey, BottomLeftKey, TopRightKey, BottomRightKey;
	TArray<FVector2D> SplitterPercentages;
	
	if (!SpecificLayoutString.IsEmpty())
	{
		// The Layout String only holds the unique ID of the Additional Layout Configs to use
		const FString& IniSection = FLayoutSaveRestore::GetAdditionalLayoutConfigIni();
		
		TopLeftKey = SpecificLayoutString + TEXT(".Viewport0");
		BottomLeftKey = SpecificLayoutString + TEXT(".Viewport1");
		TopRightKey = SpecificLayoutString + TEXT(".Viewport2");
		BottomRightKey = SpecificLayoutString + TEXT(".Viewport3");

		for (int32 i = 0; i < 4; ++i)
		{
			FString PercentageString;
			FVector2D NewPercentage = ViewportLayout2x2Defs::DefaultSplitterPercentages;
			if(GConfig->GetString(*IniSection, *(SpecificLayoutString + FString::Printf(TEXT(".Percentages%i"), i)), PercentageString, GEditorUserSettingsIni))
			{
				NewPercentage.InitFromString(PercentageString);
			}						
			SplitterPercentages.Add(NewPercentage);
		}
	}

	SplitterWidget = 
	SNew( SSplitter2x2 )
	.TopLeft()
	[
		SAssignNew( ViewportWidgetTL, SLevelViewport )
		.ViewportType( LVT_OrthoYZ )		// Side
		.ParentLayout( AsShared() )
		.ParentLevelEditor( ParentLevelEditor )
		.ConfigKey( TopLeftKey )
	]
	.BottomLeft()
	[
		SAssignNew( ViewportWidgetBL, SLevelViewport )
		.ViewportType( LVT_Perspective ) // Persp
		.Realtime( true )
		.ParentLayout( AsShared() )
		.ParentLevelEditor( ParentLevelEditor )
		.ConfigKey( BottomLeftKey )
	]
	.TopRight()
	[
		SAssignNew( ViewportWidgetTR, SLevelViewport )
		.ViewportType( LVT_OrthoXZ )		// Front
		.ParentLayout( AsShared() )
		.ParentLevelEditor( ParentLevelEditor )
		.ConfigKey( TopRightKey )
	]
	.BottomRight()
	[
		SAssignNew( ViewportWidgetBR, SLevelViewport )
		.ViewportType( LVT_OrthoXY )		// Top
		.ParentLayout( AsShared() )
		.ParentLevelEditor( ParentLevelEditor )
		.ConfigKey( BottomRightKey )
	];

	Viewports.Add( ViewportWidgetTL );
	Viewports.Add( ViewportWidgetBL );
	Viewports.Add( ViewportWidgetTR );
	Viewports.Add( ViewportWidgetBR );

	
	// Make newly-created perspective viewports active by default
	GCurrentLevelEditingViewportClient = &ViewportWidgetBL->GetLevelViewportClient();

	
	if (SplitterPercentages.Num() > 0)
	{
		SplitterWidget->SetSplitterPercentages(SplitterPercentages);
	}

	InitCommonLayoutFromString(SpecificLayoutString);

	return SplitterWidget.ToSharedRef();
}
void FStatsMemoryDumpCommand::ProcessMemoryOperations( const TMap<int64, FStatPacketArray>& CombinedHistory )
{
	// This is only example code, no fully implemented, may sometimes crash.
	// This code is not optimized. 
	double PreviousSeconds = FPlatformTime::Seconds();
	uint64 NumMemoryOperations = 0;

	// Generate frames
	TArray<int64> Frames;
	CombinedHistory.GenerateKeyArray( Frames );
	Frames.Sort();

	// Raw stats callstack for this stat packet array.
	TMap<FName, FStackState> StackStates;

	// All allocation ordered by the sequence tag.
	// There is an assumption that the sequence tag will not turn-around.
	//TMap<uint32, FAllocationInfo> SequenceAllocationMap;
	TArray<FAllocationInfo> SequenceAllocationArray;

	// Pass 1.
	// Read all stats messages, parse all memory operations and decode callstacks.
	const int64 FirstFrame = 0;
	PreviousSeconds -= NumSecondsBetweenLogs;
	for( int32 FrameIndex = 0; FrameIndex < Frames.Num(); ++FrameIndex )
	{
		{
			const double CurrentSeconds = FPlatformTime::Seconds();
			if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs )
			{
				UE_LOG( LogStats, Warning, TEXT( "Processing frame %i/%i" ), FrameIndex+1, Frames.Num() );
				PreviousSeconds = CurrentSeconds;
			}
		}

		const int64 TargetFrame = Frames[FrameIndex];
		const int64 Diff = TargetFrame - FirstFrame;
		const FStatPacketArray& Frame = CombinedHistory.FindChecked( TargetFrame );

		bool bAtLeastOnePacket = false;
		for( int32 PacketIndex = 0; PacketIndex < Frame.Packets.Num(); PacketIndex++ )
		{
			{
				const double CurrentSeconds = FPlatformTime::Seconds();
				if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs )
				{
					UE_LOG( LogStats, Log, TEXT( "Processing packet %i/%i" ), PacketIndex, Frame.Packets.Num() );
					PreviousSeconds = CurrentSeconds;
					bAtLeastOnePacket = true;
				}
			}

			const FStatPacket& StatPacket = *Frame.Packets[PacketIndex];
			const FName& ThreadFName = StatsThreadStats.Threads.FindChecked( StatPacket.ThreadId );

			FStackState* StackState = StackStates.Find( ThreadFName );
			if( !StackState )
			{
				StackState = &StackStates.Add( ThreadFName );
				StackState->Stack.Add( ThreadFName );
				StackState->Current = ThreadFName;
			}

			const FStatMessagesArray& Data = StatPacket.StatMessages;

			int32 LastPct = 0;
			const int32 NumDataElements = Data.Num();
			const int32 OnerPercent = FMath::Max( NumDataElements / 100, 1024 );
			bool bAtLeastOneMessage = false;
			for( int32 Index = 0; Index < NumDataElements; Index++ )
			{
				if( Index % OnerPercent )
				{
					const double CurrentSeconds = FPlatformTime::Seconds();
					if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs )
					{
						const int32 CurrentPct = int32( 100.0*(Index + 1) / NumDataElements );
						UE_LOG( LogStats, Log, TEXT( "Processing %3i%% (%i/%i) stat messages" ), CurrentPct, Index, NumDataElements );
						PreviousSeconds = CurrentSeconds;
						bAtLeastOneMessage = true;
					}
				}

				const FStatMessage& Item = Data[Index];

				const EStatOperation::Type Op = Item.NameAndInfo.GetField<EStatOperation>();
				const FName RawName = Item.NameAndInfo.GetRawName();

				if( Op == EStatOperation::CycleScopeStart || Op == EStatOperation::CycleScopeEnd || Op == EStatOperation::Memory )
				{
					if( Op == EStatOperation::CycleScopeStart )
					{
						StackState->Stack.Add( RawName );
						StackState->Current = RawName;
					}
					else if( Op == EStatOperation::Memory )
					{
						// Experimental code used only to test the implementation.
						// First memory operation is Alloc or Free
						const uint64 EncodedPtr = Item.GetValue_Ptr();
						const bool bIsAlloc = (EncodedPtr & (uint64)EMemoryOperation::Alloc) != 0;
						const bool bIsFree = (EncodedPtr & (uint64)EMemoryOperation::Free) != 0;
						const uint64 Ptr = EncodedPtr & ~(uint64)EMemoryOperation::Mask;
						if( bIsAlloc )
						{
							NumMemoryOperations++;
							// @see FStatsMallocProfilerProxy::TrackAlloc
							// After alloc ptr message there is always alloc size message and the sequence tag.
							Index++;
							const FStatMessage& AllocSizeMessage = Data[Index];
							const int64 AllocSize = AllocSizeMessage.GetValue_int64();

							// Read operation sequence tag.
							Index++;
							const FStatMessage& SequenceTagMessage = Data[Index];
							const uint32 SequenceTag = SequenceTagMessage.GetValue_int64();

							// Create a callstack.
							TArray<FName> StatsBasedCallstack;
							for( const auto& StackName : StackState->Stack )
							{
								StatsBasedCallstack.Add( StackName );
							}

							// Add a new allocation.
							SequenceAllocationArray.Add(
								FAllocationInfo(
								Ptr,
								AllocSize,
								StatsBasedCallstack,
								SequenceTag,
								EMemoryOperation::Alloc,
								StackState->bIsBrokenCallstack
								) );
						}
						else if( bIsFree )
						{
							NumMemoryOperations++;
							// Read operation sequence tag.
							Index++;
							const FStatMessage& SequenceTagMessage = Data[Index];
							const uint32 SequenceTag = SequenceTagMessage.GetValue_int64();

							// Create a callstack.
							/*
							TArray<FName> StatsBasedCallstack;
							for( const auto& RawName : StackState->Stack )
							{
								StatsBasedCallstack.Add( RawName );
							}
							*/

							// Add a new free.
							SequenceAllocationArray.Add(
								FAllocationInfo(
								Ptr,		
								0,
								TArray<FName>()/*StatsBasedCallstack*/,					
								SequenceTag,
								EMemoryOperation::Free,
								StackState->bIsBrokenCallstack
								) );
						}
						else
						{
							UE_LOG( LogStats, Warning, TEXT( "Pointer from a memory operation is invalid" ) );
						}
					}
					else if( Op == EStatOperation::CycleScopeEnd )
					{
						if( StackState->Stack.Num() > 1 )
						{
							const FName ScopeStart = StackState->Stack.Pop();
							const FName ScopeEnd = Item.NameAndInfo.GetRawName();

							check( ScopeStart == ScopeEnd );

							StackState->Current = StackState->Stack.Last();

							// The stack should be ok, but it may be partially broken.
							// This will happen if memory profiling starts in the middle of executing a background thread.
							StackState->bIsBrokenCallstack = false;
						}
						else
						{
							const FName ShortName = Item.NameAndInfo.GetShortName();

							UE_LOG( LogStats, Warning, TEXT( "Broken cycle scope end %s/%s, current %s" ),
									*ThreadFName.ToString(),
									*ShortName.ToString(),
									*StackState->Current.ToString() );

							// The stack is completely broken, only has the thread name and the last cycle scope.
							// Rollback to the thread node.
							StackState->bIsBrokenCallstack = true;
							StackState->Stack.Empty();
							StackState->Stack.Add( ThreadFName );
							StackState->Current = ThreadFName;
						}
					}
				}
			}
			if( bAtLeastOneMessage )
			{
				PreviousSeconds -= NumSecondsBetweenLogs;
			}
		}
		if( bAtLeastOnePacket )
		{
			PreviousSeconds -= NumSecondsBetweenLogs;
		}
	}

	UE_LOG( LogStats, Warning, TEXT( "NumMemoryOperations:   %llu" ), NumMemoryOperations );
	UE_LOG( LogStats, Warning, TEXT( "SequenceAllocationNum: %i" ), SequenceAllocationArray.Num() );

	// Pass 2.
	/*
	TMap<uint32,FAllocationInfo> UniqueSeq;
	TMultiMap<uint32,FAllocationInfo> OriginalAllocs;
	TMultiMap<uint32,FAllocationInfo> BrokenAllocs;
	for( const FAllocationInfo& Alloc : SequenceAllocationArray )
	{
		const FAllocationInfo* Found = UniqueSeq.Find(Alloc.SequenceTag);
		if( !Found )
		{
			UniqueSeq.Add(Alloc.SequenceTag,Alloc);
		}
		else
		{
			OriginalAllocs.Add(Alloc.SequenceTag, *Found);
			BrokenAllocs.Add(Alloc.SequenceTag, Alloc);
		}
	}
	*/

	// Sort all memory operation by the sequence tag, iterate through all operation and generate memory usage.
	SequenceAllocationArray.Sort( TLess<FAllocationInfo>() );

	// Alive allocations.
	TMap<uint64, FAllocationInfo> AllocationMap;
	TMultiMap<uint64, FAllocationInfo> FreeWithoutAllocMap;
	TMultiMap<uint64, FAllocationInfo> DuplicatedAllocMap;
	int32 NumDuplicatedMemoryOperations = 0;
	int32 NumFWAMemoryOperations = 0; // FreeWithoutAlloc

	UE_LOG( LogStats, Warning, TEXT( "Generating memory operations map" ) );
	const int32 NumSequenceAllocations = SequenceAllocationArray.Num();
	const int32 OnePercent = FMath::Max( NumSequenceAllocations / 100, 1024 );
	for( int32 Index = 0; Index < NumSequenceAllocations; Index++ )
	{
		if( Index % OnePercent )
		{
			const double CurrentSeconds = FPlatformTime::Seconds();
			if( CurrentSeconds > PreviousSeconds + NumSecondsBetweenLogs )
			{
				const int32 CurrentPct = int32( 100.0*(Index + 1) / NumSequenceAllocations );
				UE_LOG( LogStats, Log, TEXT( "Processing allocations %3i%% (%10i/%10i)" ), CurrentPct, Index + 1, NumSequenceAllocations );
				PreviousSeconds = CurrentSeconds;
			}
		}

		const FAllocationInfo& Alloc = SequenceAllocationArray[Index];
		const EMemoryOperation MemOp = Alloc.Op;
		const uint64 Ptr = Alloc.Ptr;
		const int64 Size = Alloc.Size;
		const uint32 SequenceTag = Alloc.SequenceTag;

		if( MemOp == EMemoryOperation::Alloc )
		{
			const FAllocationInfo* Found = AllocationMap.Find( Ptr );

			if( !Found )
			{
				AllocationMap.Add( Ptr, Alloc );
			}
			else
			{
				const FAllocationInfo* FoundAndFreed = FreeWithoutAllocMap.Find( Found->Ptr );
				const FAllocationInfo* FoundAndAllocated = FreeWithoutAllocMap.Find( Alloc.Ptr );

#if	_DEBUG
				if( FoundAndFreed )
				{
					const FString FoundAndFreedCallstack = GetCallstack( FoundAndFreed->EncodedCallstack );
				}

				if( FoundAndAllocated )
				{
					const FString FoundAndAllocatedCallstack = GetCallstack( FoundAndAllocated->EncodedCallstack );
				}

				NumDuplicatedMemoryOperations++;


				const FString FoundCallstack = GetCallstack( Found->EncodedCallstack );
				const FString AllocCallstack = GetCallstack( Alloc.EncodedCallstack );
#endif // _DEBUG

				// Replace pointer.
				AllocationMap.Add( Ptr, Alloc );
				// Store the old pointer.
				DuplicatedAllocMap.Add( Ptr, *Found );
			}
		}
		else if( MemOp == EMemoryOperation::Free )
		{
			const FAllocationInfo* Found = AllocationMap.Find( Ptr );
			if( Found )
			{
				const bool bIsValid = Alloc.SequenceTag > Found->SequenceTag;
				if( !bIsValid )
				{
					UE_LOG( LogStats, Warning, TEXT( "InvalidFree Ptr: %llu, Seq: %i/%i" ), Ptr, SequenceTag, Found->SequenceTag );
				}
				AllocationMap.Remove( Ptr );
			}
			else
			{
				FreeWithoutAllocMap.Add( Ptr, Alloc );
				NumFWAMemoryOperations++;
			}
		}
	}

	UE_LOG( LogStats, Warning, TEXT( "NumDuplicatedMemoryOperations: %i" ), NumDuplicatedMemoryOperations );
	UE_LOG( LogStats, Warning, TEXT( "NumFWAMemoryOperations:        %i" ), NumFWAMemoryOperations );

	// Dump problematic allocations
	DuplicatedAllocMap.ValueSort( FAllocationInfoGreater() );
	//FreeWithoutAllocMap

	uint64 TotalDuplicatedMemory = 0;
	for( const auto& It : DuplicatedAllocMap )
	{
		const FAllocationInfo& Alloc = It.Value;
		TotalDuplicatedMemory += Alloc.Size;
	}

	UE_LOG( LogStats, Warning, TEXT( "Dumping duplicated alloc map" ) );
	const float MaxPctDisplayed = 0.80f;
	uint64 DisplayedSoFar = 0;
	for( const auto& It : DuplicatedAllocMap )
	{
		const FAllocationInfo& Alloc = It.Value;
		const FString AllocCallstack = GetCallstack( Alloc.EncodedCallstack );
		UE_LOG( LogStats, Log, TEXT( "%lli (%.2f MB) %s" ), Alloc.Size, Alloc.Size / 1024.0f / 1024.0f, *AllocCallstack );

		DisplayedSoFar += Alloc.Size;

		const float CurrentPct = (float)DisplayedSoFar / (float)TotalDuplicatedMemory;
		if( CurrentPct > MaxPctDisplayed )
		{
			break;
		}
	}

	GenerateMemoryUsageReport( AllocationMap );
}
Example #24
0
bool FPerfCounters::Tick(float DeltaTime)
{
	// if we didn't get a socket, don't tick
	if (Socket == nullptr)
	{
		return false;
	}

	// accept any connections
	static const FString PerfCounterRequest = TEXT("FPerfCounters Request");
	FSocket* IncomingConnection = Socket->Accept(PerfCounterRequest);
	if (IncomingConnection)
	{
		if (0)
		{
			TSharedRef<FInternetAddr> FromAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
			IncomingConnection->GetPeerAddress(*FromAddr);
			UE_LOG(LogPerfCounters, Log, TEXT("New connection from %s"), *FromAddr->ToString(true));
		}

		// make sure this is non-blocking
		IncomingConnection->SetNonBlocking(true);

		new (Connections) FPerfConnection(IncomingConnection);
	}

	TArray<FPerfConnection> ConnectionsToClose;
	for (FPerfConnection& Connection : Connections)
	{
		FSocket* ExistingSocket = Connection.Connection;
		if (ExistingSocket && ExistingSocket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::Zero()))
		{
			// read any data that's ready
			// NOTE: this is not a full HTTP implementation, just enough to be usable by curl
			uint8 Buffer[2 * 1024] = { 0 };
			int32 DataLen = 0;
			if (ExistingSocket->Recv(Buffer, sizeof(Buffer) - 1, DataLen, ESocketReceiveFlags::None))
			{
				FResponse Response;
				if (ProcessRequest(Buffer, DataLen, Response))
				{
					if (SendAsUtf8(ExistingSocket, Response.Header))
					{
						if (!SendAsUtf8(ExistingSocket, Response.Body))
						{
							UE_LOG(LogPerfCounters, Warning, TEXT("Unable to send full HTTP response body"));
						}
					}
					else
					{
						UE_LOG(LogPerfCounters, Warning, TEXT("Unable to send HTTP response header: %s"), *Response.Header);
					}
				}
			}
			else
			{
				UE_LOG(LogPerfCounters, Warning, TEXT("Unable to immediately receive request header"));
			}

			ConnectionsToClose.Add(Connection);
		}
		else if (Connection.ElapsedTime > 5.0f)
		{
			ConnectionsToClose.Add(Connection);
		}

		Connection.ElapsedTime += DeltaTime;
	}

	for (FPerfConnection& Connection : ConnectionsToClose)
	{
		Connections.RemoveSingleSwap(Connection);

		FSocket* ClosingSocket = Connection.Connection;
		if (ClosingSocket)
		{
			// close the socket (whether we processed or not)
			ClosingSocket->Close();
			ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->DestroySocket(ClosingSocket);
			
			if (0)
			{
				UE_LOG(LogPerfCounters, Log, TEXT("Closed connection."));
			}
		}
	}

	// keep ticking
	return true;
}
Example #25
0
UObject* UFactory::StaticImportObject
(
	UClass*				Class,
	UObject*			InOuter,
	FName				Name,
	EObjectFlags		Flags,
	bool&				bOutOperationCanceled,
	const TCHAR*		Filename,
	UObject*			Context,
	UFactory*			InFactory,
	const TCHAR*		Parms,
	FFeedbackContext*	Warn,
	int32				MaxImportFileSize
)
{
	check(Class);

	CurrentFilename = Filename;

	// Make list of all applicable factories.
	TArray<UFactory*> Factories;
	if( InFactory )
	{
		// Use just the specified factory.
		if (ensureMsgf( !InFactory->SupportedClass || Class->IsChildOf(InFactory->SupportedClass), 
			TEXT("Factory is (%s), SupportedClass is (%s) and Class name is (%s)"), *InFactory->GetName(), (InFactory->SupportedClass)? *InFactory->SupportedClass->GetName() : TEXT("None"), *Class->GetName() ))
		{
			Factories.Add( InFactory );
		}
	}
	else
	{
		auto TransientPackage = GetTransientPackage();
		// Try all automatic factories, sorted by priority.
		for( TObjectIterator<UClass> It; It; ++It )
		{
			if( It->IsChildOf( UFactory::StaticClass() ) )
			{
				UFactory* Default = It->GetDefaultObject<UFactory>();
				if (Class->IsChildOf(Default->SupportedClass) && Default->ImportPriority >= 0)
				{
					Factories.Add(NewObject<UFactory>(TransientPackage, *It));
				}
			}
		}

		Factories.Sort([](const UFactory& A, const UFactory& B) -> bool
		{
			// First sort so that higher priorities are earlier in the list
			if( A.ImportPriority > B.ImportPriority )
			{
				return true;
			}
			else if( A.ImportPriority < B.ImportPriority )
			{
				return false;
			}

			// Then sort so that factories that only create new assets are tried after those that actually import the file data (when they have an equivalent priority)
			const bool bFactoryAImportsFiles = !A.CanCreateNew();
			const bool bFactoryBImportsFiles = !B.CanCreateNew();
			if( bFactoryAImportsFiles && !bFactoryBImportsFiles )
			{
				return true;
			}

			return false;
		});
	}

	bool bLoadedFile = false;

	// Try each factory in turn.
	for( int32 i=0; i<Factories.Num(); i++ )
	{
		UFactory* Factory = Factories[i];
		UObject* Result = NULL;
		if( Factory->CanCreateNew() )
		{
			UE_LOG(LogFactory, Log,  TEXT("FactoryCreateNew: %s with %s (%i %i %s)"), *Class->GetName(), *Factories[i]->GetClass()->GetName(), Factory->bCreateNew, Factory->bText, Filename );
			Factory->ParseParms( Parms );
			Result = Factory->FactoryCreateNew( Class, InOuter, Name, Flags, NULL, Warn );
		}
		else if( FCString::Stricmp(Filename,TEXT(""))!=0 )
		{
			if( Factory->bText )
			{
				//UE_LOG(LogFactory, Log,  TEXT("FactoryCreateText: %s with %s (%i %i %s)"), *Class->GetName(), *Factories(i)->GetClass()->GetName(), Factory->bCreateNew, Factory->bText, Filename );
				FString Data;
				if( FFileHelper::LoadFileToString( Data, Filename ) )
				{
					bLoadedFile = true;
					const TCHAR* Ptr = *Data;
					Factory->ParseParms( Parms );
					Result = Factory->FactoryCreateText( Class, InOuter, Name, Flags, NULL, *FPaths::GetExtension(Filename), Ptr, Ptr+Data.Len(), Warn );
				}
			}
			else
			{
				UE_LOG(LogFactory, Log,  TEXT("FactoryCreateBinary: %s with %s (%i %i %s)"), *Class->GetName(), *Factories[i]->GetClass()->GetName(), Factory->bCreateNew, Factory->bText, Filename );
				
				// Sanity check the file size of the impending import and prompt the user if they wish to continue if the file size is very large
				const int32 FileSize = IFileManager::Get().FileSize( Filename );
				bool bValidFileSize = true;

				if ( FileSize == INDEX_NONE )
				{
					UE_LOG(LogFactory, Error,TEXT("File '%s' does not exist"), Filename );
					bValidFileSize = false;
				}

				TArray<uint8> Data;
				if( bValidFileSize && FFileHelper::LoadFileToArray( Data, Filename ) )
				{
					bLoadedFile = true;
					Data.Add( 0 );
					const uint8* Ptr = &Data[ 0 ];
					Factory->ParseParms( Parms );
					Result = Factory->FactoryCreateBinary( Class, InOuter, Name, Flags, NULL, *FPaths::GetExtension(Filename), Ptr, Ptr+Data.Num()-1, Warn, bOutOperationCanceled );
				}
			}
		}
		if( Result )
		{
			// prevent UTextureCube created from UTextureFactory			check(Result->IsA(Class));
			Result->MarkPackageDirty();
			ULevel::LevelDirtiedEvent.Broadcast();
			Result->PostEditChange();

			CurrentFilename = TEXT("");
			return Result;
		}
	}

	if ( !bLoadedFile && !bOutOperationCanceled )
	{
		Warn->Logf( *FText::Format( NSLOCTEXT( "UnrealEd", "NoFindImport", "Can't find file '{0}' for import" ), FText::FromString( FString(Filename) ) ).ToString() );
	}

	CurrentFilename = TEXT("");

	return NULL;
}
Example #26
0
bool FCollection::CheckoutCollection(FText& OutError)
{
	if ( !ensure(SourceFilename.Len()) )
	{
		OutError = LOCTEXT("Error_Internal", "There was an internal error.");
		return false;
	}

	ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider();
	if ( !ISourceControlModule::Get().IsEnabled() )
	{
		OutError = LOCTEXT("Error_SCCDisabled", "Source control is not enabled. Enable source control in the preferences menu.");
		return false;
	}

	if ( !SourceControlProvider.IsAvailable() )
	{
		OutError = LOCTEXT("Error_SCCNotAvailable", "Source control is currently not available. Check your connection and try again.");
		return false;
	}

	FString AbsoluteFilename = FPaths::ConvertRelativePathToFull(SourceFilename);
	FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate);

	bool bSuccessfullyCheckedOut = false;
	TArray<FString> FilesToBeCheckedOut;
	FilesToBeCheckedOut.Add(AbsoluteFilename);

	if (SourceControlState.IsValid() && SourceControlState->IsDeleted())
	{
		// Revert our delete
		if ( !RevertCollection(OutError) )
		{
			return false;
		}

		// Make sure we get a fresh state from the server
		SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate);
	}

	// If not at the head revision, sync up
	if (SourceControlState.IsValid() && !SourceControlState->IsCurrent())
	{
		if ( !SourceControlProvider.Execute(ISourceControlOperation::Create<FSync>(), FilesToBeCheckedOut) )
		{
			// Could not sync up with the head revision
			OutError = LOCTEXT("Error_SCCSync", "Failed to sync collection to the head revision.");
			return false;
		}

		// Check to see if the file exists at the head revision
		if ( IFileManager::Get().FileSize(*SourceFilename) < 0 )
		{
			// File was deleted at head...
			// Just add our changes to an empty list so we can mark it for add
			FCollection NewCollection;
			MergeWithCollection(NewCollection);
		}
		else
		{
			// File found! Load it and merge with our local changes
			FCollection NewCollection;
			if ( !NewCollection.LoadFromFile(SourceFilename, false) )
			{
				// Failed to load the head revision file so it isn't safe to delete it
				OutError = LOCTEXT("Error_SCCBadHead", "Failed to load the collection at the head revision.");
				return false;
			}

			// Loaded the head revision, now merge up so the files are in a consistent state
			MergeWithCollection(NewCollection);
		}

		// Make sure we get a fresh state from the server
		SourceControlState = SourceControlProvider.GetState(AbsoluteFilename, EStateCacheUsage::ForceUpdate);
	}

	if(SourceControlState.IsValid())
	{
		if(SourceControlState->IsAdded() || SourceControlState->IsCheckedOut())
		{
			// Already checked out or opened for add
			bSuccessfullyCheckedOut = true;
		}
		else if(SourceControlState->CanCheckout())
		{
			// In depot and needs to be checked out
			bSuccessfullyCheckedOut = (SourceControlProvider.Execute(ISourceControlOperation::Create<FCheckOut>(), FilesToBeCheckedOut) == ECommandResult::Succeeded);
			if (!bSuccessfullyCheckedOut)
			{
				OutError = LOCTEXT("Error_SCCCheckout", "Failed to check out collection.");
			}
		}
		else if(!SourceControlState->IsSourceControlled())
		{
			// Not yet in the depot. Add it.
			bSuccessfullyCheckedOut = (SourceControlProvider.Execute(ISourceControlOperation::Create<FMarkForAdd>(), FilesToBeCheckedOut) == ECommandResult::Succeeded);
			if (!bSuccessfullyCheckedOut)
			{
				OutError = LOCTEXT("Error_SCCAdd", "Failed to add new collection to source control.");
			}
		}
		else if(!SourceControlState->IsCurrent())
		{
			OutError = LOCTEXT("Error_SCCNotCurrent", "Collection is not at head revision after sync.");
		}
		else if(SourceControlState->IsCheckedOutOther())
		{
			OutError = LOCTEXT("Error_SCCCheckedOutOther", "Collection is checked out by another user.");
		}
		else
		{
			OutError = LOCTEXT("Error_SCCUnknown", "Could not determine source control state.");
		}
	}
	else
	{
		OutError = LOCTEXT("Error_SCCInvalid", "Source control state is invalid.");
	}

	return bSuccessfullyCheckedOut;
}
Example #27
0
int32 UGridMesher::buildNewMesh(const TArray<float>& vertexRadii, const TArray<FColor>& vertexColors,
	const TArray<FVector>& vertexNormals, UMaterialInterface* newMeshMaterial,
	int32 meshToRebuild /*= -1 if we're to build a new mesh*/)
{
	/*void CreateMeshSection(int32 SectionIndex, const TArray<FVector>& Vertices,
	const TArray<int32>& Triangles, const TArray<FVector>& Normals,
	const TArray<FVector2D>& UV0, const TArray<FColor>& VertexColors,
	const TArray<FProcMeshTangent>& Tangents, bool bCreateCollision);*/
	bool calcNormals = vertexNormals.Num() == 0;
	TArray<FVector> Vertices;
	TArray<int32> Triangles;
	TArray<FVector> Normals;
	TArray<FVector2D> UV0;
	TArray<FProcMeshTangent> Tangents;

	if (debugTextOutArray.Num() != 0)
	{
		for (USceneComponent* debugText : debugTextOutArray)
		{
			debugText->DetachFromParent();
			debugText->DestroyComponent();
		}
	}
	debugLineOut->Flush();


	for (const FRectGridLocation& gridLoc : myGrid->gridLocationsM)
	{
		FVector tilePos = myGrid->getNodeLocationOnSphere(gridLoc);
		Vertices.Add(tilePos * vertexRadii[gridLoc.tileIndex]);
		if (calcNormals)
		{
			FVector vertexNormal = calculateVertexNormal(gridLoc, vertexRadii);
			Normals.Add(vertexNormal);
		}
		else
		{
			Normals.Add(vertexNormals[gridLoc.tileIndex]);
		}
		if (renderNodes)
		{
			debugLineOut->DrawPoint(tilePos * baseMeshRadius, FLinearColor::Blue, 8, 2);
		}
		if (renderNodeIndexes)
		{
			UTextRenderComponent* nodeTextId = NewObject<UTextRenderComponent>(this);
			nodeTextId->RegisterComponent();
			nodeTextId->SetRelativeLocation(tilePos * (baseMeshRadius*1.01));
			nodeTextId->SetText(FText::FromString(FString::FromInt(gridLoc.tileIndex)));
			nodeTextId->SetTextRenderColor(FColor::Red);
			nodeTextId->SetWorldSize(baseMeshRadius / 50.0f);
			FVector xAxis(1.0, 0.0, 0.0);
			FRotator textRotator = Normals[gridLoc.tileIndex].Rotation() - xAxis.Rotation();
			nodeTextId->AddRelativeRotation(textRotator);
			nodeTextId->AttachTo(this);
			debugTextOutArray.Add(nodeTextId);
		}
	}

	for (int32 uLoc = 0; uLoc < myGrid->rectilinearGridM.Num(); ++uLoc)
	{
		for (int32 vLoc = 0; vLoc <= myGrid->gridFrequency * 2; ++vLoc)
		{
			if (vLoc != myGrid->gridFrequency * 2)
			{
				//upperTriangle
				int32 vertU = uLoc;
				int32 vertV = vLoc;
				Triangles.Add(myGrid->rectilinearGridM[vertU][vertV]);
				++vertV;
				Triangles.Add(myGrid->rectilinearGridM[vertU][vertV]);
				--vertV;
				myGrid->decrementU(vertU, vertV);
				Triangles.Add(myGrid->rectilinearGridM[vertU][vertV]);
			}
			if (vLoc != 0)
			{
				//lowerTriangle
				int32 vertU = uLoc;
				int32 vertV = vLoc;
				Triangles.Add(myGrid->rectilinearGridM[vertU][vertV]);
				myGrid->decrementU(vertU, vertV);
				Triangles.Add(myGrid->rectilinearGridM[vertU][vertV]);
				--vertV;
				Triangles.Add(myGrid->rectilinearGridM[vertU][vertV]);
			}
		}
	}

	int32 targetMeshNum = numMeshes;
	if (meshToRebuild != -1)
	{
		targetMeshNum = meshToRebuild;
	}
	CreateMeshSection(targetMeshNum, Vertices, Triangles, Normals, UV0, vertexColors, Tangents, false);
	SetMaterial(targetMeshNum, newMeshMaterial);
	if (meshToRebuild != -1)
	{
		++numMeshes;
	}
	return targetMeshNum;
}
Example #28
0
int32 UObjectLibrary::LoadBlueprintAssetDataFromPaths(const TArray<FString>& Paths, bool bForceSynchronousScan)
{
	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
	IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();

	if (!bHasBlueprintClasses)
	{
		return 0;
	}

#if WITH_EDITOR
	// Cooked data has the asset data already set up
	const bool bShouldDoSynchronousScan = !bIsGlobalAsyncScanEnvironment || bForceSynchronousScan;
	if ( bShouldDoSynchronousScan )
	{
		AssetRegistry.ScanPathsSynchronous(Paths);
	}
	else
	{
		if ( AssetRegistry.IsLoadingAssets() )
		{
			// Keep track of the paths we asked for so once assets are discovered we will refresh the list
			for (const FString& Path : Paths)
			{
				DeferredAssetDataPaths.AddUnique(Path);
			}
		}
	}
#endif

	FARFilter ARFilter;
	ARFilter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
	
	for (int PathIndex = 0; PathIndex < Paths.Num(); PathIndex++)
	{
		ARFilter.PackagePaths.Add(FName(*Paths[PathIndex]));
	}
	
	ARFilter.bRecursivePaths = true;
	ARFilter.bIncludeOnlyOnDiskAssets = true;

	/* GetDerivedClassNames doesn't work yet
	if ( ObjectBaseClass )
	{
		TArray<FName> SearchClassNames;
		TSet<FName> ExcludedClassNames;
		TSet<FName> DerivedClassNames;
		SearchClassNames.Add(ObjectBaseClass->GetFName());
		AssetRegistry.GetDerivedClassNames(SearchClassNames, ExcludedClassNames, DerivedClassNames);

		const FName GeneratedClassName = FName(TEXT("GeneratedClass"));
		for ( auto DerivedClassIt = DerivedClassNames.CreateConstIterator(); DerivedClassIt; ++DerivedClassIt )
		{
			ARFilter.TagsAndValues.Add(GeneratedClassName, (*DerivedClassIt).ToString());
		}
	}*/

	AssetDataList.Empty();
	AssetRegistry.GetAssets(ARFilter, AssetDataList);

	// Filter out any blueprints found whose parent class is not derived from ObjectBaseClass
	if (ObjectBaseClass)
	{
		TSet<FName> DerivedClassNames;
		TArray<FName> ClassNames;
		ClassNames.Add(ObjectBaseClass->GetFName());
		AssetRegistry.GetDerivedClassNames(ClassNames, TSet<FName>(), DerivedClassNames);

		for(int32 AssetIdx=AssetDataList.Num() - 1; AssetIdx >= 0; --AssetIdx)
		{
			FAssetData& Data = AssetDataList[AssetIdx];

			bool bShouldRemove = true;
			const FString* ParentClassFromData = Data.TagsAndValues.Find("ParentClass");
			if (ParentClassFromData && !ParentClassFromData->IsEmpty())
			{
				const FString ClassObjectPath = FPackageName::ExportTextPathToObjectPath(*ParentClassFromData);
				const FString ClassName = FPackageName::ObjectPathToObjectName(ClassObjectPath);
				if (DerivedClassNames.Contains(FName(*ClassName)))
				{
					// This asset is derived from ObjectBaseClass. Keep it.
					bShouldRemove = false;
				}
			}

			if ( bShouldRemove )
			{
				AssetDataList.RemoveAt(AssetIdx);
			}
		}
	}

	return AssetDataList.Num();
}
Example #29
0
void USoundCue::PostLoad()
{
	Super::PostLoad();

	// Game doesn't care if there are NULL graph nodes
#if WITH_EDITOR
	if (GIsEditor )
	{
		if (SoundCueGraph)
		{
			// Deal with SoundNode types being removed - iterate in reverse as nodes may be removed
			for (int32 idx = SoundCueGraph->Nodes.Num() - 1; idx >= 0; --idx)
			{
				USoundCueGraphNode* Node = Cast<USoundCueGraphNode>(SoundCueGraph->Nodes[idx]);

				if (Node && Node->SoundNode == NULL)
				{
					FBlueprintEditorUtils::RemoveNode(NULL, Node, true);
				}
			}
		}
		else
		{
			// we should have a soundcuegraph unless we are contained in a package which is missing editor only data
			check( GetOutermost()->PackageFlags & PKG_FilterEditorOnly );
		}

		// Always load all sound waves in the editor
		for (USoundNode* SoundNode : AllNodes)
		{
			if (USoundNodeAssetReferencer* AssetReferencerNode = Cast<USoundNodeAssetReferencer>(SoundNode))
			{
				AssetReferencerNode->LoadAsset();
			}
		}
	}
	else
#endif
	{
		TArray<USoundNode*> NodesToEvaluate;
		NodesToEvaluate.Push(FirstNode);

		while (NodesToEvaluate.Num() > 0)
		{
			if (USoundNode* SoundNode = NodesToEvaluate.Pop(false))
			{
				if (USoundNodeAssetReferencer* AssetReferencerNode = Cast<USoundNodeAssetReferencer>(SoundNode))
				{
					AssetReferencerNode->LoadAsset();
				}
				else if (USoundNodeQualityLevel* QualityLevelNode = Cast<USoundNodeQualityLevel>(SoundNode))
				{
					// Only pick the node connected for current quality, currently don't support changing audio quality on the fly
					static const int32 CachedQualityLevel = GEngine->GetGameUserSettings()->GetAudioQualityLevel();
					if (CachedQualityLevel < QualityLevelNode->ChildNodes.Num())
					{
						NodesToEvaluate.Add(QualityLevelNode->ChildNodes[CachedQualityLevel]);
					}
				}
				else
				{
					NodesToEvaluate.Append(SoundNode->ChildNodes);
				}
			}
		}
	}
}
TArray< TSharedRef<FTokenizedMessage> > FCompilerResultsLog::ParseCompilerLogDump(const FString& LogDump)
{
	TArray< TSharedRef<FTokenizedMessage> > Messages;

	TArray< FString > MessageLines;
	LogDump.ParseIntoArray(MessageLines, TEXT("\n"), false);

	// delete any trailing empty lines
	for (int32 i = MessageLines.Num()-1; i >= 0; --i)
	{
		if (!MessageLines[i].IsEmpty())
		{
			if (i < MessageLines.Num() - 1)
			{
				MessageLines.RemoveAt(i+1, MessageLines.Num() - (i+1));
			}
			break;
		}
	}

	for (int32 i = 0; i < MessageLines.Num(); ++i)
	{
		FString Line = MessageLines[i];
		if (Line.EndsWith(TEXT("\r")))
		{
			Line = Line.LeftChop(1);
		}
		Line = Line.ConvertTabsToSpaces(4).TrimTrailing();

		// handle output line error message if applicable
		// @todo Handle case where there are parenthesis in path names
		// @todo Handle errors reported by Clang
		FString LeftStr, RightStr;
		FString FullPath, LineNumberString;
		if (Line.Split(TEXT(")"), &LeftStr, &RightStr, ESearchCase::CaseSensitive) &&
			LeftStr.Split(TEXT("("), &FullPath, &LineNumberString, ESearchCase::CaseSensitive) &&
			LineNumberString.IsNumeric() && (FCString::Strtoi(*LineNumberString, NULL, 10) > 0))
		{
			EMessageSeverity::Type Severity = EMessageSeverity::Error;
			FString FullPathTrimmed = FullPath;
			FullPathTrimmed.Trim();
			if (FullPathTrimmed.Len() != FullPath.Len()) // check for leading whitespace
			{
				Severity = EMessageSeverity::Info;
			}

			TSharedRef<FTokenizedMessage> Message = FTokenizedMessage::Create( Severity );
			if ( Severity == EMessageSeverity::Info )	// add whitespace now
			{
				FString Whitespace = FullPath.Left(FullPath.Len() - FullPathTrimmed.Len());
				Message->AddToken( FTextToken::Create( FText::FromString( Whitespace ) ) );
				FullPath = FullPathTrimmed;
			}

			FString Link = FullPath + TEXT("(") + LineNumberString + TEXT(")");
			Message->AddToken( FTextToken::Create( FText::FromString( Link ) )->OnMessageTokenActivated(FOnMessageTokenActivated::CreateStatic(&FCompilerResultsLog::OnGotoError) ) );
			Message->AddToken( FTextToken::Create( FText::FromString( RightStr ) ) );
			Messages.Add(Message);

			if (Severity == EMessageSeverity::Error)
			{
				FPlatformMisc::LowLevelOutputDebugStringf(TEXT("%s"), *Line);
			}
		}
		else
		{
			EMessageSeverity::Type Severity = EMessageSeverity::Info;
			if (Line.Contains(TEXT("error LNK"), ESearchCase::CaseSensitive))
			{
				Severity = EMessageSeverity::Error;
				FPlatformMisc::LowLevelOutputDebugStringf(TEXT("%s"), *Line);
			}

			TSharedRef<FTokenizedMessage> Message = FTokenizedMessage::Create( Severity );
			Message->AddToken( FTextToken::Create( FText::FromString( Line ) ) );
			Messages.Add(Message);
		}
	}

	return Messages;
}