Esempio n. 1
0
void AGraph::DFS_makeVisualizers(TArray<int32>& stack,
								 int32 vIndex)
{
	if (mark[vIndex] == nullptr || mark[vIndex]->visited) return;

	markVertex(vIndex);
	stack.Push(vIndex);
	//generate a visulaizer for an unvisited vertex
	auto vertexMesh = makeVertexMeshForVertex(vIndex);
	vertexMeshes.Add(vertexMesh);

	for (auto v = first(vIndex);
		v != nullptr;
		v = next(vIndex, v->vertexIndex))
	{
		bool isBackEdge = v->visited && vIndex < v->vertexIndex;

		if (!v->visited || isBackEdge)
		{
			//generate an edge
			auto edgeMesh = makeEdgeMeshForEdge(vIndex, v->vertexIndex);
			initializeEdgeMesh(edgeMesh, mark[vIndex], v);
			edgeMeshes.Add(edgeMesh);

			if (!v->visited)
			{
				DFS_makeVisualizers(stack, v->vertexIndex);
			}
		}
	}

	stack.Pop();
}
Esempio n. 2
0
void C_WriteCVars (BYTE **demo_p, DWORD filter, bool compact)
{
	FBaseCVar *cvar = CVars;
	BYTE *ptr = *demo_p;

	if (compact)
	{
		TArray<FBaseCVar *> cvars;
		ptr += sprintf ((char *)ptr, "\\\\%ux", filter);
		FilterCompactCVars (cvars, filter);
		while (cvars.Pop (cvar))
		{
			UCVarValue val = cvar->GetGenericRep (CVAR_String);
			ptr += sprintf ((char *)ptr, "\\%s", val.String);
		}
	}
	else
	{
		cvar = CVars;
		while (cvar)
		{
			if ((cvar->Flags & filter) && !(cvar->Flags & (CVAR_NOSAVE|CVAR_IGNORE)))
			{
				UCVarValue val = cvar->GetGenericRep (CVAR_String);
				ptr += sprintf ((char *)ptr, "\\%s\\%s",
					cvar->GetName (), val.String);
			}
			cvar = cvar->m_Next;
		}
	}

	*demo_p = ptr + 1;
}
Esempio n. 3
0
FString C_GetMassCVarString (DWORD filter, bool compact)
{
	FBaseCVar *cvar;
	FString dump;

	if (compact)
	{
		TArray<FBaseCVar *> cvars;
		dump.AppendFormat("\\\\%ux", filter);
		FilterCompactCVars(cvars, filter);
		while (cvars.Pop (cvar))
		{
			UCVarValue val = cvar->GetGenericRep(CVAR_String);
			dump << '\\' << val.String;
		}
	}
	else
	{
		for (cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
		{
			if ((cvar->Flags & filter) && !(cvar->Flags & (CVAR_NOSAVE|CVAR_IGNORE)))
			{
				UCVarValue val = cvar->GetGenericRep(CVAR_String);
				dump << '\\' << cvar->GetName() << '\\' << val.String;
			}
		}
	}
	return dump;
}
EConvertQueryResult AddSweepResults(bool& OutHasValidBlockingHit, const UWorld* World, int32 NumHits, PxSweepHit* Hits, float CheckLength, const PxFilterData& QueryFilter, TArray<FHitResult>& OutHits, const FVector& StartLoc, const FVector& EndLoc, const PxGeometry& Geom, const PxTransform& QueryTM, float MaxDistance, bool bReturnFaceIndex, bool bReturnPhysMat)
{
	OutHits.Reserve(OutHits.Num() + NumHits);
	EConvertQueryResult ConvertResult = EConvertQueryResult::Valid;
	bool bHadBlockingHit = false;
	const PxVec3 PDir = U2PVector((EndLoc - StartLoc).GetSafeNormal());

	for(int32 i=0; i<NumHits; i++)
	{
		PxSweepHit& PHit = Hits[i];
		checkSlow(PHit.flags & PxHitFlag::eDISTANCE);
		if(PHit.distance <= MaxDistance)
		{
			PHit.faceIndex = FindFaceIndex(PHit, PDir);

			FHitResult& NewResult = OutHits[OutHits.AddDefaulted()];
			if (ConvertQueryImpactHit(World, PHit, NewResult, CheckLength, QueryFilter, StartLoc, EndLoc, &Geom, QueryTM, bReturnFaceIndex, bReturnPhysMat) == EConvertQueryResult::Valid)
			{
				bHadBlockingHit |= NewResult.bBlockingHit;
			}
			else
			{
				// Reject invalid result (this should be rare). Remove from the results.
				OutHits.Pop(/*bAllowShrinking=*/ false);
				ConvertResult = EConvertQueryResult::Invalid;
			}
			
		}
	}

	// Sort results from first to last hit
	OutHits.Sort( FCompareFHitResultTime() );
	OutHasValidBlockingHit = bHadBlockingHit;
	return ConvertResult;
}
Esempio n. 5
0
void SGraphTitleBar::RebuildBreadcrumbTrail()
{
	// Build up a stack of graphs so we can pop them in reverse order and create breadcrumbs
	TArray<UEdGraph*> Stack;
	for (UEdGraph* OuterChain = EdGraphObj; OuterChain != NULL; OuterChain = GetOuterGraph(OuterChain))
	{
		Stack.Push(OuterChain);
	}

	BreadcrumbTrail->ClearCrumbs(false);

	//Get the last object in the array
	UEdGraph* LastObj = NULL;
	if( Stack.Num() > 0 )
	{
		LastObj = Stack[Stack.Num() -1];
	}

	while (Stack.Num() > 0)
	{
		UEdGraph* Graph = Stack.Pop();
		
		auto Foo = TAttribute<FText>::Create(TAttribute<FText>::FGetter::CreateStatic<const UEdGraph*>(&SGraphTitleBar::GetTitleForOneCrumb, Graph));
		BreadcrumbTrail->PushCrumb(Foo, Graph);
	}
}
bool UAblAbilityTaskDependencyValidator::HasCircularDependency(const TWeakObjectPtr<UAblAbilityTaskValidatorContext>& Context, const UAblAbilityTask* TaskToCheck, const UAblAbilityTask* CurrentTask) const
{
	static TArray<const UAblAbilityTask*> Stack;
	Stack.Push(CurrentTask);

	bool Result = false;
	if (CurrentTask->HasDependencies())
	{
		for (const UAblAbilityTask* Dependency : CurrentTask->GetTaskDependencies())
		{
			if (Dependency == TaskToCheck)
			{
				FText ErrorMessage = FText::FormatOrdered(LOCTEXT("AbilityValidatorTaskCirculeDependency", "Circular Dependency Detected for Task {0}, dependency stack:"), TaskToCheck->GetTaskName());
				Context->AddError(ErrorMessage);

				Result = true;
				break;
			}
			else
			{
				if (!Stack.Contains(Dependency) && HasCircularDependency(Context, TaskToCheck, Dependency))
				{
					Context->AddError(FText::FormatOrdered(LOCTEXT("AbilityValidatorTaskCircularDependencyStack", "Task {0} depends on {1}"), CurrentTask->GetTaskName(), Dependency->GetTaskName()));

					Result = true;
					break;
				}
			}
		}
	}

	Stack.Pop();
	return Result;
}
Esempio n. 7
0
void ASpellDeck::PutOnTop(TArray<UActionCard*> cardsToPut_p)
{
	for (auto card : cardsToPut_p)
	{
		spellDeck.Push(cardsToPut_p.Pop());
	}
}
Esempio n. 8
0
void cvar_t::C_WriteCVars (byte **demo_p, DWORD filter, bool compact)
{
	cvar_t *cvar = ad.GetCVars();
	byte *ptr = *demo_p;

	if (compact)
	{
		TArray<cvar_t *> cvars;
		ptr += sprintf ((char *)ptr, "\\\\%ux", (unsigned int)filter);
		FilterCompactCVars (cvars, filter);
		while (cvars.Pop (cvar))
		{
			ptr += sprintf ((char *)ptr, "\\%s", cvar->cstring());
		}
	}
	else
	{
		cvar = ad.GetCVars();
		while (cvar)
		{
			if (cvar->m_Flags & filter)
			{
				ptr += sprintf ((char *)ptr, "\\%s\\%s",
								cvar->name(), cvar->cstring());
			}
			cvar = cvar->m_Next;
		}
	}

	*demo_p = ptr + 1;
}
Esempio n. 9
0
	TArray<FChunkPart> FDataStructure::GetFinalDataStructure()
	{
		if (DataStructure.Top().PartSize == 0)
		{
			DataStructure.Pop(false);
		}
		return MoveTemp(DataStructure);
	}
Esempio n. 10
0
bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd)
{
	fixed_t tryx, tryy;
	bool try_ok;
	int good;

	if (actor->movedir == DI_NODIR)
		return false;

	if ((unsigned)actor->movedir >= 8)
		I_Error ("Weird bot movedir!");

	tryx = actor->x + 8*xspeed[actor->movedir];
	tryy = actor->y + 8*yspeed[actor->movedir];

	try_ok = CleanAhead (actor, tryx, tryy, cmd);

	if (!try_ok) //Anything blocking that could be opened etc..
	{
		if (!spechit.Size ())
			return false;

		actor->movedir = DI_NODIR;

		good = 0;
		line_t *ld;

		while (spechit.Pop (ld))
		{
			bool tryit = true;

			if (ld->special == Door_LockedRaise && !P_CheckKeys (actor, ld->args[3], false))
				tryit = false;
			else if (ld->special == Generic_Door && !P_CheckKeys (actor, ld->args[4], false))
				tryit = false;

			if (tryit &&
				(P_TestActivateLine (ld, actor, 0, SPAC_Use) ||
				 P_TestActivateLine (ld, actor, 0, SPAC_Push)))
			{
				good |= ld == actor->BlockingLine ? 1 : 2;
			}
		}
		if (good && ((pr_botopendoor() >= 203) ^ (good & 1)))
		{
			cmd->ucmd.buttons |= BT_USE;
			cmd->ucmd.forwardmove = FORWARDRUN;
			return true;
		}
		else
			return false;
	}
	else //Move forward.
		cmd->ucmd.forwardmove = FORWARDRUN;

	return true;
}
Esempio n. 11
0
void SGameplayTagWidget::VerifyAssetTagValidity()
{
	FGameplayTagContainer LibraryTags;

	// Create a set that is the library of all valid tags
	TArray< TSharedPtr<FGameplayTagNode> > NodeStack;

	UGameplayTagsManager& TagsManager = IGameplayTagsModule::Get().GetGameplayTagsManager();
	
	TagsManager.GetFilteredGameplayRootTags(TEXT(""), NodeStack);

	while (NodeStack.Num() > 0)
	{
		TSharedPtr<FGameplayTagNode> CurNode = NodeStack.Pop();
		if (CurNode.IsValid())
		{
			LibraryTags.AddTag(TagsManager.RequestGameplayTag(CurNode->GetCompleteTag()));
			NodeStack.Append(CurNode->GetChildTagNodes());
		}
	}

	// Find and remove any tags on the asset that are no longer in the library
	for (int32 ContainerIdx = 0; ContainerIdx < TagContainers.Num(); ++ContainerIdx)
	{
		UObject* OwnerObj = TagContainers[ContainerIdx].TagContainerOwner.Get();
		FGameplayTagContainer* Container = TagContainers[ContainerIdx].TagContainer;

		if (Container)
		{
			FGameplayTagContainer EditableContainer = *Container;
			FGameplayTagContainer InvalidTags;
			for (auto It = Container->CreateConstIterator(); It; ++It)
			{
				if (!LibraryTags.HasTag(*It, EGameplayTagMatchType::Explicit, EGameplayTagMatchType::Explicit))
				{
					InvalidTags.AddTag(*It);
				}
			}
			if (InvalidTags.Num() > 0)
			{
				FString InvalidTagNames;

				for (auto InvalidIter = InvalidTags.CreateConstIterator(); InvalidIter; ++InvalidIter)
				{
					EditableContainer.RemoveTag(*InvalidIter);
					InvalidTagNames += InvalidIter->ToString() + TEXT("\n");
				}
				SetContainer(Container, &EditableContainer, OwnerObj);

				FFormatNamedArguments Arguments;
				Arguments.Add(TEXT("Objects"), FText::FromString( InvalidTagNames ));
				FText DialogText = FText::Format( LOCTEXT("GameplayTagWidget_InvalidTags", "Invalid Tags that have been removed: \n\n{Objects}"), Arguments );
				OpenMsgDlgInt( EAppMsgType::Ok, DialogText, LOCTEXT("GameplayTagWidget_Warning", "Warning") );
			}
		}
	}
}
Esempio n. 12
0
void FPhysXCPUDispatcherSingleThread::submitTask( PxBaseTask& Task ) 
{
	SCOPE_CYCLE_COUNTER(STAT_PhysXSingleThread);
	check(IsInGameThread());

	TaskStack.Push(&Task);
	if (TaskStack.Num() > 1)
	{
		return;
	}
	Task.run();
	Task.release();
	while (TaskStack.Num() > 1)
	{
		PxBaseTask& ChildTask = *TaskStack.Pop();
		ChildTask.run();
		ChildTask.release();
	}
	verify(&Task == TaskStack.Pop() && !TaskStack.Num());
}
void GetDisplayedHierarchyRecursive(TArray< int32 >& TreeAddress, const FSCSEditorTreeNode& Node, TArray< FSCSResolvedIdentifier >& OutResult)
{
	FSCSIdentifier Identifier = { Node.GetVariableName(), TreeAddress };
	FSCSResolvedIdentifier ResolvedIdentifier = { Identifier, Node.GetComponentTemplate() };
	OutResult.Push(ResolvedIdentifier);
	const auto& Children = Node.GetChildren();
	for (int32 Iter = 0; Iter != Children.Num(); ++Iter)
	{
		TreeAddress.Push(Iter);
		GetDisplayedHierarchyRecursive(TreeAddress, *Children[Iter], OutResult);
		TreeAddress.Pop();
	}
}
Esempio n. 14
0
void UnlatchCVars (void)
{
	FLatchedValue var;

	while (LatchedValues.Pop (var))
	{
		DWORD oldflags = var.Variable->Flags;
		var.Variable->Flags &= ~(CVAR_LATCH | CVAR_SERVERINFO);
		var.Variable->SetGenericRep (var.Value, var.Type);
		if (var.Type == CVAR_String)
			delete[] var.Value.String;
		var.Variable->Flags = oldflags;
	}
}
Esempio n. 15
0
ERenamePinResult UK2Node::RenameUserDefinedPin(const FString& OldName, const FString& NewName, bool bTest)
{
	UEdGraphPin* Pin = NULL;
	for (int32 PinIdx=0; PinIdx<Pins.Num(); PinIdx++)
	{
		if (OldName == Pins[PinIdx]->PinName)
		{
			Pin = Pins[PinIdx];
		}
		else if(NewName == Pins[PinIdx]->PinName)
		{
			return ERenamePinResult::ERenamePinResult_NameCollision;
		}
	}

	if(!Pin)
	{
		return ERenamePinResult::ERenamePinResult_NoSuchPin;
	}

	if(!bTest)
	{
		Pin->Modify();
		Pin->PinName = NewName;
		if(!Pin->DefaultTextValue.IsEmpty())
		{
			Pin->GetSchema()->TrySetDefaultText(*Pin, Pin->DefaultTextValue);
		}

		if (Pin->SubPins.Num() > 0)
		{
			TArray<UEdGraphPin*> PinsToUpdate = Pin->SubPins;

			while (PinsToUpdate.Num() > 0)
			{
				UEdGraphPin* PinToRename = PinsToUpdate.Pop(/*bAllowShrinking=*/ false);
				if (PinToRename->SubPins.Num() > 0)
				{
					PinsToUpdate.Append(PinToRename->SubPins);
				}
				PinToRename->Modify();
				PinToRename->PinName = NewName + PinToRename->PinName.RightChop(OldName.Len());
				PinToRename->PinFriendlyName = FText::FromString(NewName + PinToRename->PinFriendlyName.ToString().RightChop(OldName.Len()));
			}
		}
	}

	return ERenamePinResult::ERenamePinResult_Success;
}
TArray<FString> USkeleUtilityFunctionLibrary::GetDefaultPlayerNamesFromFile() {
    FString filePath = FPaths::GameDir() + "Config/DefaultPlayerNames.ini";

    FString fileContents = "";
    FFileHelper::LoadFileToString(fileContents, *filePath);

    TArray<FString> items;
    int32 itemCount = fileContents.ParseIntoArray(items, TEXT(","), true);

    if (items.Last().Contains(TEXT("\n"))) {
        items.Pop();
    }

    return items;
}
Esempio n. 17
0
bool FXmlFile::Save(const FString& Path)
{
	FString Xml;
	Xml += TEXT("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") LINE_TERMINATOR;

	TArray<const FXmlNode*> NodeStack;
	static const TCHAR Tabs[] = TEXT("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t");
	auto Indent = &Tabs[ARRAY_COUNT(Tabs) - 1];
	const auto* CurrentNode = GetRootNode();
	while (CurrentNode)
	{
		if (auto Child = CurrentNode->GetFirstChildNode())
		{
			NodeStack.Push(CurrentNode);
			Xml += FString::Printf(TEXT("%s<%s>") LINE_TERMINATOR, Indent, *CurrentNode->GetTag());

			CurrentNode = Child;	
			// Actually increases the indent by one tab
			--Indent;
			continue;
		}

		auto Tag = *CurrentNode->GetTag();
		Xml += FString::Printf(TEXT("%s<%s>%s</%s>") LINE_TERMINATOR, Indent, Tag, *CurrentNode->GetContent(), Tag);
		CurrentNode = CurrentNode->GetNextNode();

		while (!CurrentNode && NodeStack.Num() != 0)
		{
			// Actually decreases the indent by one tab
			++Indent;
			CurrentNode = NodeStack.Pop();
			Xml += FString::Printf(TEXT("%s</%s>") LINE_TERMINATOR, Indent, *CurrentNode->GetTag());
			CurrentNode = CurrentNode->GetNextNode();
		}
	}
	
	TUniquePtr<FArchive> Archive(IFileManager::Get().CreateFileWriter(*Path));
	if (!Archive)
	{
		ErrorMessage = NSLOCTEXT("XmlParser", "FileSaveFail", "Failed to save the file").ToString();
		ErrorMessage += FString::Printf(TEXT("\"%s\""), *Path);
		return false;
	}

	FTCHARToUTF8 Converter(*Xml);
	Archive->Serialize(const_cast<char*>(Converter.Get()), Converter.Length());
	return true;
}
Esempio n. 18
0
    /** FloodFill core */
    void FloodFillTriangleGroups( int32 InitialTriangleIndex, int32 GroupIndex )
    {
        TArray<int32> TriangleStack;

        // Start with given triangle
        TriangleStack.Add( InitialTriangleIndex );

        // Set the group for our first triangle
        Triangles[ InitialTriangleIndex ].Group = GroupIndex;

        // Process until we have triangles in stack
        while ( TriangleStack.Num() )
        {
            // Pop triangle index from stack
            int32 TriangleIndex = TriangleStack.Pop();

            FMeshConnectivityTriangle &Triangle = Triangles[ TriangleIndex ];

            // All triangles should already have a group before we start processing neighbors
            checkSlow( Triangle.Group == GroupIndex );

            // Add to list of triangles in group
            Groups[ GroupIndex ].Triangles.Add( TriangleIndex );

            // Recurse to all other triangles connected with this one
            for ( int32 i=0; i<3; i++ )
            {
                int32 VertexIndex = Triangle.Vertices[ i ];
                const FMeshConnectivityVertex &Vertex = Vertices[ VertexIndex ];

                for ( int32 j=0; j<Vertex.Triangles.Num(); j++ )
                {
                    int32 OtherTriangleIndex = Vertex.Triangles[ j ];
                    FMeshConnectivityTriangle &OtherTriangle = Triangles[ OtherTriangleIndex ];

                    // Only recurse if triangle was not already assigned to a group
                    if ( OtherTriangle.Group == INDEX_NONE )
                    {
                        // OK, the other triangle now belongs to our group!
                        OtherTriangle.Group = GroupIndex;

                        // Add the other triangle to the stack to be processed
                        TriangleStack.Add( OtherTriangleIndex );
                    }
                }
            }
        }
    }
Esempio n. 19
0
void R_3D_LeaveSkybox()
{
	HeightStack current;

	current.height_top = NULL;
	current.height_cur = NULL;
	current.height_max = -1;

	toplist.Pop(current);

	height_top = current.height_top;
	height_cur = current.height_cur;
	height_max = current.height_max;

	CurrentSkybox--;
}
void FRedirectCollector::ResolveStringAssetReference()
{
	while (StringAssetReferences.Num())
	{
		TMultiMap<FString, FString>::TIterator First(StringAssetReferences);
		FString ToLoad = First.Key();
		FString RefFilename = First.Value();
		First.RemoveCurrent();

		if (ToLoad.Len() > 0)
		{
			UE_LOG(LogRedirectors, Log, TEXT("String Asset Reference '%s'"), *ToLoad);
			StringAssetRefFilenameStack.Push(RefFilename);

			UObject *Loaded = LoadObject<UObject>(NULL, *ToLoad, NULL, LOAD_None, NULL);
			StringAssetRefFilenameStack.Pop();

			UObjectRedirector* Redirector = dynamic_cast<UObjectRedirector*>(Loaded);
			if (Redirector)
			{
				UE_LOG(LogRedirectors, Log, TEXT("    Found redir '%s'"), *Redirector->GetFullName());
				FRedirection Redir;
				Redir.PackageFilename = RefFilename;
				Redir.RedirectorName = Redirector->GetFullName();
				Redir.RedirectorPackageFilename = Redirector->GetLinker()->Filename;
				CA_SUPPRESS(28182)
				Redir.DestinationObjectName = Redirector->DestinationObject->GetFullName();
				Redirections.AddUnique(Redir);
				Loaded = Redirector->DestinationObject;
			}
			if (Loaded)
			{
				FString Dest = Loaded->GetPathName();
				UE_LOG(LogRedirectors, Log, TEXT("    Resolved to '%s'"), *Dest);
				if (Dest != ToLoad)
				{
					StringAssetRemap.Add(ToLoad, Dest);
				}
			}
			else
			{
				UE_LOG(LogRedirectors, Log, TEXT("    Not Found!"));
			}
		}
	}
}
Esempio n. 21
0
void P_FreeStrifeConversations ()
{
	FStrifeDialogueNode *node;

	while (StrifeDialogues.Pop (node))
	{
		delete node;
	}

	for (int i = 0; i < 344; ++i)
	{
		if (StrifeTypes[i] != NULL)
		{
			GetDefaultByType (StrifeTypes[i])->Conversation = NULL;
		}
	}

	CurNode = NULL;
	PrevNode = NULL;
}
void FDestructibleMeshEditorViewportClient::UpdateChunkSelection( TArray<int32> InSelectedChunkIndices )
{
	// Store the proxies in the ppol
	UnusedProxies.Append(SelectedChunks);

	// Clear selected chunks
	SelectedChunks.Empty(InSelectedChunkIndices.Num());

	// make sure we have enough proxies to fill the selection array */
	while(UnusedProxies.Num() < InSelectedChunkIndices.Num())
	{
		UnusedProxies.Add(NewObject<UDestructibleChunkParamsProxy>());
	}

	UDestructibleMesh* DestructibleMesh = DestructibleMeshEditorPtr.Pin()->GetDestructibleMesh();
	UDestructibleFractureSettings* FractureSettings = DestructibleMesh->FractureSettings;

	TArray<UObject*> SelectedObjects;
	// Setup the selection array
	for (int32 i=0; i < InSelectedChunkIndices.Num(); ++i)
	{
		UDestructibleChunkParamsProxy* Proxy = UnusedProxies.Pop();

		Proxy->DestructibleMesh = DestructibleMesh;
		Proxy->ChunkIndex = InSelectedChunkIndices[i];
		Proxy->DestructibleMeshEditorPtr = DestructibleMeshEditorPtr;

		if (FractureSettings != NULL && FractureSettings->ChunkParameters.Num() > Proxy->ChunkIndex)
		{
			Proxy->ChunkParams = Proxy->DestructibleMesh->FractureSettings->ChunkParameters[Proxy->ChunkIndex];
		}

		SelectedChunks.Add(Proxy);
		SelectedObjects.Add(Proxy);
	}


	FDestructibleMeshEditor* MeshEd = (FDestructibleMeshEditor*)DestructibleMeshEditorPtr.Pin().Get();
	MeshEd->SetSelectedChunks(SelectedObjects);
}
Esempio n. 23
0
bool USoundCue::RecursiveFindPathToNode(USoundNode* CurrentNode, const UPTRINT CurrentHash, const UPTRINT NodeHashToFind, TArray<USoundNode*>& OutPath) const
{
	OutPath.Push(CurrentNode);
	if (CurrentHash == NodeHashToFind)
	{
		return true;
	}

	for (int32 ChildIndex = 0; ChildIndex < CurrentNode->ChildNodes.Num(); ++ChildIndex)
	{
		USoundNode* ChildNode = CurrentNode->ChildNodes[ChildIndex];
		if (ChildNode)
		{
			if (RecursiveFindPathToNode(ChildNode, USoundNode::GetNodeWaveInstanceHash(CurrentHash, ChildNode, ChildIndex), NodeHashToFind, OutPath))
			{
				return true;
			}
		}
	}

	OutPath.Pop();
	return false;
}
EConvertQueryResult ConvertRaycastResults(bool& OutHasValidBlockingHit, const UWorld* World, int32 NumHits, PxRaycastHit* Hits, float CheckLength, const PxFilterData& QueryFilter, TArray<FHitResult>& OutHits, const FVector& StartLoc, const FVector& EndLoc, bool bReturnFaceIndex, bool bReturnPhysMat)
{
#if PLATFORM_LINUX	// to narrow down OR-24947
	_Pragma("clang optimize off");
#endif // PLATFORM_LINUX

	OutHits.Reserve(OutHits.Num() + NumHits);
	EConvertQueryResult ConvertResult = EConvertQueryResult::Valid;
	bool bHadBlockingHit = false;

	PxTransform PStartTM(U2PVector(StartLoc));
	for(int32 i=0; i<NumHits; i++)
	{
		FHitResult& NewResult = OutHits[OutHits.Emplace()];
		const PxRaycastHit& PHit = Hits[i];

		if (ConvertQueryImpactHit(World, PHit, NewResult, CheckLength, QueryFilter, StartLoc, EndLoc, NULL, PStartTM, bReturnFaceIndex, bReturnPhysMat) == EConvertQueryResult::Valid)
		{
			bHadBlockingHit |= NewResult.bBlockingHit;
		}
		else
		{
			// Reject invalid result (this should be rare). Remove from the results.
			OutHits.Pop(/*bAllowShrinking=*/ false);
			ConvertResult = EConvertQueryResult::Invalid;
		}
	}

	// Sort results from first to last hit
	OutHits.Sort( FCompareFHitResultTime() );
	OutHasValidBlockingHit = bHadBlockingHit;
	return ConvertResult;

#if PLATFORM_LINUX	// to narrow down OR-24947
	_Pragma("clang optimize on");
#endif // PLATFORM_LINUX
}
			FRectangleSortHelper(TArray<FIntRect>& InOutSprites)
			{
				// Sort by Y, then by X (top left corner), descending order (so we can use it as a stack from the top row down)
				TArray<FIntRect> SpritesLeft = InOutSprites;
				SpritesLeft.Sort([](const FIntRect& A, const FIntRect& B) { return (A.Min.Y == B.Min.Y) ? (A.Min.X > B.Min.X) : (A.Min.Y > B.Min.Y); });
				InOutSprites.Reset();

				// Start pulling sprites out, the first one in each row will dominate remaining ones and cause them to get labeled
				TArray<FIntRect> DominatedSprites;
				DominatedSprites.Empty(SpritesLeft.Num());
				while (SpritesLeft.Num())
				{
					FIntRect DominatingSprite = SpritesLeft.Pop();
					DominatedSprites.Add(DominatingSprite);

					// Find the sprites that are dominated (intersect the infinite horizontal band described by the dominating sprite)
					for (int32 Index = 0; Index < SpritesLeft.Num();)
					{
						const FIntRect& CurElement = SpritesLeft[Index];
						if ((CurElement.Min.Y <= DominatingSprite.Max.Y) && (CurElement.Max.Y >= DominatingSprite.Min.Y))
						{
							DominatedSprites.Add(CurElement);
							SpritesLeft.RemoveAt(Index, /*Count=*/ 1, /*bAllowShrinking=*/ false);
						}
						else
						{
							++Index;
						}
					}

					// Sort the sprites in the band by X and add them to the result
					DominatedSprites.Sort([](const FIntRect& A, const FIntRect& B) { return (A.Min.X < B.Min.X); });
					InOutSprites.Append(DominatedSprites);
					DominatedSprites.Reset();
				}
			}
void FMaterialEditorUtilities::GetVisibleMaterialParametersFromExpression(UMaterialExpression *MaterialExpression, UMaterialInstance *MaterialInstance, TArray<FGuid> &VisibleExpressions, TArray<UMaterialExpression*> &ProcessedExpressions, const TArray<FFunctionExpressionInput>* FunctionInputs, TArray<FGuid>* VisibleFunctionInputExpressions)
{
	if(!MaterialExpression)
	{
		return;
	}

	check(MaterialInstance);

	//don't allow re-entrant expressions to continue
	if (ProcessedExpressions.Contains(MaterialExpression))
	{
		return;
	}
	ProcessedExpressions.Push(MaterialExpression);

	if(MaterialExpression->Material != NULL)
	{
		// if it's a material parameter it must be visible so add it to the map
		UMaterialExpressionParameter *Param = Cast<UMaterialExpressionParameter>( MaterialExpression );
		UMaterialExpressionTextureSampleParameter *TexParam = Cast<UMaterialExpressionTextureSampleParameter>( MaterialExpression );
		UMaterialExpressionFontSampleParameter *FontParam = Cast<UMaterialExpressionFontSampleParameter>( MaterialExpression );
		if( Param )
		{
			VisibleExpressions.AddUnique(Param->ExpressionGUID);

			UMaterialExpressionScalarParameter *ScalarParam = Cast<UMaterialExpressionScalarParameter>( MaterialExpression );
			UMaterialExpressionVectorParameter *VectorParam = Cast<UMaterialExpressionVectorParameter>( MaterialExpression );
			TArray<FName> Names;
			TArray<FGuid> Ids;
			if( ScalarParam )
			{
				MaterialInstance->GetMaterial()->GetAllScalarParameterNames( Names, Ids );
				for( int32 i = 0; i < Names.Num(); i++ )
				{
					if( Names[i] == ScalarParam->ParameterName )
					{
						VisibleExpressions.AddUnique( Ids[ i ] );
					}
				}
			}
			else if ( VectorParam )
			{
				MaterialInstance->GetMaterial()->GetAllVectorParameterNames( Names, Ids );
				for( int32 i = 0; i < Names.Num(); i++ )
				{
					if( Names[i] == VectorParam->ParameterName )
					{
						VisibleExpressions.AddUnique( Ids[ i ] );
					}
				}
			}
		}
		else if(TexParam)
		{
			VisibleExpressions.AddUnique( TexParam->ExpressionGUID );
			TArray<FName> Names;
			TArray<FGuid> Ids;
			MaterialInstance->GetMaterial()->GetAllTextureParameterNames( Names, Ids );
			for( int32 i = 0; i < Names.Num(); i++ )
			{
				if( Names[i] == TexParam->ParameterName )
				{
					VisibleExpressions.AddUnique( Ids[ i ] );
				}
			}
		}
		else if(FontParam)
		{
			VisibleExpressions.AddUnique( FontParam->ExpressionGUID );
			TArray<FName> Names;
			TArray<FGuid> Ids;
			MaterialInstance->GetMaterial()->GetAllFontParameterNames( Names, Ids );
			for( int32 i = 0; i < Names.Num(); i++ )
			{
				if( Names[i] == FontParam->ParameterName )
				{
					VisibleExpressions.AddUnique( Ids[ i ] );
				}
			}
		}
	}

	UMaterialExpressionFunctionInput* InputExpression = Cast<UMaterialExpressionFunctionInput>(MaterialExpression);
	if(InputExpression)
	{
		VisibleFunctionInputExpressions->AddUnique(InputExpression->Id);
	}

	// check if it's a switch expression and branch according to its value
	UMaterialExpressionStaticSwitchParameter* StaticSwitchParamExpression = Cast<UMaterialExpressionStaticSwitchParameter>(MaterialExpression);
	UMaterialExpressionStaticSwitch* StaticSwitchExpression = Cast<UMaterialExpressionStaticSwitch>(MaterialExpression);
	UMaterialExpressionMaterialFunctionCall* FunctionCallExpression = Cast<UMaterialExpressionMaterialFunctionCall>(MaterialExpression);
	if(StaticSwitchParamExpression)
	{
		bool Value = false;
		FGuid ExpressionID;
		MaterialInstance->GetStaticSwitchParameterValue(StaticSwitchParamExpression->ParameterName, Value, ExpressionID);
		VisibleExpressions.AddUnique(ExpressionID);

		if(Value)
		{
			GetVisibleMaterialParametersFromExpression(StaticSwitchParamExpression->A.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions);
		}
		else
		{
			GetVisibleMaterialParametersFromExpression(StaticSwitchParamExpression->B.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions);
		}
	}
	else if(StaticSwitchExpression)
	{
		bool bValue = StaticSwitchExpression->DefaultValue;
		FGuid ExpressionID;
		if(StaticSwitchExpression->Value.Expression)
		{

			GetStaticSwitchExpressionValue(MaterialInstance, StaticSwitchExpression->Value.Expression, bValue, ExpressionID, FunctionInputs );

			// Flag the switch value as a visible expression
			UMaterialExpressionFunctionInput* FunctionInputExpression =  Cast<UMaterialExpressionFunctionInput>(StaticSwitchExpression->Value.Expression);
			if(FunctionInputExpression)
			{
				VisibleFunctionInputExpressions->AddUnique(FunctionInputExpression->Id);
			}
			else if (StaticSwitchExpression && StaticSwitchExpression->Material != NULL)
			{
				if(ExpressionID.IsValid())
				{
					VisibleExpressions.AddUnique(ExpressionID);
				}
			}
		}

		if(bValue)
		{
			GetVisibleMaterialParametersFromExpression(StaticSwitchExpression->A.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions);
		}
		else
		{
			GetVisibleMaterialParametersFromExpression(StaticSwitchExpression->B.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions);
		}
	}
	else if(FunctionCallExpression)
	{
		// Only process functions if any of the dependent functions contain static switches that could potentially cull the material parameters.
		if(IsFunctionContainingSwitchExpressions(FunctionCallExpression->MaterialFunction))
		{

			// Pass an accumalated array of FFunctionExpressionInput so that input expressions can be matched to the input from the parent.			
			TArray<FGuid> VisibleFunctionInputs;
			TArray<FFunctionExpressionInput> FunctionCallInputs = FunctionCallExpression->FunctionInputs;
			if(FunctionInputs)
			{
				FunctionCallInputs.Append(*FunctionInputs);
			}

			for(int32 FunctionOutputIndex = 0; FunctionOutputIndex < FunctionCallExpression->FunctionOutputs.Num(); FunctionOutputIndex++)
			{
				// Recurse material functions. Returns an array of input ids that are visible and have not been culled by static switches.
				GetVisibleMaterialParametersFromExpression( FunctionCallExpression->FunctionOutputs[FunctionOutputIndex].ExpressionOutput->A.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, &FunctionCallInputs, &VisibleFunctionInputs);
			}

			// Parse children of function call inputs that have not been culled 
			for(int32 ExpressionInputIndex = 0; ExpressionInputIndex < FunctionCallExpression->FunctionInputs.Num(); ExpressionInputIndex++)
			{
				const FFunctionExpressionInput& Input = FunctionCallExpression->FunctionInputs[ExpressionInputIndex];
				if(VisibleFunctionInputs.Contains(Input.ExpressionInputId)) 
				{
					// Retrieve the expression input and then start parsing its children
					GetVisibleMaterialParametersFromExpression(Input.Input.Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions );
				}
			}
		}
		else
		{
			const TArray<FExpressionInput*>& ExpressionInputs = MaterialExpression->GetInputs();
			for(int32 ExpressionInputIndex = 0; ExpressionInputIndex < ExpressionInputs.Num(); ExpressionInputIndex++)
			{
				//retrieve the expression input and then start parsing its children
				FExpressionInput* Input = ExpressionInputs[ExpressionInputIndex];
				GetVisibleMaterialParametersFromExpression(Input->Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions);
			}
		}
	}
	else
	{
		const TArray<FExpressionInput*>& ExpressionInputs = MaterialExpression->GetInputs();
		for(int32 ExpressionInputIndex = 0; ExpressionInputIndex < ExpressionInputs.Num(); ExpressionInputIndex++)
		{
			//retrieve the expression input and then start parsing its children
			FExpressionInput* Input = ExpressionInputs[ExpressionInputIndex];
			GetVisibleMaterialParametersFromExpression(Input->Expression, MaterialInstance, VisibleExpressions, ProcessedExpressions, FunctionInputs, VisibleFunctionInputExpressions);
		}
	}

	UMaterialExpression* TopExpression = ProcessedExpressions.Pop();
	//ensure that the top of the stack matches what we expect (the same as MaterialExpression)
	check(MaterialExpression == TopExpression);
}
Esempio n. 27
0
File: UnLua.cpp Progetto: hce/unlua
static int
luaCallFunction(lua_State *L)
{
	int numparams = lua_gettop(L);
	lua_getglobal(L, "__unlua");
	AActor* unlua = (AActor*) lua_topointer(L, -1);
	lua_remove(L, -1);

	AActor* actor = (AActor*) lua_topointer(L, lua_upvalueindex(1));
	UFunction* func = (UFunction*) lua_topointer(L, lua_upvalueindex(2));
	const char* funcName_s = lua_tostring(L, lua_upvalueindex(3));
	

	if (func->NumParms == 0) {
		actor->ProcessEvent(func, NULL, NULL);
		return 0;
	}

	TFieldIterator<UProperty> It (func);
	char params[1024];
	memset(params, 0, sizeof(params));
	char* parptr = params;
	TArray<FString*> allocatedStrings;
	for (int i = 0; i < func->NumParms; ++i) {
		if (i >= numparams) {
			if (It->PropertyFlags & CPF_OptionalParm) {
				break;
			} else {
				lua_pushfstring(L, "Too few parameters for call to %s", funcName_s);
				lua_error(L);
			}
		}
		if (It->IsA(UIntProperty::StaticClass())) {
			int anint = lua_tointeger(L, i + 1);
			memcpy(parptr, &anint, It->ElementSize);
		} else if (It->IsA(UStrProperty::StaticClass())) {
			FString* astr = new FString(lua_tostring(L, i + 1));
			allocatedStrings.AddItem(astr);
			memcpy(parptr, astr, It->ElementSize);
		} else if (It->IsA(UNameProperty::StaticClass())) {
			FString astr(lua_tostring(L, i + 1));
			FName aname(*astr, FNAME_Add);
			memcpy(parptr, &aname, It->ElementSize);
		} else if (It->IsA(UByteProperty::StaticClass())) {
			*parptr = (char) lua_tointeger(L, i + 1);
		} else if (It->IsA(UBoolProperty::StaticClass())) {
			int bval = lua_toboolean(L, i + 1);
			memcpy(parptr, &bval, It->ElementSize);
		} else if (It->IsA(UFloatProperty::StaticClass())) {
			float fval = (float)lua_tonumber(L, i + 1);
			memcpy(parptr, &fval, It->ElementSize);
		} else if (It->IsA(UObjectProperty::StaticClass())) {
			UObjectProperty *uop = (UObjectProperty*) *It;
			struct UnLuaActor* ula  = (struct UnLuaActor*)
				luaL_testudata(L, i + 1, "UnrealActor");
			struct UnrealClass* ulc = (struct UnrealClass*)
				luaL_testudata(L, i + 1, "UnrealClass");
			UObject** uobj = (UObject**) parptr;
			if ((ula == NULL) && (ulc == NULL))
				*uobj = NULL;
			else {
				if (uop->PropertyClass == UClass::StaticClass()) {
					if (ulc == NULL) {
						lua_pushfstring(L, "Invalid type for parameter %d", i+1);
						lua_error(L);
					}
					*uobj = ulc->uclass;
				} else {
					if (ula->actor->IsA(uop->PropertyClass)) {
						//memcpy(parptr, ula->actor, It->ElementSize);
						*uobj = ula->actor;
					} else {
						lua_pushfstring(L, "Invalid type for parameter %d", i+1);
						lua_error(L);
					}
				}
			}
		} else {
			lua_pushfstring(L, "luaCallFunction: Unreal function %s requires "
				"parameter type not yet supported by UnLua", funcName_s);
			lua_error(L);
		}
		parptr += It->ElementSize;
		++It;
	}
	actor->ProcessEvent(func, params, NULL);
	free(params);
	while(allocatedStrings.Num())
		delete allocatedStrings.Pop();

	return 0;
}
Esempio n. 28
0
void C_ReadCVars (BYTE **demo_p)
{
	char *ptr = *((char **)demo_p);
	char *breakpt;

	if (*ptr++ != '\\')
		return;

	if (*ptr == '\\')
	{       // compact mode
		TArray<FBaseCVar *> cvars;
		FBaseCVar *cvar;
		DWORD filter;

		ptr++;
		breakpt = strchr (ptr, '\\');
		*breakpt = 0;
		filter = strtoul (ptr, NULL, 16);
		*breakpt = '\\';
		ptr = breakpt + 1;

		FilterCompactCVars (cvars, filter);

		while (cvars.Pop (cvar))
		{
			UCVarValue val;
			breakpt = strchr (ptr, '\\');
			if (breakpt)
				*breakpt = 0;
			val.String = ptr;
			cvar->ForceSet (val, CVAR_String);
			if (breakpt)
			{
				*breakpt = '\\';
				ptr = breakpt + 1;
			}
			else
				break;
		}
	}
	else
	{
		char *value;

		while ( (breakpt = strchr (ptr, '\\')) )
		{
			*breakpt = 0;
			value = breakpt + 1;
			if ( (breakpt = strchr (value, '\\')) )
				*breakpt = 0;

			cvar_set (ptr, value);

			*(value - 1) = '\\';
			if (breakpt)
			{
				*breakpt = '\\';
				ptr = breakpt + 1;
			}
			else
			{
				break;
			}
		}
	}
	*demo_p += strlen (*((char **)demo_p)) + 1;
}
AGameplayCueNotify_Actor* UGameplayCueManager::GetInstancedCueActor(AActor* TargetActor, UClass* CueClass, const FGameplayCueParameters& Parameters)
{
	QUICK_SCOPE_CYCLE_COUNTER(STAT_GameplayCueManager_GetInstancedCueActor);


	// First, see if this actor already have a GameplayCueNotifyActor already going for this CueClass
	AGameplayCueNotify_Actor* CDO = Cast<AGameplayCueNotify_Actor>(CueClass->ClassDefaultObject);
	FGCNotifyActorKey	NotifyKey(TargetActor, CueClass, 
							CDO->bUniqueInstancePerInstigator ? Parameters.GetInstigator() : nullptr, 
							CDO->bUniqueInstancePerSourceObject ? Parameters.GetSourceObject() : nullptr);

	AGameplayCueNotify_Actor* SpawnedCue = nullptr;
	if (TWeakObjectPtr<AGameplayCueNotify_Actor>* WeakPtrPtr = NotifyMapActor.Find(NotifyKey))
	{		
		SpawnedCue = WeakPtrPtr->Get();
		// If the cue is scheduled to be destroyed, don't reuse it, create a new one instead
		if (SpawnedCue && SpawnedCue->GameplayCuePendingRemove() == false)
		{
			if (SpawnedCue->GetOwner() != TargetActor)
			{
				// This should not happen. This means we think we can recycle and GC actor that is currently being used by someone else.
				ABILITY_LOG(Warning, TEXT("GetInstancedCueActor attempting to reuse GC Actor with a different owner! %s (Target: %s). Using GC Actor: %s. Current Owner: %s"), *GetNameSafe(CueClass), *GetNameSafe(TargetActor), *GetNameSafe(SpawnedCue), *GetNameSafe(SpawnedCue->GetOwner()));
			}
			else
			{
				UE_CLOG((GameplayCueActorRecycleDebug>0), LogAbilitySystem, Display, TEXT("::GetInstancedCueActor Using Existing %s (Target: %s). Using GC Actor: %s"), *GetNameSafe(CueClass), *GetNameSafe(TargetActor), *GetNameSafe(SpawnedCue));
				return SpawnedCue;
			}
		}
	}

	// We don't have an instance for this, and we need one, so make one
	if (ensure(TargetActor) && ensure(CueClass))
	{
		AActor* NewOwnerActor = TargetActor;
#if WITH_EDITOR
		// Don't set owner if we are using fake CDO actor to do anim previewing
		NewOwnerActor= (TargetActor && TargetActor->HasAnyFlags(RF_ClassDefaultObject) == false ? TargetActor : nullptr);
#endif

		// Look to reuse an existing one that is stored on the CDO:
		if (GameplayCueActorRecycle > 0)
		{
			FPreallocationInfo& Info = GetPreallocationInfo(GetWorld());
			TArray<AGameplayCueNotify_Actor*>* PreallocatedList = Info.PreallocatedInstances.Find(CueClass);
			if (PreallocatedList && PreallocatedList->Num() > 0)
			{
				SpawnedCue = PreallocatedList->Pop(false);
				checkf(SpawnedCue && SpawnedCue->IsPendingKill() == false, TEXT("Spawned Cue is pending kill or null: %s"), *GetNameSafe(SpawnedCue));
				SpawnedCue->bInRecycleQueue = false;				
				SpawnedCue->SetActorHiddenInGame(false);
				SpawnedCue->SetOwner(NewOwnerActor);
				SpawnedCue->SetActorLocationAndRotation(TargetActor->GetActorLocation(), TargetActor->GetActorRotation());

				UE_CLOG((GameplayCueActorRecycleDebug>0), LogAbilitySystem, Display, TEXT("GetInstancedCueActor Popping Recycled %s (Target: %s). Using GC Actor: %s"), *GetNameSafe(CueClass), *GetNameSafe(TargetActor), *GetNameSafe(SpawnedCue));
#if WITH_EDITOR
				// let things know that we 'spawned'
				ISequenceRecorder& SequenceRecorder	= FModuleManager::LoadModuleChecked<ISequenceRecorder>("SequenceRecorder");
				SequenceRecorder.NotifyActorStartRecording(SpawnedCue);
#endif
			}
		}

		// If we can't reuse, then spawn a new one
		if (SpawnedCue == nullptr)
		{
			FActorSpawnParameters SpawnParams;
			SpawnParams.Owner = NewOwnerActor;
			if (SpawnedCue == nullptr)
			{
				if (LogGameplayCueActorSpawning)
				{
					ABILITY_LOG(Warning, TEXT("Spawning GameplaycueActor: %s"), *CueClass->GetName());
				}

				SpawnedCue = GetWorld()->SpawnActor<AGameplayCueNotify_Actor>(CueClass, TargetActor->GetActorLocation(), TargetActor->GetActorRotation(), SpawnParams);
			}
		}

		// Associate this GameplayCueNotifyActor with this target actor/key
		if (ensure(SpawnedCue))
		{
			SpawnedCue->NotifyKey = NotifyKey;
			NotifyMapActor.Add(NotifyKey, SpawnedCue);
		}
	}

	UE_CLOG((GameplayCueActorRecycleDebug>0), LogAbilitySystem, Display, TEXT("GetInstancedCueActor  Returning %s (Target: %s). Using GC Actor: %s"), *GetNameSafe(CueClass), *GetNameSafe(TargetActor), *GetNameSafe(SpawnedCue));
	return SpawnedCue;
}
Esempio n. 30
0
void UPaperTerrainComponent::OnSplineEdited()
{
	// Ensure we have the data structure for the desired collision method
	if (SpriteCollisionDomain == ESpriteCollisionMode::Use3DPhysics)
	{
		CachedBodySetup = NewObject<UBodySetup>(this);
	}
	else
	{
		CachedBodySetup = nullptr;
	}

	const float SlopeAnalysisTimeRate = 10.0f;
	const float FillRasterizationTimeRate = 100.0f;

	GeneratedSpriteGeometry.Empty();

	if ((AssociatedSpline != nullptr) && (TerrainMaterial != nullptr))
	{
		if (AssociatedSpline->ReparamStepsPerSegment != ReparamStepsPerSegment)
		{
			AssociatedSpline->ReparamStepsPerSegment = ReparamStepsPerSegment;
			AssociatedSpline->UpdateSpline();
		}

		FRandomStream RandomStream(RandomSeed);

		const FInterpCurveVector& SplineInfo = AssociatedSpline->SplineInfo;

		float SplineLength = AssociatedSpline->GetSplineLength();


		struct FTerrainRuleHelper
		{
		public:
			FTerrainRuleHelper(const FPaperTerrainMaterialRule* Rule)
				: StartWidth(0.0f)
				, EndWidth(0.0f)
			{
				for (const UPaperSprite* Sprite : Rule->Body)
				{
					if (Sprite != nullptr)
					{
						const float Width = GetSpriteRenderDataBounds2D(Sprite->BakedRenderData).GetSize().X;
						if (Width > 0.0f)
						{
							ValidBodies.Add(Sprite);
							ValidBodyWidths.Add(Width);
						}
					}
				}

				if (Rule->StartCap != nullptr)
				{
					const float Width = GetSpriteRenderDataBounds2D(Rule->StartCap->BakedRenderData).GetSize().X;
					if (Width > 0.0f)
					{
						StartWidth = Width;
					}
				}

				if (Rule->EndCap != nullptr)
				{
					const float Width = GetSpriteRenderDataBounds2D(Rule->EndCap->BakedRenderData).GetSize().X;
					if (Width > 0.0f)
					{
						EndWidth = Width;
					}
				}
			}

			float StartWidth;
			float EndWidth;

			TArray<const UPaperSprite*> ValidBodies;
			TArray<float> ValidBodyWidths;

			int32 GenerateBodyIndex(FRandomStream& InRandomStream) const
			{
				check(ValidBodies.Num() > 0);
				return InRandomStream.GetUnsignedInt() % ValidBodies.Num();
			}
		};

		// Split the spline into segments based on the slope rules in the material
		TArray<FTerrainSegment> Segments;

		FTerrainSegment* ActiveSegment = new (Segments) FTerrainSegment();
		ActiveSegment->StartTime = 0.0f;
		ActiveSegment->EndTime = SplineLength;

		{
			float CurrentTime = 0.0f;
			while (CurrentTime < SplineLength)
			{
				const FTransform Frame(GetTransformAtDistance(CurrentTime));
				const FVector UnitTangent = Frame.GetUnitAxis(EAxis::X);
				const float RawSlopeAngleRadians = FMath::Atan2(FVector::DotProduct(UnitTangent, PaperAxisY), FVector::DotProduct(UnitTangent, PaperAxisX));
				const float RawSlopeAngle = FMath::RadiansToDegrees(RawSlopeAngleRadians);
				const float SlopeAngle = FMath::Fmod(FMath::UnwindDegrees(RawSlopeAngle) + 360.0f, 360.0f);

				const FPaperTerrainMaterialRule* DesiredRule = (TerrainMaterial->Rules.Num() > 0) ? &(TerrainMaterial->Rules[0]) : nullptr;
				for (const FPaperTerrainMaterialRule& TestRule : TerrainMaterial->Rules)
				{
					if ((SlopeAngle >= TestRule.MinimumAngle) && (SlopeAngle < TestRule.MaximumAngle))
					{
						DesiredRule = &TestRule;
					}
				}

				if (ActiveSegment->Rule != DesiredRule)
				{
					if (ActiveSegment->Rule == nullptr)
					{
						ActiveSegment->Rule = DesiredRule;
					}
					else
					{
						ActiveSegment->EndTime = CurrentTime;
						
						// Segment is too small, delete it
						if (ActiveSegment->EndTime < ActiveSegment->StartTime + 2.0f * SegmentOverlapAmount)
						{
							Segments.Pop(false);
						}

						ActiveSegment = new (Segments)FTerrainSegment();
						ActiveSegment->StartTime = CurrentTime;
						ActiveSegment->EndTime = SplineLength;
						ActiveSegment->Rule = DesiredRule;
					}
				}

				CurrentTime += SlopeAnalysisTimeRate;
			}
		}

		// Account for overlap
		for (FTerrainSegment& Segment : Segments)
		{
			Segment.StartTime -= SegmentOverlapAmount;
			Segment.EndTime += SegmentOverlapAmount;
		}

		// Convert those segments to actual geometry
		for (FTerrainSegment& Segment : Segments)
		{
			check(Segment.Rule);
			FTerrainRuleHelper RuleHelper(Segment.Rule);

			float RemainingSegStart = Segment.StartTime + RuleHelper.StartWidth;
			float RemainingSegEnd = Segment.EndTime - RuleHelper.EndWidth;
			const float BodyDistance = RemainingSegEnd - RemainingSegStart;
			float DistanceBudget = BodyDistance;

			bool bUseBodySegments = (DistanceBudget > 0.0f) && (RuleHelper.ValidBodies.Num() > 0);

			// Add the start cap
			if (RuleHelper.StartWidth > 0.0f)
			{
				new (Segment.Stamps) FTerrainSpriteStamp(Segment.Rule->StartCap, Segment.StartTime + RuleHelper.StartWidth * 0.5f, /*bIsEndCap=*/ bUseBodySegments);
			}

			// Add body segments
			if (bUseBodySegments)
			{
				int32 NumSegments = 0;
				float Position = RemainingSegStart;
				
				while (DistanceBudget > 0.0f)
				{
					const int32 BodyIndex = RuleHelper.GenerateBodyIndex(RandomStream);
					const UPaperSprite* Sprite = RuleHelper.ValidBodies[BodyIndex];
					const float Width = RuleHelper.ValidBodyWidths[BodyIndex];

 					if ((NumSegments > 0) && ((Width * 0.5f) > DistanceBudget))
 					{
 						break;
 					}
					new (Segment.Stamps) FTerrainSpriteStamp(Sprite, Position + (Width * 0.5f), /*bIsEndCap=*/ false);

					DistanceBudget -= Width;
					Position += Width;
					++NumSegments;
				}

				const float UsedSpace = (BodyDistance - DistanceBudget);
				const float OverallScaleFactor = BodyDistance / UsedSpace;
				
 				// Stretch body segments
				float PositionCorrectionSum = 0.0f;
 				for (int32 Index = 0; Index < NumSegments; ++Index)
 				{
					FTerrainSpriteStamp& Stamp = Segment.Stamps[Index + (Segment.Stamps.Num() - NumSegments)];
					
					const float WidthChange = (OverallScaleFactor - 1.0f) * Stamp.NominalWidth;
					const float FirstGapIsSmallerFactor = (Index == 0) ? 0.5f : 1.0f;
					PositionCorrectionSum += WidthChange * FirstGapIsSmallerFactor;

					Stamp.Scale = OverallScaleFactor;
					Stamp.Time += PositionCorrectionSum;
 				}
			}
			else
			{
				// Stretch endcaps
			}

			// Add the end cap
			if (RuleHelper.EndWidth > 0.0f)
			{
				new (Segment.Stamps) FTerrainSpriteStamp(Segment.Rule->EndCap, Segment.EndTime - RuleHelper.EndWidth * 0.5f, /*bIsEndCap=*/ bUseBodySegments);
			}
		}

		// Convert stamps into geometry
		SpawnSegments(Segments, !bClosedSpline || (bClosedSpline && !bFilledSpline));

		// Generate the background if the spline is closed
		if (bClosedSpline && bFilledSpline)
		{
			// Create a polygon from the spline
			FBox2D SplineBounds(ForceInit);
			TArray<FVector2D> SplinePolyVertices2D;
			TArray<float> SplineEdgeOffsetAmounts;
			{
				float CurrentTime = 0.0f;
				while (CurrentTime < SplineLength)
				{
					const float Param = AssociatedSpline->SplineReparamTable.Eval(CurrentTime, 0.0f);
					const FVector Position3D = AssociatedSpline->SplineInfo.Eval(Param, FVector::ZeroVector);
					const FVector2D Position2D = FVector2D(FVector::DotProduct(Position3D, PaperAxisX), FVector::DotProduct(Position3D, PaperAxisY));

					SplineBounds += Position2D;
					SplinePolyVertices2D.Add(Position2D);

					// Find the collision offset for this sample point
					float CollisionOffset = 0;
					for (int SegmentIndex = 0; SegmentIndex < Segments.Num(); ++SegmentIndex)
					{
						FTerrainSegment& Segment = Segments[SegmentIndex];
						if (CurrentTime >= Segment.StartTime && CurrentTime <= Segment.EndTime)
						{
							CollisionOffset = (Segment.Rule != nullptr) ? (Segment.Rule->CollisionOffset * 0.25f) : 0;
							break;
						}
					}
					SplineEdgeOffsetAmounts.Add(CollisionOffset);

					CurrentTime += FillRasterizationTimeRate;
				}
			}

			SimplifyPolygon(SplinePolyVertices2D, SplineEdgeOffsetAmounts);

			// Always CCW and facing forward regardless of spline winding
			TArray<FVector2D> CorrectedSplineVertices;
			PaperGeomTools::CorrectPolygonWinding(CorrectedSplineVertices, SplinePolyVertices2D, false);

			TArray<FVector2D> TriangulatedPolygonVertices;
			PaperGeomTools::TriangulatePoly(/*out*/TriangulatedPolygonVertices, CorrectedSplineVertices, false);

			GenerateCollisionDataFromPolygon(SplinePolyVertices2D, SplineEdgeOffsetAmounts, TriangulatedPolygonVertices);

			if (TerrainMaterial->InteriorFill != nullptr)
			{
				const UPaperSprite* FillSprite = TerrainMaterial->InteriorFill;
				FPaperTerrainSpriteGeometry& MaterialBatch = *new (GeneratedSpriteGeometry)FPaperTerrainSpriteGeometry(); //@TODO: Look up the existing one instead
				MaterialBatch.Material = FillSprite->GetDefaultMaterial();

				FSpriteDrawCallRecord& FillDrawCall = *new (MaterialBatch.Records) FSpriteDrawCallRecord();
				FillDrawCall.BuildFromSprite(FillSprite);
				FillDrawCall.RenderVerts.Empty();
				FillDrawCall.Color = TerrainColor;
				FillDrawCall.Destination = PaperAxisZ * 0.1f;

				const FVector2D TextureSize = GetSpriteRenderDataBounds2D(FillSprite->BakedRenderData).GetSize();
				const FVector2D SplineSize = SplineBounds.GetSize();

				GenerateFillRenderDataFromPolygon(FillSprite, FillDrawCall, TextureSize, TriangulatedPolygonVertices);

				//@TODO: Add support for the fill sprite being smaller than the entire texture
#if NOT_WORKING
				const float StartingDivisionPointX = FMath::CeilToFloat(SplineBounds.Min.X / TextureSize.X);
				const float StartingDivisionPointY = FMath::CeilToFloat(SplineBounds.Min.Y / TextureSize.Y);

				FPoly VerticalRemainder = SplineAsPolygon;
				for (float Y = StartingDivisionPointY; VerticalRemainder.Vertices.Num() > 0; Y += TextureSize.Y)
				{
					FPoly Top;
					FPoly Bottom;
					const FVector SplitBaseOuter = (Y * PaperAxisY);
					VerticalRemainder.SplitWithPlane(SplitBaseOuter, -PaperAxisY, &Top, &Bottom, 1);
					VerticalRemainder = Bottom;

					FPoly HorizontalRemainder = Top;
					for (float X = StartingDivisionPointX; HorizontalRemainder.Vertices.Num() > 0; X += TextureSize.X)
					{
						FPoly Left;
						FPoly Right;
						const FVector SplitBaseInner = (X * PaperAxisX) + (Y * PaperAxisY);
						HorizontalRemainder.SplitWithPlane(SplitBaseInner, -PaperAxisX, &Left, &Right, 1);
						HorizontalRemainder = Right;

						//BROKEN, function no longer exists (split into 2 parts)
						SpawnFromPoly(Segments, SplineEdgeOffsetAmounts, FillSprite, FillDrawCall, TextureSize, Left);
					}
				}
#endif
			}
		}

		// Draw debug frames at the start and end of the spline
#if PAPER_TERRAIN_DRAW_DEBUG
		{
			const float Time = 5.0f;
			{
				FTransform WorldTransform = GetTransformAtDistance(0.0f) * ComponentToWorld;
				DrawDebugCoordinateSystem(GetWorld(), WorldTransform.GetLocation(), FRotator(WorldTransform.GetRotation()), 30.0f, true, Time, SDPG_Foreground);
			}
			{
				FTransform WorldTransform = GetTransformAtDistance(SplineLength) * ComponentToWorld;
				DrawDebugCoordinateSystem(GetWorld(), WorldTransform.GetLocation(), FRotator(WorldTransform.GetRotation()), 30.0f, true, Time, SDPG_Foreground);
			}
		}
#endif
	}

	RecreateRenderState_Concurrent();
}