/** * Creates a static lighting vertex to represent the given static mesh vertex. * @param VertexBuffer - The static mesh's vertex buffer. * @param VertexIndex - The index of the static mesh vertex to access. * @param OutVertex - Upon return, contains a static lighting vertex representing the specified static mesh vertex. */ static void GetStaticLightingVertex( const FPositionVertexBuffer& PositionVertexBuffer, const FStaticMeshVertexBuffer& VertexBuffer, uint32 VertexIndex, const FMatrix& LocalToWorld, const FMatrix& LocalToWorldInverseTranspose, FStaticLightingVertex& OutVertex ) { OutVertex.WorldPosition = LocalToWorld.TransformPosition(PositionVertexBuffer.VertexPosition(VertexIndex)); OutVertex.WorldTangentX = LocalToWorld.TransformVector(VertexBuffer.VertexTangentX(VertexIndex)).GetSafeNormal(); OutVertex.WorldTangentY = LocalToWorld.TransformVector(VertexBuffer.VertexTangentY(VertexIndex)).GetSafeNormal(); OutVertex.WorldTangentZ = LocalToWorldInverseTranspose.TransformVector(VertexBuffer.VertexTangentZ(VertexIndex)).GetSafeNormal(); checkSlow(VertexBuffer.GetNumTexCoords() <= ARRAY_COUNT(OutVertex.TextureCoordinates)); for(uint32 LightmapTextureCoordinateIndex = 0;LightmapTextureCoordinateIndex < VertexBuffer.GetNumTexCoords();LightmapTextureCoordinateIndex++) { OutVertex.TextureCoordinates[LightmapTextureCoordinateIndex] = VertexBuffer.GetVertexUV(VertexIndex,LightmapTextureCoordinateIndex); } }
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); } } }