Files
UnrealEngine/Engine/Source/Programs/UnrealLightmass/Private/Lighting/Collision.cpp
2025-05-18 13:04:45 +08:00

545 lines
23 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Collision.h"
#include "LightingSystem.h"
#include "UnrealLightmass.h"
namespace Lightmass
{
FStaticLightingAggregateMesh::FStaticLightingAggregateMesh(const FScene& InScene):
Scene(InScene),
bHasShadowCastingPrimitives(false),
SceneBounds(ForceInit),
SceneSurfaceArea(0),
SceneSurfaceAreaWithinImportanceVolume(0)
{
}
FBox3f FStaticLightingAggregateMesh::GetBounds() const
{
// Expand the bounds slightly to avoid having to handle geometry that is exactly on the bounding box,
// Which happens if you create a new level in Unreal with BSP from the default builder brush.
return bHasShadowCastingPrimitives ? SceneBounds.ExpandBy(5.0f * Scene.SceneConstants.StaticLightingLevelScale) : FBox3f(FVector4f(0,0,0), FVector4f(0,0,0));
}
FDefaultAggregateMesh::~FDefaultAggregateMesh()
{
for (int32 i = 0; i < MeshInfos.Num(); i++)
{
delete MeshInfos[i];
}
}
/**
* Merges a mesh into the shadow mesh.
* @param Mesh - The mesh the triangle comes from.
*/
void FDefaultAggregateMesh::AddMesh(const FStaticLightingMesh* Mesh, const FStaticLightingMapping* Mapping)
{
// Only use shadow casting meshes.
if( Mesh->LightingFlags&GI_INSTANCE_CASTSHADOW )
{
SceneBounds = SceneBounds + Mesh->BoundingBox;
const FStaticLightingTextureMapping* TextureMapping = Mapping ? Mapping->GetTextureMapping() : NULL;
const int32 BaseVertexIndex = Vertices.Num();
MeshInfos.Add(new FStaticLightingMeshInfo(BaseVertexIndex, Mesh));
Vertices.AddUninitialized(Mesh->NumVertices);
UVs.AddZeroed(Mesh->NumVertices);
LightmapUVs.AddZeroed(Mesh->NumVertices);
const uint32 MeshLODIndices = Mesh->GetLODIndices();
const uint32 MeshHLODRange = Mesh->GetHLODRange();
const FBoxSphereBounds3f ImportanceBounds = Scene.GetImportanceBounds();
for(int32 TriangleIndex = 0;TriangleIndex < Mesh->NumTriangles;TriangleIndex++)
{
// Read the triangle from the mesh.
FStaticLightingVertex V0;
FStaticLightingVertex V1;
FStaticLightingVertex V2;
int32 ElementIndex;
Mesh->GetTriangle(TriangleIndex,V0,V1,V2,ElementIndex);
int32 I0 = 0;
int32 I1 = 0;
int32 I2 = 0;
Mesh->GetTriangleIndices(TriangleIndex,I0,I1,I2);
check(I0 <= Mesh->NumVertices && I1 <= Mesh->NumVertices && I2 <= Mesh->NumVertices);
const bool bTwoSided = Mesh->IsTwoSided(ElementIndex) || Mesh->IsCastingShadowAsTwoSided();
const bool bStaticAndOpaque = !Mesh->IsMasked(ElementIndex) && !Mesh->IsTranslucent(ElementIndex) && !Mesh->bMovable;
Vertices[BaseVertexIndex + I0] = V0.WorldPosition;
Vertices[BaseVertexIndex + I1] = V1.WorldPosition;
Vertices[BaseVertexIndex + I2] = V2.WorldPosition;
UVs[BaseVertexIndex + I0] = V0.TextureCoordinates[Mesh->TextureCoordinateIndex];
UVs[BaseVertexIndex + I1] = V1.TextureCoordinates[Mesh->TextureCoordinateIndex];
UVs[BaseVertexIndex + I2] = V2.TextureCoordinates[Mesh->TextureCoordinateIndex];
if (TextureMapping)
{
LightmapUVs[BaseVertexIndex + I0] = V0.TextureCoordinates[TextureMapping->LightmapTextureCoordinateIndex];
LightmapUVs[BaseVertexIndex + I1] = V1.TextureCoordinates[TextureMapping->LightmapTextureCoordinateIndex];
LightmapUVs[BaseVertexIndex + I2] = V2.TextureCoordinates[TextureMapping->LightmapTextureCoordinateIndex];
}
// Compute the triangle's normal.
const FVector4f TriangleNormal = (V2.WorldPosition - V0.WorldPosition) ^ (V1.WorldPosition - V0.WorldPosition);
// Compute the triangle area.
const float TriangleArea = TriangleNormal.Size3() * 0.5f;
// Ignore zero area triangles.
if( TriangleArea > TRIANGLE_AREA_THRESHOLD && Mesh->IsElementCastingShadow(ElementIndex))
{
const int32 PayloadIndex = TrianglePayloads.Add(
FTriangleSOAPayload(MeshInfos.Last(), Mapping, ElementIndex, BaseVertexIndex + I0, BaseVertexIndex + I1, BaseVertexIndex + I2));
bHasShadowCastingPrimitives = true;
new(kDOPTriangles) FkDOPBuildCollisionTriangle<uint32>(
PayloadIndex, // Use the triangle's material index as an index into TrianglePayloads.
V0.WorldPosition,V1.WorldPosition,V2.WorldPosition,
Mesh->MeshIndex,
MeshLODIndices,
MeshHLODRange,
bTwoSided,
bStaticAndOpaque
);
}
// Sum the total triangle area of everything in the aggregate mesh
SceneSurfaceArea += TriangleArea;
// Sum the total triangle area of everything in the aggregate mesh within the importance volume,
// if any vertex is contained or if there is no importance volume at all
if( ImportanceBounds.SphereRadius < DELTA ||
ImportanceBounds.GetBox().IsInside( V0.WorldPosition ) ||
ImportanceBounds.GetBox().IsInside( V1.WorldPosition ) ||
ImportanceBounds.GetBox().IsInside( V2.WorldPosition ) )
{
SceneSurfaceAreaWithinImportanceVolume += TriangleArea;
}
}
}
}
void FDefaultAggregateMesh::AddMeshForVoxelization(const FStaticLightingMesh* Mesh, const FStaticLightingMapping* Mapping, bool bUseForInstancing)
{
if (bUseForInstancing)
{
check(Mesh->GetInstanceableStaticMesh());
}
if (Mesh->LightingFlags & GI_INSTANCE_CASTSHADOW && Mesh->DoesMeshBelongToLOD0())
{
SceneBounds = SceneBounds + Mesh->BoundingBox;
const FStaticLightingTextureMapping* TextureMapping = Mapping ? Mapping->GetTextureMapping() : NULL;
const int32 BaseVertexIndex = Vertices.Num();
MeshInfos.Add(new FStaticLightingMeshInfo(BaseVertexIndex, Mesh));
Vertices.AddUninitialized(Mesh->NumVertices);
UVs.AddZeroed(Mesh->NumVertices);
LightmapUVs.AddZeroed(Mesh->NumVertices);
const uint32 MeshLODIndices = Mesh->GetLODIndices();
const uint32 MeshHLODRange = Mesh->GetHLODRange();
for (int32 TriangleIndex = 0; TriangleIndex < Mesh->NumTriangles; TriangleIndex++)
{
// Read the triangle from the mesh.
int32 I0 = 0;
int32 I1 = 0;
int32 I2 = 0;
FStaticLightingVertex V0;
FStaticLightingVertex V1;
FStaticLightingVertex V2;
int32 ElementIndex;
if (bUseForInstancing)
{
Mesh->GetInstanceableStaticMesh()->GetNonTransformedTriangleIndices(TriangleIndex, I0, I1, I2);
Mesh->GetInstanceableStaticMesh()->GetNonTransformedTriangle(TriangleIndex, V0, V1, V2, ElementIndex);
}
else
{
Mesh->GetTriangleIndices(TriangleIndex, I0, I1, I2);
Mesh->GetTriangle(TriangleIndex, V0, V1, V2, ElementIndex);
}
if (Mesh->IsElementCastingShadow(ElementIndex) && !Mesh->IsTranslucent(ElementIndex))
{
check(I0 <= Mesh->NumVertices && I1 <= Mesh->NumVertices && I2 <= Mesh->NumVertices);
const bool bTwoSided = Mesh->IsTwoSided(ElementIndex) || Mesh->IsCastingShadowAsTwoSided();
const bool bStaticAndOpaque = !Mesh->IsMasked(ElementIndex) && !Mesh->IsTranslucent(ElementIndex) && !Mesh->bMovable;
Vertices[BaseVertexIndex + I0] = V0.WorldPosition;
Vertices[BaseVertexIndex + I1] = V1.WorldPosition;
Vertices[BaseVertexIndex + I2] = V2.WorldPosition;
UVs[BaseVertexIndex + I0] = V0.TextureCoordinates[Mesh->TextureCoordinateIndex];
UVs[BaseVertexIndex + I1] = V1.TextureCoordinates[Mesh->TextureCoordinateIndex];
UVs[BaseVertexIndex + I2] = V2.TextureCoordinates[Mesh->TextureCoordinateIndex];
if (TextureMapping)
{
LightmapUVs[BaseVertexIndex + I0] = V0.TextureCoordinates[TextureMapping->LightmapTextureCoordinateIndex];
LightmapUVs[BaseVertexIndex + I1] = V1.TextureCoordinates[TextureMapping->LightmapTextureCoordinateIndex];
LightmapUVs[BaseVertexIndex + I2] = V2.TextureCoordinates[TextureMapping->LightmapTextureCoordinateIndex];
}
// Compute the triangle's normal.
const FVector4f TriangleNormal = (V2.WorldPosition - V0.WorldPosition) ^ (V1.WorldPosition - V0.WorldPosition);
// Compute the triangle area.
const float TriangleArea = TriangleNormal.Size3() * 0.5f;
// Ignore zero area triangles.
if (TriangleArea > TRIANGLE_AREA_THRESHOLD)
{
const int32 PayloadIndex = TrianglePayloads.Add(
FTriangleSOAPayload(MeshInfos.Last(), Mapping, ElementIndex, BaseVertexIndex + I0, BaseVertexIndex + I1, BaseVertexIndex + I2));
new(kDOPTriangles) FkDOPBuildCollisionTriangle<uint32>(
PayloadIndex, // Use the triangle's material index as an index into TrianglePayloads.
V0.WorldPosition, V1.WorldPosition, V2.WorldPosition,
Mesh->MeshIndex,
MeshLODIndices,
MeshHLODRange,
bTwoSided,
bStaticAndOpaque
);
}
}
}
}
}
/**
* Pre-allocates memory ahead of time, before calling AddMesh() a bunch of times.
*
* @param NumMeshes - Expected number of meshes which will be added
* @param NumVertices - Expected number of vertices which will be added
* @param NumTriangles - Expected number of triangles which will be added
*/
void FDefaultAggregateMesh::ReserveMemory( int32 NumMeshes, int32 NumVertices, int32 NumTriangles )
{
UE_LOG(LogLightmass, Log, TEXT("Reserving memory for %d meshes, %d vertices, %d triangles"), NumMeshes, NumVertices, NumTriangles);
MeshInfos.Reserve( NumMeshes );
Vertices.Reserve( NumVertices );
UVs.Reserve( NumVertices );
LightmapUVs.Reserve( NumVertices );
TrianglePayloads.Reserve( NumTriangles );
kDOPTriangles.Reserve( NumTriangles );
}
void FDefaultAggregateMesh::PrepareForRaytracing()
{
// Build the kDOP for simple meshes.
kDopTree.Build(kDOPTriangles);
kDOPTriangles.Empty();
TrianglePayloads.Shrink();
}
void FDefaultAggregateMesh::DumpStats() const
{
UE_LOG(LogLightmass, Log, TEXT("Preallocated %.1fGb for kDOP nodes and triangles"), kDopTree.kDOPPreallocatedMemory / 1024.0f / 1024.0f / 1024.0f);
UE_LOG(LogLightmass, Log, TEXT("Building kDOP took %5.2f seconds."), kDopTree.kDOPBuildTime);
// Log information about the aggregate mesh.
UE_LOG(LogLightmass, Log, TEXT("Static lighting kDOP: %u nodes, %u leaves, %u triangles, %u vertices"), kDopTree.GKDOPNodes, kDopTree.GKDOPNumLeaves, kDopTree.GKDOPTriangles, Vertices.Num());
UE_LOG(LogLightmass, Log, TEXT("Static lighting kDOP: %.3f%% wasted space in leaves"), ((kDopTree.GKDOPTriangles - kDOPTriangles.Num()) / (float)kDopTree.GKDOPTriangles) * 100.0f);
const uint64 kDOPTreeBytes = kDopTree.Nodes.GetAllocatedSize()
+ kDopTree.SOATriangles.GetAllocatedSize()
+ kDOPTriangles.GetAllocatedSize()
+ TrianglePayloads.GetAllocatedSize()
+ MeshInfos.GetAllocatedSize()
+ Vertices.GetAllocatedSize()
+ UVs.GetAllocatedSize()
+ LightmapUVs.GetAllocatedSize();
UE_LOG(LogLightmass, Log, TEXT("kDopTree.Nodes : %7.1fMb"), kDopTree.Nodes.GetAllocatedSize() / 1048576.0f);
UE_LOG(LogLightmass, Log, TEXT("kDopTree.SOATriangles : %7.1fMb"), kDopTree.SOATriangles.GetAllocatedSize() / 1048576.0f);
UE_LOG(LogLightmass, Log, TEXT("kDOPTriangles : %7.1fMb"), kDOPTriangles.GetAllocatedSize() / 1048576.0f);
UE_LOG(LogLightmass, Log, TEXT("TrianglePayloads : %7.1fMb"), TrianglePayloads.GetAllocatedSize() / 1048576.0f);
UE_LOG(LogLightmass, Log, TEXT("MeshInfos : %7.1fMb"), MeshInfos.GetAllocatedSize() / 1048576.0f);
UE_LOG(LogLightmass, Log, TEXT("Vertices : %7.1fMb"), Vertices.GetAllocatedSize() / 1048576.0f);
UE_LOG(LogLightmass, Log, TEXT("UVs : %7.1fMb"), UVs.GetAllocatedSize() / 1048576.0f);
UE_LOG(LogLightmass, Log, TEXT("LightmapUVs : %7.1fMb"), LightmapUVs.GetAllocatedSize() / 1048576.0f);
UE_LOG(LogLightmass, Log, TEXT("Static lighting kDOP: %u nodes, %u leaves, %u triangles, %u vertices, %.1f Mb"), kDopTree.GKDOPNodes, kDopTree.GKDOPNumLeaves, kDopTree.GKDOPTriangles, Vertices.Num(), kDOPTreeBytes / 1048576.0f);
}
void FDefaultAggregateMesh::DumpPostBuildStats() const
{
double KDOPTrianglesTraversedRealPercent = (1.0 - ((kDopTree.GKDOPTrianglesTraversed - kDopTree.GKDOPTrianglesTraversedReal) / (kDopTree.GKDOPTrianglesTraversed * 100.0)));
UE_LOG(LogLightmass, Log, TEXT("kDOP traversals (in millions): %.3g parents, %.3g leaves, %.3g triangles (%.3g, %.3g%%, real triangles)."),
kDopTree.GKDOPParentNodesTraversed / 1000000.0,
kDopTree.GKDOPLeafNodesTraversed / 1000000.0,
kDopTree.GKDOPTrianglesTraversed / 1000000.0,
kDopTree.GKDOPTrianglesTraversedReal / 1000000.0,
KDOPTrianglesTraversedRealPercent);
}
class FStaticLightingAggregateMeshDataProvider
{
public:
/** Initialization constructor. */
FStaticLightingAggregateMeshDataProvider(const FDefaultAggregateMesh* InMesh,const FLightRay& InLightRay):
Mesh(InMesh),
LightRay(InLightRay)
{}
// kDOP data provider interface.
FORCEINLINE const FVector4f& GetVertex(uint32 Index) const
{
return Mesh->Vertices[Index];
}
FORCEINLINE FVector2f GetUV(uint32 Index) const
{
return Mesh->UVs[Index];
}
FORCEINLINE FVector2f GetLightmapUV(uint32 Index) const
{
return Mesh->LightmapUVs[Index];
}
/*
FORCEINLINE UMaterialInterface* GetMaterial(uint32 MaterialIndex) const
{
return NULL;
}
*/
FORCEINLINE int32 GetItemIndex(uint32 MaterialIndex) const
{
return MaterialIndex;
}
FORCEINLINE const TkDOPTree<const FStaticLightingAggregateMeshDataProvider,uint32>& GetkDOPTree(void) const
{
return Mesh->kDopTree;
}
FORCEINLINE const FMatrix44f& GetLocalToWorld(void) const
{
return FMatrix44f::Identity;
}
FORCEINLINE const FMatrix44f& GetWorldToLocal(void) const
{
return FMatrix44f::Identity;
}
FORCEINLINE FMatrix44f GetLocalToWorldTransposeAdjoint(void) const
{
return FMatrix44f::Identity;
}
FORCEINLINE float GetDeterminant(void) const
{
return 1.0f;
}
private:
const FDefaultAggregateMesh* Mesh;
const FLightRay& LightRay;
};
/**
* Checks a light ray for intersection with the shadow mesh.
* @param LightRay - The line segment to check for intersection.
* @param bFindClosestIntersection - true if the intersection must return the closest intersection. false if it may return any intersection.
* This can be used as an optimization for rays which only need to know if there was an intersection or not, but not any other information about the intersection.
* Note: bFindClosestIntersection == false currently does not handle masked materials correctly, it treats them as if they were opaque.
* However, bFindClosestIntersection == false does work correctly in conjunction with LIGHTRAY_STATIC_AND_OPAQUEONLY.
* @param bCalculateTransmission - Whether to keep track of transmission or not. If this is true, bFindClosestIntersection must also be true.
* @param bDirectShadowingRay - Whether this ray is being used to calculate direct shadowing.
* @param CoherentRayCache - The calling thread's collision cache.
* @param [out] Intersection - The intersection of between the light ray and the mesh.
* @return true if there is an intersection, false otherwise
*/
bool FDefaultAggregateMesh::IntersectLightRay(
const FLightRay& LightRay,
bool bFindClosestIntersection,
bool bCalculateTransmission,
bool bDirectShadowingRay,
FCoherentRayCache& CoherentRayCache,
FLightRayIntersection& ClosestIntersection) const
{
LIGHTINGSTAT(FScopedRDTSCTimer RayTraceTimer(bFindClosestIntersection ? CoherentRayCache.FirstHitRayTraceTime : CoherentRayCache.BooleanRayTraceTime);)
bFindClosestIntersection ? CoherentRayCache.NumFirstHitRaysTraced++ : CoherentRayCache.NumBooleanRaysTraced++;
// Calculating transmission requires finding the closest intersection for now
//@todo - allow boolean visibility tests while calculating transmission
checkSlow(!bCalculateTransmission || bFindClosestIntersection);
ClosestIntersection.bIntersects = false;
FLinearColor Transmission(FLinearColor::White);
//@todo - remove this hack which prevents infinite looping in some levels.
const int32 MaxNumIterativeIntersections = 20;
int32 NumIterativeIntersections = 0;
do
{
FLightRay ClippedLightRay = LightRay;
if (ClosestIntersection.bIntersects)
{
NumIterativeIntersections++;
// Clip the ray so it is now from the intersection point (plus some amount to avoid intersecting the same triangle again) to the original end of the ray
ClippedLightRay.ClipAgainstIntersectionFromEnd(ClosestIntersection.IntersectionVertex.WorldPosition + Scene.SceneConstants.VisibilityRayOffsetDistance * ClippedLightRay.Direction.GetUnsafeNormal3());
if ((ClosestIntersection.Mesh == LightRay.Mesh && ((ClosestIntersection.Mesh->LightingFlags & GI_INSTANCE_SELFSHADOWDISABLE) || (LightRay.TraceFlags & LIGHTRAY_SELFSHADOWDISABLE)))
|| (bDirectShadowingRay && ClosestIntersection.Mesh->IsIndirectlyShadowedOnly(ClosestIntersection.ElementIndex))
|| (ClosestIntersection.Mesh != LightRay.Mesh && (ClosestIntersection.Mesh->LightingFlags & GI_INSTANCE_SELFSHADOWONLY))
|| !ClosestIntersection.Mesh->IsSurfaceDomain(ClosestIntersection.ElementIndex))
{
// Nothing more to do if we're just avoiding shadowing based on trace flags, just keep going
}
else if (ClosestIntersection.Mesh->IsMasked(ClosestIntersection.ElementIndex) || (bDirectShadowingRay && ClosestIntersection.Mesh->IsCastingShadowsAsMasked(ClosestIntersection.ElementIndex)))
{
// look to see if we hit a hole or opaque part
if (ClosestIntersection.Mesh->EvaluateMaskedCollision(ClosestIntersection.IntersectionVertex.TextureCoordinates[0], ClosestIntersection.ElementIndex))
{
// Hit an opaque part of a masked mesh, terminate the ray intersection
break;
}
}
else if (bCalculateTransmission)
{
const FLinearColor NewTransmission = ClosestIntersection.Mesh->EvaluateTransmission(
ClosestIntersection.IntersectionVertex.TextureCoordinates[0],
ClosestIntersection.ElementIndex);
// Accumulate the total transmission along the ray
// The result is order independent so the intersections don't have to be strictly front to back
Transmission *= NewTransmission;
}
ClosestIntersection.bIntersects = false;
}
// Check the kDOP containing low polygon meshes first.
FHitResult Result;
FStaticLightingAggregateMeshDataProvider kDOPDataProvider(this, ClippedLightRay);
TkDOPLineCollisionCheck<const FStaticLightingAggregateMeshDataProvider,uint32> kDOPCheck(
ClippedLightRay.Start,
ClippedLightRay.Start + ClippedLightRay.Direction * ClippedLightRay.Length,
bFindClosestIntersection,
(LightRay.TraceFlags & LIGHTRAY_STATIC_AND_OPAQUEONLY) != 0,
!bDirectShadowingRay,
(LightRay.TraceFlags & LIGHTRAY_FLIP_SIDEDNESS) != 0,
kDOPDataProvider,
LightRay.Mapping ? LightRay.Mapping->Mesh->MeshIndex : INDEX_NONE,
LightRay.Mapping ? LightRay.Mapping->Mesh->GetLODIndices() : INDEX_NONE,
LightRay.Mapping ? LightRay.Mapping->Mesh->GetHLODRange() : INDEX_NONE,
&Result);
bool bHit = false;
if (!bFindClosestIntersection && CoherentRayCache.kDOPNodeIndex != 0xFFFFFFFF)
{
TTraversalHistory<uint32> History;
// Trace against the last hit node if we're doing a boolean visibility check before traversing the whole tree
// Provides a small speedup with coherent boolean visibility rays (1.1x faster for precomputed visibility)
bHit = kDopTree.Nodes[CoherentRayCache.kDOPNodeIndex].LineCheck(kDOPCheck, History.AddNode(CoherentRayCache.kDOPNodeIndex));
}
if (!bHit)
{
bHit = kDopTree.LineCheck(kDOPCheck);
}
if (bHit)
{
// Setup a vertex to represent the intersection.
FMinimalStaticLightingVertex IntersectionVertex;
IntersectionVertex.WorldPosition = ClippedLightRay.Start + ClippedLightRay.Direction * ClippedLightRay.Length * Result.Time;
IntersectionVertex.WorldTangentZ = kDOPCheck.LocalHitNormal;
const FTriangleSOAPayload& Payload = TrianglePayloads[ Result.Item ];
const FVector4f& v1 = kDOPDataProvider.GetVertex(Payload.VertexIndex[0]);
const FVector4f& v2 = kDOPDataProvider.GetVertex(Payload.VertexIndex[1]);
const FVector4f& v3 = kDOPDataProvider.GetVertex(Payload.VertexIndex[2]);
const FVector4f LocalHitPosition = kDOPCheck.LocalStart + kDOPCheck.LocalDir * Result.Time;
FVector4f BaryCentricWeights;
//@todo - why is such a huge tolerance needed? Reuse the barycentric coords calculated by the ray-triangle intersection instead of deriving them from the hit position.
//@todo - why does this sometimes fail if there was an intersection?
if (bFindClosestIntersection && GetBarycentricWeights(v1, v2, v3, LocalHitPosition, KINDA_SMALL_NUMBER * 100.0f, BaryCentricWeights))
{
const FVector2f& UV1 = kDOPDataProvider.GetUV(Payload.VertexIndex[0]);
const FVector2f& UV2 = kDOPDataProvider.GetUV(Payload.VertexIndex[1]);
const FVector2f& UV3 = kDOPDataProvider.GetUV(Payload.VertexIndex[2]);
// Interpolate the material texture coordinates to the intersection point
//@todo - only lookup and interpolate UV's if needed
IntersectionVertex.TextureCoordinates[0] = UV1 * BaryCentricWeights.X + UV2 * BaryCentricWeights.Y + UV3 * BaryCentricWeights.Z;
const FVector2f& LightmapUV1 = kDOPDataProvider.GetLightmapUV(Payload.VertexIndex[0]);
const FVector2f& LightmapUV2 = kDOPDataProvider.GetLightmapUV(Payload.VertexIndex[1]);
const FVector2f& LightmapUV3 = kDOPDataProvider.GetLightmapUV(Payload.VertexIndex[2]);
// Interpolate the lightmap texture coordinates to the intersection point
IntersectionVertex.TextureCoordinates[1] = LightmapUV1 * BaryCentricWeights.X + LightmapUV2 * BaryCentricWeights.Y + LightmapUV3 * BaryCentricWeights.Z;
}
else
{
IntersectionVertex.TextureCoordinates[0] = FVector2f(0,0);
IntersectionVertex.TextureCoordinates[1] = FVector2f(0,0);
}
ClosestIntersection = FLightRayIntersection(true, IntersectionVertex, Payload.MeshInfo->Mesh, Payload.Mapping, Payload.ElementIndex);
if (bFindClosestIntersection)
{
ClippedLightRay.ClipAgainstIntersectionFromStart(ClosestIntersection.IntersectionVertex.WorldPosition);
}
else
{
// Store off the hit node so future boolean visibility rays can test against that first
CoherentRayCache.kDOPNodeIndex = kDOPCheck.HitNodeIndex;
//@todo - handle masked materials correctly with !bFindClosestIntersection
return true;
}
}
}
// Continue tracing as long as we are intersecting meshes that might need to restart the ray
while (ClosestIntersection.bIntersects
&& (ClosestIntersection.Mesh->IsTranslucent(ClosestIntersection.ElementIndex) ||
ClosestIntersection.Mesh->IsMasked(ClosestIntersection.ElementIndex) ||
(ClosestIntersection.Mesh == LightRay.Mesh && ((ClosestIntersection.Mesh->LightingFlags & GI_INSTANCE_SELFSHADOWDISABLE) || (LightRay.TraceFlags & LIGHTRAY_SELFSHADOWDISABLE))) ||
// Continue tracing if we are only allowed to self shadow and intersected a different mesh
(ClosestIntersection.Mesh != LightRay.Mesh && (ClosestIntersection.Mesh->LightingFlags & GI_INSTANCE_SELFSHADOWONLY)) ||
(bDirectShadowingRay && ClosestIntersection.Mesh->IsIndirectlyShadowedOnly(ClosestIntersection.ElementIndex)))
&& NumIterativeIntersections < MaxNumIterativeIntersections);
if (NumIterativeIntersections >= MaxNumIterativeIntersections)
{
ClosestIntersection.bIntersects = false;
}
// Must not return an intersection with a translucent mesh
checkSlow(!ClosestIntersection.bIntersects
|| (!ClosestIntersection.Mesh->IsTranslucent(ClosestIntersection.ElementIndex) && !ClosestIntersection.Mesh->IsSurfaceDomain(ClosestIntersection.ElementIndex))
|| bDirectShadowingRay && ClosestIntersection.Mesh->IsCastingShadowsAsMasked(ClosestIntersection.ElementIndex));
ClosestIntersection.Transmission = Transmission;
return ClosestIntersection.bIntersects;
}
const FStaticLightingMesh* FDefaultAggregateMesh::IntersectBox(const FBox3f Box) const
{
FLightRay UnusedRay;
FStaticLightingAggregateMeshDataProvider kDOPDataProvider(this, UnusedRay);
TkDOPBoxCollisionCheck<const FStaticLightingAggregateMeshDataProvider, uint32> kDOPCheck(
Box,
kDOPDataProvider);
kDopTree.BoxCheck(kDOPCheck);
if (kDOPCheck.Item != -1)
{
const FTriangleSOAPayload& Payload = TrianglePayloads[kDOPCheck.Item];
return Payload.MeshInfo->Mesh;
}
else
{
return nullptr;
}
}
} //namespace Lightmass