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);
		}
	}	
}
Exemplo n.º 2
0
	FORCEINLINE static FBoxCenterAndExtent GetBoundingBox(const float& Element)
	{
		return FBoxCenterAndExtent(FVector4(0), FVector4(Element));
	}
Exemplo n.º 3
0
	/**
	 * Get the bounding box of the provided octree element. In this case, the box
	 * is merely the point specified by the element.
	 *
	 * @param	Element	Octree element to get the bounding box for
	 *
	 * @return	Bounding box of the provided octree element
	 */
	FORCEINLINE static FBoxCenterAndExtent GetBoundingBox( const FPaintedVertex& Element )
	{
		return FBoxCenterAndExtent( Element.Position, FVector::ZeroVector );
	}
Exemplo n.º 4
0
void RemapPaintedVertexColors(
	const TArray<FPaintedVertex>& InPaintedVertices,
	const FColorVertexBuffer& InOverrideColors,
	const FPositionVertexBuffer& NewPositions,
	const FStaticMeshVertexBuffer* OptionalVertexBuffer,
	TArray<FColor>& OutOverrideColors
	)
{

	// Local copy of painted vertices we can scratch on.
	TArray<FPaintedVertex> PaintedVertices(InPaintedVertices);

	// Find the extents formed by the cached vertex positions in order to optimize the octree used later
	FVector MinExtents( PaintedVertices[ 0 ].Position );
	FVector MaxExtents( PaintedVertices[ 0 ].Position );

	for (int32 VertIndex = 0; VertIndex < PaintedVertices.Num(); ++VertIndex)
	{
		FVector& CurVector = PaintedVertices[ VertIndex ].Position;

		MinExtents.X = FMath::Min<float>( MinExtents.X, CurVector.X );
		MinExtents.Y = FMath::Min<float>( MinExtents.Y, CurVector.Y );
		MinExtents.Z = FMath::Min<float>( MinExtents.Z, CurVector.Z );

		MaxExtents.X = FMath::Max<float>( MaxExtents.X, CurVector.X );
		MaxExtents.Y = FMath::Max<float>( MaxExtents.Y, CurVector.Y );
		MaxExtents.Z = FMath::Max<float>( MaxExtents.Z, CurVector.Z );
	}

	// Create an octree which spans the extreme extents of the old and new vertex positions in order to quickly query for the colors
	// of the new vertex positions
	for (int32 VertIndex = 0; VertIndex < (int32)NewPositions.GetNumVertices(); ++VertIndex)
	{
		FVector CurVector = NewPositions.VertexPosition(VertIndex);

		MinExtents.X = FMath::Min<float>( MinExtents.X, CurVector.X );
		MinExtents.Y = FMath::Min<float>( MinExtents.Y, CurVector.Y );
		MinExtents.Z = FMath::Min<float>( MinExtents.Z, CurVector.Z );

		MaxExtents.X = FMath::Max<float>( MaxExtents.X, CurVector.X );
		MaxExtents.Y = FMath::Max<float>( MaxExtents.Y, CurVector.Y );
		MaxExtents.Z = FMath::Max<float>( MaxExtents.Z, CurVector.Z );
	}

	FBox Bounds( MinExtents, MaxExtents );
	TSMCVertPosOctree VertPosOctree( Bounds.GetCenter(), Bounds.GetExtent().GetMax() );

	// Add each old vertex to the octree
	for ( int32 PaintedVertexIndex = 0; PaintedVertexIndex < PaintedVertices.Num(); ++PaintedVertexIndex )
	{
		VertPosOctree.AddElement( PaintedVertices[ PaintedVertexIndex ] );
	}


	// Iterate over each new vertex position, attempting to find the old vertex it is closest to, applying
	// the color of the old vertex to the new position if possible.
	OutOverrideColors.Empty(NewPositions.GetNumVertices());
	const float DistanceOverNormalThreshold = OptionalVertexBuffer ? KINDA_SMALL_NUMBER : 0.0f;
	for ( uint32 NewVertIndex = 0; NewVertIndex < NewPositions.GetNumVertices(); ++NewVertIndex )
	{
		TArray<FPaintedVertex> PointsToConsider;
		TSMCVertPosOctree::TConstIterator<> OctreeIter( VertPosOctree );
		const FVector& CurPosition = NewPositions.VertexPosition( NewVertIndex );
		FVector CurNormal = FVector::ZeroVector;
		if (OptionalVertexBuffer)
		{
			CurNormal = OptionalVertexBuffer->VertexTangentZ( NewVertIndex );
		}

		// Iterate through the octree attempting to find the vertices closest to the current new point
		while ( OctreeIter.HasPendingNodes() )
		{
			const TSMCVertPosOctree::FNode& CurNode = OctreeIter.GetCurrentNode();
			const FOctreeNodeContext& CurContext = OctreeIter.GetCurrentContext();

			// Find the child of the current node, if any, that contains the current new point
			FOctreeChildNodeRef ChildRef = CurContext.GetContainingChild( FBoxCenterAndExtent( CurPosition, FVector::ZeroVector ) );

			if ( !ChildRef.IsNULL() )
			{
				const TSMCVertPosOctree::FNode* ChildNode = CurNode.GetChild( ChildRef );

				// If the specified child node exists and contains any of the old vertices, push it to the iterator for future consideration
				if ( ChildNode && ChildNode->GetInclusiveElementCount() > 0 )
				{
					OctreeIter.PushChild( ChildRef );
				}
				// If the child node doesn't have any of the old vertices in it, it's not worth pursuing any further. In an attempt to find
				// anything to match vs. the new point, add all of the children of the current octree node that have old points in them to the
				// iterator for future consideration.
				else
				{
					FOREACH_OCTREE_CHILD_NODE( ChildRef )
					{
						if( CurNode.HasChild( ChildRef ) && CurNode.GetChild( ChildRef )->GetInclusiveElementCount() > 0 )
						{
							OctreeIter.PushChild( ChildRef );
						}
					}
				}
			}

			// Add all of the elements in the current node to the list of points to consider for closest point calculations
			PointsToConsider.Append( CurNode.GetElements() );
			OctreeIter.Advance();
		}

		// If any points to consider were found, iterate over each and find which one is the closest to the new point 
		if ( PointsToConsider.Num() > 0 )
		{
			FPaintedVertex BestVertex = PointsToConsider[0];
			float BestDistanceSquared = ( BestVertex.Position - CurPosition ).SizeSquared();
			float BestNormalDot = FVector( BestVertex.Normal ) | CurNormal;

			for ( int32 ConsiderationIndex = 1; ConsiderationIndex < PointsToConsider.Num(); ++ConsiderationIndex )
			{
				FPaintedVertex& Vertex = PointsToConsider[ ConsiderationIndex ];
				const float DistSqrd = ( Vertex.Position - CurPosition ).SizeSquared();
				const float NormalDot = FVector( Vertex.Normal ) | CurNormal;
				if ( DistSqrd < BestDistanceSquared - DistanceOverNormalThreshold )
				{
					BestVertex = Vertex;
					BestDistanceSquared = DistSqrd;
					BestNormalDot = NormalDot;
				}
				else if ( OptionalVertexBuffer && DistSqrd < BestDistanceSquared + DistanceOverNormalThreshold && NormalDot > BestNormalDot )
				{
					BestVertex = Vertex;
					BestDistanceSquared = DistSqrd;
					BestNormalDot = NormalDot;
				}
			}

			OutOverrideColors.Add(BestVertex.Color);
		}
	}
}
// Called every frame
void ASpacePartioner::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

	//check(bInitialized);
	//check(bDrawDebugInfo);

	if (bInitialized && bDrawDebugInfo)
	{

		int level = 0;
		float max;
		float offsetMax;
		float offset;
		FVector maxExtent;
		FVector center;
		FVector tempForCoercion;
		FBoxCenterAndExtent OldBounds = FBoxCenterAndExtent();

		int nodeCount = 0;
		int elementCount = 0;

		// Go through the nodes of the octree
		for (FSimpleOctree::TConstIterator<> NodeIt(*OctreeData); NodeIt.HasPendingNodes(); NodeIt.Advance())
		{
			const FSimpleOctree::FNode& CurrentNode = NodeIt.GetCurrentNode();
			const FOctreeNodeContext& CurrentContext = NodeIt.GetCurrentContext();
			const FBoxCenterAndExtent& CurrentBounds = CurrentContext.Bounds;

			nodeCount++;

			FOREACH_OCTREE_CHILD_NODE(ChildRef)
			{
				if (CurrentNode.HasChild(ChildRef))
				{
					NodeIt.PushChild(ChildRef);
				}
			}

			// If the extents have changed then we have moved a level.
			if (!OldBounds.Extent.Equals(CurrentBounds.Extent))
			{
				level++;
			}
			OldBounds = CurrentBounds;

			// UE_LOG(LogTemp, Log, TEXT("Level: %d"), level);

			// Draw Node Bounds
			tempForCoercion = CurrentBounds.Extent;
			max = tempForCoercion.GetMax();
			center = CurrentBounds.Center;

			// UE_LOG(LogTemp, Log, TEXT("center before: %s"), *center.ToString());
			
			// To understand the math here check out the constructors in FOctreeNodeContext
			// Offset nodes that are not the root bounds
			if (!OctreeData->GetRootBounds().Extent.Equals(CurrentBounds.Extent))
			{
				for (int i = 1; i < level; i++)
				{
					// Calculate offset
					offsetMax = max / (1.0f + (1.0f / FOctreeNodeContext::LoosenessDenominator));
					offset = max - offsetMax;
					max = offsetMax;
				
					// Calculate Center Offset
					if (center.X > 0)
					{
						center.X = center.X + offset;
					}
					else
					{
						center.X = center.X - offset;
					}

					if (center.Y > 0)
					{
						center.Y = center.Y + offset;
					}
					else
					{
						center.Y = center.Y - offset;
					}

					if (center.Z > 0)
					{
						center.Z = center.Z + offset;
					}
					else
					{
						center.Z = center.Z - offset;
					}
				}
			}

			UE_LOG(LogTemp, Log, TEXT("max: %f"), max);
			// UE_LOG(LogTemp, Log, TEXT("center of nodes: %s"), *center.ToString());

			maxExtent = FVector(max, max, max);
			

			// UE_LOG(LogTemp, Log, TEXT("Extent of nodes: %s"), *tempForCoercion.ToString());

			DrawDebugBox(GetWorld(), center, maxExtent, FColor().Blue, false, 0.0f);
			DrawDebugSphere(GetWorld(), center + maxExtent, 4.0f, 12, FColor().Green, false, 0.0f);
			DrawDebugSphere(GetWorld(), center - maxExtent, 4.0f, 12, FColor().Red, false, 0.0f);

			for (FSimpleOctree::ElementConstIt ElementIt(CurrentNode.GetElementIt()); ElementIt; ++ElementIt)
			{
				const FOctreeElement& Sample = *ElementIt;

				// Draw debug boxes around elements
				max = Sample.BoxSphereBounds.BoxExtent.GetMax();
				maxExtent = FVector(max, max, max);
				center = Sample.MyActor->GetActorLocation();

				DrawDebugBox(GetWorld(), center, maxExtent, FColor().Blue, false, 0.0f);
				DrawDebugSphere(GetWorld(), center + maxExtent, 4.0f, 12, FColor().White, false, 0.0f);
				DrawDebugSphere(GetWorld(), center - maxExtent, 4.0f, 12, FColor().White, false, 0.0f);
				elementCount++;
			}
		}
		// UE_LOG(LogTemp, Log, TEXT("Node Count: %d, Element Count: %d"), nodeCount, elementCount);
	}