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(); }
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; }
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; }
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; }
void ASpellDeck::PutOnTop(TArray<UActionCard*> cardsToPut_p) { for (auto card : cardsToPut_p) { spellDeck.Push(cardsToPut_p.Pop()); } }
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; }
TArray<FChunkPart> FDataStructure::GetFinalDataStructure() { if (DataStructure.Top().PartSize == 0) { DataStructure.Pop(false); } return MoveTemp(DataStructure); }
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; }
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") ); } } } }
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(); } }
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; } }
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; }
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; }
/** 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 ); } } } } }
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!")); } } } }
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); }
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); }
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; }
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; }
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(); }