void FogWorker::FloodFill(int32 x, int32 y) { if(x < 0 || x >= TextureSize || y < 0 || y >= TextureSize) { return; } // Wikipedia // Flood-fill (node, target-color, replacement-color): // 1. If target - color is equal to replacement - color, return. // 2. If color of node is not equal to target - color, return. if(FMath::IsNearlyEqual(UnfoggedData[x + y * TextureSize], 1.0f)) { return; } // 3. Set Q to the empty queue. TQueue<FIntVector> Q; // 4. Add node to Q. Q.Enqueue(FIntVector(x, y, 0)); FIntVector N; // 5. For each element N of Q : while(Q.Dequeue(N)) { // 6. Set w and e equal to N. auto w = N, e = N; // 7. Move w to the west until the color of the node to the west of w no longer matches target - color. while(w.X - 1 > 0 && !FMath::IsNearlyEqual(UnfoggedData[w.X - 1 + w.Y * TextureSize], 1.0f)) { w.X--; } // 8. Move e to the east until the color of the node to the east of e no longer matches target - color. while(e.X + 1 < TextureSize && !FMath::IsNearlyEqual(UnfoggedData[e.X + 1 + e.Y * TextureSize], 1.0f)) { e.X++; } // 9. For each node n between w and e : for(auto i = w.X; i <= e.X; ++i) { FIntVector n(i, N.Y, 0); // 10. Set the color of n to replacement - color. UnfoggedData[n.X + n.Y * TextureSize] = 1.0f; // 11. If the color of the node to the north of n is target - color, add that node to Q. if(n.Y + 1 < TextureSize && !FMath::IsNearlyEqual(UnfoggedData[n.X + (n.Y + 1) * TextureSize], 1.0f)) Q.Enqueue(FIntVector(n.X, n.Y + 1, 0)); // 12. If the color of the node to the south of n is target - color, add that node to Q. if(n.Y - 1 > 0 && !FMath::IsNearlyEqual(UnfoggedData[n.X + (n.Y - 1) * TextureSize], 1.0f)) { Q.Enqueue(FIntVector(n.X, n.Y - 1, 0)); } } // 13. Continue looping until Q is exhausted. } // 14. Return. }
FIntVector UWorldComposition::GetLevelOffset(ULevel* InLevel) const { UWorld* OwningWorld = GetWorld(); UPackage* LevelPackage = Cast<UPackage>(InLevel->GetOutermost()); FIntVector LevelPosition = FIntVector::ZeroValue; if (LevelPackage->WorldTileInfo) { FIntPoint AbsolutePosition = LevelPackage->WorldTileInfo->AbsolutePosition; LevelPosition = FIntVector(AbsolutePosition.X, AbsolutePosition.Y, 0); } return LevelPosition - OwningWorld->OriginLocation; }
bool FIndirectLightingCache::AllocateBlock(int32 Size, FIntVector& OutMin) { if (Size == 1) { // Assign a min that won't overlap with any of the samples allocated from the volume texture, so we can be added to VolumeBlocks without collisions // This min is not used for anything else for point samples OutMin = FIntVector(NextPointId, 0, 0); NextPointId++; // Point samples don't go through the volume texture, allocation always succeeds return true; } else { return BlockAllocator.AddElement((uint32&)OutMin.X, (uint32&)OutMin.Y, (uint32&)OutMin.Z, Size, Size, Size); } }
void UWorldComposition::EvaluateWorldOriginLocation(const FVector& ViewLocation) { UWorld* OwningWorld = GetWorld(); FVector Location = ViewLocation; if (!bRebaseOriginIn3DSpace) { // Consider only XY plane Location.Z = 0.f; } // Request to shift world in case current view is quite far from current origin if (Location.SizeSquared() > FMath::Square(RebaseOriginDistance)) { OwningWorld->RequestNewWorldOrigin(FIntVector(Location.X, Location.Y, Location.Z) + OwningWorld->OriginLocation); } }
void UCheatManager::SetWorldOrigin() { UWorld* World = GetWorld(); check(World); APlayerController* const MyPlayerController = GetOuterAPlayerController(); FVector ViewLocation; FRotator ViewRotation; MyPlayerController->GetPlayerViewPoint( ViewLocation, ViewRotation ); if( MyPlayerController->GetPawn() != NULL ) { ViewLocation = MyPlayerController->GetPawn()->GetActorLocation(); } // Consider only XY plane ViewLocation.Z = 0; FIntVector NewOrigin = FIntVector(ViewLocation.X, ViewLocation.Y, ViewLocation.Z) + World->OriginLocation; World->RequestNewWorldOrigin(NewOrigin); }
bool UVoxelComponent::IsUnbeheldVolume(const FIntVector& InVector) const { static const TArray<FIntVector> Direction = TArrayBuilder<FIntVector>() .Add(FIntVector(+0, +0, +1)) // Up .Add(FIntVector(+0, +0, -1)) // Down .Add(FIntVector(+1, +0, +0)) // Forward .Add(FIntVector(-1, +0, +0)) // Backward .Add(FIntVector(+0, +1, +0)) // Right .Add(FIntVector(+0, -1, +0)); // Left int count = 0; for (int i = 0; i < Direction.Num(); ++i) { if (INDEX_NONE != Voxel->Voxel.IndexOfByKey(InVector + Direction[i])) { ++count; } } return Direction.Num() == count; }
void FIndirectLightingCache::InterpolateBlock( FScene* Scene, const FIndirectLightingCacheBlock& Block, TArray<float>& AccumulatedWeight, TArray<FSHVectorRGB2>& AccumulatedIncidentRadiance) { const FBoxCenterAndExtent BlockBoundingBox(Block.Min + Block.Size / 2, Block.Size / 2); const FVector HalfTexelWorldOffset = BlockBoundingBox.Extent / FVector(Block.TexelSize); if (GCacheLimitQuerySize && Block.TexelSize > 2) { for (int32 VolumeIndex = 0; VolumeIndex < Scene->PrecomputedLightVolumes.Num(); VolumeIndex++) { const FPrecomputedLightVolume* PrecomputedLightVolume = Scene->PrecomputedLightVolumes[VolumeIndex]; // Compute the target query size // We will try to split up the allocation into groups that are smaller than this before querying the octree // This prevents very large objects from finding all the samples in the level in their octree search const float WorldTargetSize = PrecomputedLightVolume->GetNodeLevelExtent(GCacheQueryNodeLevel) * 2; const FVector WorldCellSize = Block.Size / FVector(Block.TexelSize); // Number of cells to increment by for query blocks FIntVector NumStepCells; NumStepCells.X = FMath::Max(1, FMath::FloorToInt(WorldTargetSize / WorldCellSize.X)); NumStepCells.Y = FMath::Max(1, FMath::FloorToInt(WorldTargetSize / WorldCellSize.Y)); NumStepCells.Z = FMath::Max(1, FMath::FloorToInt(WorldTargetSize / WorldCellSize.Z)); FIntVector NumQueryStepCells(0, 0, 0); // World space size to increment by for query blocks const FVector WorldStepSize = FVector(NumStepCells) * WorldCellSize; FVector QueryWorldStepSize(0, 0, 0); check(NumStepCells.X > 0 && NumStepCells.Y > 0 && NumStepCells.Z > 0); // This will track the position in cells of the query block being built FIntVector CellIndex(0, 0, 0); // This will track the min world position of the query block being built FVector MinPosition = Block.Min; for (MinPosition.Z = Block.Min.Z, CellIndex.Z = 0; CellIndex.Z < Block.TexelSize; MinPosition.Z += WorldStepSize.Z, CellIndex.Z += NumStepCells.Z) { QueryWorldStepSize.Z = WorldStepSize.Z; NumQueryStepCells.Z = NumStepCells.Z; // If this is the last query block in this dimension, adjust both the world space and cell sizes to match if (CellIndex.Z + NumStepCells.Z > Block.TexelSize) { QueryWorldStepSize.Z = Block.Min.Z + Block.Size.Z - MinPosition.Z; NumQueryStepCells.Z = Block.TexelSize - CellIndex.Z; } for (MinPosition.Y = Block.Min.Y, CellIndex.Y = 0; CellIndex.Y < Block.TexelSize; MinPosition.Y += WorldStepSize.Y, CellIndex.Y += NumStepCells.Y) { QueryWorldStepSize.Y = WorldStepSize.Y; NumQueryStepCells.Y = NumStepCells.Y; if (CellIndex.Y + NumStepCells.Y > Block.TexelSize) { QueryWorldStepSize.Y = Block.Min.Y + Block.Size.Y - MinPosition.Y; NumQueryStepCells.Y = Block.TexelSize - CellIndex.Y; } for (MinPosition.X = Block.Min.X, CellIndex.X = 0; CellIndex.X < Block.TexelSize; MinPosition.X += WorldStepSize.X, CellIndex.X += NumStepCells.X) { QueryWorldStepSize.X = WorldStepSize.X; NumQueryStepCells.X = NumStepCells.X; if (CellIndex.X + NumStepCells.X > Block.TexelSize) { QueryWorldStepSize.X = Block.Min.X + Block.Size.X - MinPosition.X; NumQueryStepCells.X = Block.TexelSize - CellIndex.X; } FVector BoxExtent = QueryWorldStepSize / 2; // Use a 0 query extent in dimensions that only have one cell, these become point queries BoxExtent.X = NumQueryStepCells.X == 1 ? 0 : BoxExtent.X; BoxExtent.Y = NumQueryStepCells.Y == 1 ? 0 : BoxExtent.Y; BoxExtent.Z = NumQueryStepCells.Z == 1 ? 0 : BoxExtent.Z; // Build a bounding box for the query block const FBoxCenterAndExtent BoundingBox(MinPosition + BoxExtent + HalfTexelWorldOffset, BoxExtent); checkSlow(CellIndex.X < Block.TexelSize && CellIndex.Y < Block.TexelSize && CellIndex.Z < Block.TexelSize); checkSlow(CellIndex.X + NumQueryStepCells.X <= Block.TexelSize && CellIndex.Y + NumQueryStepCells.Y <= Block.TexelSize && CellIndex.Z + NumQueryStepCells.Z <= Block.TexelSize); // Interpolate from the SH volume lighting samples that Lightmass computed PrecomputedLightVolume->InterpolateIncidentRadianceBlock( BoundingBox, NumQueryStepCells, FIntVector(Block.TexelSize), CellIndex, AccumulatedWeight, AccumulatedIncidentRadiance); } } } } } else { for (int32 VolumeIndex = 0; VolumeIndex < Scene->PrecomputedLightVolumes.Num(); VolumeIndex++) { const FPrecomputedLightVolume* PrecomputedLightVolume = Scene->PrecomputedLightVolumes[VolumeIndex]; check(PrecomputedLightVolume); check(PrecomputedLightVolume->IsUsingHighQualityLightMap() == AllowHighQualityLightmaps(Scene->GetFeatureLevel())); // Interpolate from the SH volume lighting samples that Lightmass computed // Query using the bounds of all the samples in this block // There will be a performance cliff for large objects which end up intersecting with the entire octree PrecomputedLightVolume->InterpolateIncidentRadianceBlock( FBoxCenterAndExtent(BlockBoundingBox.Center + HalfTexelWorldOffset, BlockBoundingBox.Extent), FIntVector(Block.TexelSize), FIntVector(Block.TexelSize), FIntVector(0), AccumulatedWeight, AccumulatedIncidentRadiance); } } }