/** Updates GCurrentSelectedLightmapSample given a selected actor's components and the location of the click. */ void SetDebugLightmapSample(TArray<UActorComponent*>* Components, UModel* Model, int32 iSurf, FVector ClickLocation) { UStaticMeshComponent* SMComponent = NULL; if (Components) { // Find the first supported component for (int32 ComponentIndex = 0; ComponentIndex < Components->Num() && !SMComponent; ComponentIndex++) { SMComponent = Cast<UStaticMeshComponent>((*Components)[ComponentIndex]); if (SMComponent && (!SMComponent->StaticMesh || SMComponent->LODData.Num() == 0)) { SMComponent = NULL; } } } bool bFoundLightmapSample = false; // Only static mesh components and BSP handled for now if (SMComponent) { UStaticMesh* StaticMesh = SMComponent->StaticMesh; check(StaticMesh); check(StaticMesh->RenderData); check(StaticMesh->RenderData->LODResources.Num()); // Only supporting LOD0 const int32 LODIndex = 0; FStaticMeshLODResources& LODModel = StaticMesh->RenderData->LODResources[LODIndex]; FIndexArrayView Indices = LODModel.IndexBuffer.GetArrayView(); const bool bHasStaticLighting = SMComponent->HasStaticLighting(); if (bHasStaticLighting) { bool bUseTextureMap = false; int32 LightmapSizeX = 0; int32 LightmapSizeY = 0; SMComponent->GetLightMapResolution(LightmapSizeX, LightmapSizeY); if (LightmapSizeX > 0 && LightmapSizeY > 0 && StaticMesh->LightMapCoordinateIndex >= 0 && (uint32)StaticMesh->LightMapCoordinateIndex < LODModel.VertexBuffer.GetNumTexCoords() ) { bUseTextureMap = true; } else { bUseTextureMap = false; } // Search through the static mesh's triangles for the one that was hit (since we can't get triangle index from a line check) for(int32 TriangleIndex = 0; TriangleIndex < Indices.Num(); TriangleIndex += 3) { uint32 Index0 = Indices[TriangleIndex]; uint32 Index1 = Indices[TriangleIndex + 1]; uint32 Index2 = Indices[TriangleIndex + 2]; // Transform positions to world space FVector Position0 = SMComponent->ComponentToWorld.TransformPosition(LODModel.PositionVertexBuffer.VertexPosition(Index0)); FVector Position1 = SMComponent->ComponentToWorld.TransformPosition(LODModel.PositionVertexBuffer.VertexPosition(Index1)); FVector Position2 = SMComponent->ComponentToWorld.TransformPosition(LODModel.PositionVertexBuffer.VertexPosition(Index2)); FVector BaryCentricWeights; // Continue if click location is in the triangle and get its barycentric weights if (GetBarycentricWeights(Position0, Position1, Position2, ClickLocation, .001f, BaryCentricWeights)) { RestoreSelectedLightmapSample(); if (bUseTextureMap) { // Fetch lightmap UV's FVector2D LightmapUV0 = LODModel.VertexBuffer.GetVertexUV(Index0, StaticMesh->LightMapCoordinateIndex); FVector2D LightmapUV1 = LODModel.VertexBuffer.GetVertexUV(Index1, StaticMesh->LightMapCoordinateIndex); FVector2D LightmapUV2 = LODModel.VertexBuffer.GetVertexUV(Index2, StaticMesh->LightMapCoordinateIndex); // Interpolate lightmap UV's to the click location FVector2D InterpolatedUV = LightmapUV0 * BaryCentricWeights.X + LightmapUV1 * BaryCentricWeights.Y + LightmapUV2 * BaryCentricWeights.Z; int32 PaddedSizeX = LightmapSizeX; int32 PaddedSizeY = LightmapSizeY; if (GLightmassDebugOptions.bPadMappings && GAllowLightmapPadding && LightmapSizeX - 2 > 0 && LightmapSizeY - 2 > 0) { PaddedSizeX -= 2; PaddedSizeY -= 2; } const int32 LocalX = FMath::TruncToInt(InterpolatedUV.X * PaddedSizeX); const int32 LocalY = FMath::TruncToInt(InterpolatedUV.Y * PaddedSizeY); if (LocalX < 0 || LocalX >= PaddedSizeX || LocalY < 0 || LocalY >= PaddedSizeY) { UE_LOG(LogStaticLightingSystem, Log, TEXT("Texel selection failed because the lightmap UV's wrap!")); } else { bFoundLightmapSample = UpdateSelectedTexel(SMComponent, -1, SMComponent->LODData[LODIndex].LightMap, ClickLocation, InterpolatedUV, LocalX, LocalY, LightmapSizeX, LightmapSizeY); } } break; } } if (!bFoundLightmapSample && Indices.Num() > 0) { const int32 SelectedTriangle = FMath::RandRange(0, Indices.Num() / 3 - 1); uint32 Index0 = Indices[SelectedTriangle]; uint32 Index1 = Indices[SelectedTriangle + 1]; uint32 Index2 = Indices[SelectedTriangle + 2]; FVector2D LightmapUV0 = LODModel.VertexBuffer.GetVertexUV(Index0, StaticMesh->LightMapCoordinateIndex); FVector2D LightmapUV1 = LODModel.VertexBuffer.GetVertexUV(Index1, StaticMesh->LightMapCoordinateIndex); FVector2D LightmapUV2 = LODModel.VertexBuffer.GetVertexUV(Index2, StaticMesh->LightMapCoordinateIndex); FVector BaryCentricWeights; BaryCentricWeights.X = FMath::FRandRange(0, 1); BaryCentricWeights.Y = FMath::FRandRange(0, 1); if (BaryCentricWeights.X + BaryCentricWeights.Y >= 1) { BaryCentricWeights.X = 1 - BaryCentricWeights.X; BaryCentricWeights.Y = 1 - BaryCentricWeights.Y; } BaryCentricWeights.Z = 1 - BaryCentricWeights.X - BaryCentricWeights.Y; FVector2D InterpolatedUV = LightmapUV0 * BaryCentricWeights.X + LightmapUV1 * BaryCentricWeights.Y + LightmapUV2 * BaryCentricWeights.Z; UE_LOG(LogStaticLightingSystem, Log, TEXT("Failed to intersect any triangles, picking random texel")); int32 PaddedSizeX = LightmapSizeX; int32 PaddedSizeY = LightmapSizeY; if (GLightmassDebugOptions.bPadMappings && GAllowLightmapPadding && LightmapSizeX - 2 > 0 && LightmapSizeY - 2 > 0) { PaddedSizeX -= 2; PaddedSizeY -= 2; } const int32 LocalX = FMath::TruncToInt(InterpolatedUV.X * PaddedSizeX); const int32 LocalY = FMath::TruncToInt(InterpolatedUV.Y * PaddedSizeY); if (LocalX < 0 || LocalX >= PaddedSizeX || LocalY < 0 || LocalY >= PaddedSizeY) { UE_LOG(LogStaticLightingSystem, Log, TEXT("Texel selection failed because the lightmap UV's wrap!")); } else { bFoundLightmapSample = UpdateSelectedTexel(SMComponent, -1, SMComponent->LODData[LODIndex].LightMap, ClickLocation, InterpolatedUV, LocalX, LocalY, LightmapSizeX, LightmapSizeY); } } } } else if (Model) { UWorld* World = Model->LightingLevel->OwningWorld; check( World); for (int32 ModelIndex = 0; ModelIndex < World->GetCurrentLevel()->ModelComponents.Num(); ModelIndex++) { UModelComponent* CurrentComponent = World->GetCurrentLevel()->ModelComponents[ModelIndex]; int32 LightmapSizeX = 0; int32 LightmapSizeY = 0; CurrentComponent->GetLightMapResolution(LightmapSizeX, LightmapSizeY); if (LightmapSizeX > 0 && LightmapSizeY > 0) { for (int32 ElementIndex = 0; ElementIndex < CurrentComponent->GetElements().Num(); ElementIndex++) { FModelElement& Element = CurrentComponent->GetElements()[ElementIndex]; TScopedPointer<FRawIndexBuffer16or32>* IndexBufferRef = Model->MaterialIndexBuffers.Find(Element.Material); check(IndexBufferRef); for(uint32 TriangleIndex = Element.FirstIndex; TriangleIndex < Element.FirstIndex + Element.NumTriangles * 3; TriangleIndex += 3) { uint32 Index0 = (*IndexBufferRef)->Indices[TriangleIndex]; uint32 Index1 = (*IndexBufferRef)->Indices[TriangleIndex + 1]; uint32 Index2 = (*IndexBufferRef)->Indices[TriangleIndex + 2]; FModelVertex* ModelVertices = (FModelVertex*)Model->VertexBuffer.Vertices.GetData(); FVector Position0 = ModelVertices[Index0].Position; FVector Position1 = ModelVertices[Index1].Position; FVector Position2 = ModelVertices[Index2].Position; FVector BaryCentricWeights; // Continue if click location is in the triangle and get its barycentric weights if (GetBarycentricWeights(Position0, Position1, Position2, ClickLocation, .001f, BaryCentricWeights)) { RestoreSelectedLightmapSample(); // Fetch lightmap UV's FVector2D LightmapUV0 = ModelVertices[Index0].ShadowTexCoord; FVector2D LightmapUV1 = ModelVertices[Index1].ShadowTexCoord; FVector2D LightmapUV2 = ModelVertices[Index2].ShadowTexCoord; // Interpolate lightmap UV's to the click location FVector2D InterpolatedUV = LightmapUV0 * BaryCentricWeights.X + LightmapUV1 * BaryCentricWeights.Y + LightmapUV2 * BaryCentricWeights.Z; // Find the node index belonging to the selected triangle const UModel* CurrentModel = CurrentComponent->GetModel(); int32 SelectedNodeIndex = INDEX_NONE; for (int32 ElementNodeIndex = 0; ElementNodeIndex < Element.Nodes.Num(); ElementNodeIndex++) { const FBspNode& CurrentNode = CurrentModel->Nodes[Element.Nodes[ElementNodeIndex]]; if ((int32)Index0 >= CurrentNode.iVertexIndex && (int32)Index0 < CurrentNode.iVertexIndex + CurrentNode.NumVertices) { SelectedNodeIndex = Element.Nodes[ElementNodeIndex]; } } check(SelectedNodeIndex >= 0); TArray<ULightComponentBase*> DummyLights; // fill out the model's NodeGroups (not the mapping part of it, but the nodes part) Model->GroupAllNodes(World->GetCurrentLevel(), DummyLights); // Find the FGatheredSurface that the selected node got put into during the last lighting rebuild TArray<int32> GatheredNodes; // find the NodeGroup that this node went into, and get all of its node for (TMap<int32, FNodeGroup*>::TIterator It(Model->NodeGroups); It && GatheredNodes.Num() == 0; ++It) { FNodeGroup* NodeGroup = It.Value(); for (int32 NodeIndex = 0; NodeIndex < NodeGroup->Nodes.Num(); NodeIndex++) { if (NodeGroup->Nodes[NodeIndex] == SelectedNodeIndex) { GatheredNodes = NodeGroup->Nodes; break; } } } check(GatheredNodes.Num() > 0); // use the surface of the selected node, it will have to suffice for the GetSurfaceLightMapResolution() call int32 SelectedGatheredSurfIndex = Model->Nodes[SelectedNodeIndex].iSurf; // Get the lightmap resolution used by the FGatheredSurface containing the selected node FMatrix WorldToMap; CurrentComponent->GetSurfaceLightMapResolution(SelectedGatheredSurfIndex, 1, LightmapSizeX, LightmapSizeY, WorldToMap, &GatheredNodes); int32 PaddedSizeX = LightmapSizeX; int32 PaddedSizeY = LightmapSizeY; if (GLightmassDebugOptions.bPadMappings && GAllowLightmapPadding && LightmapSizeX - 2 > 0 && LightmapSizeY - 2 > 0) { PaddedSizeX -= 2; PaddedSizeY -= 2; } check(LightmapSizeX > 0 && LightmapSizeY > 0); // Apply the transform to the intersection position to find the local texel coordinates const FVector4 StaticLightingTextureCoordinate = WorldToMap.TransformPosition(ClickLocation); const int32 LocalX = FMath::TruncToInt(StaticLightingTextureCoordinate.X * PaddedSizeX); const int32 LocalY = FMath::TruncToInt(StaticLightingTextureCoordinate.Y * PaddedSizeY); check(LocalX >= 0 && LocalX < PaddedSizeX && LocalY >= 0 && LocalY < PaddedSizeY); bFoundLightmapSample = UpdateSelectedTexel( CurrentComponent, SelectedNodeIndex, Element.LightMap, ClickLocation, InterpolatedUV, LocalX, LocalY, LightmapSizeX, LightmapSizeY); if (!bFoundLightmapSample) { RestoreSelectedLightmapSample(); GCurrentSelectedLightmapSample = FSelectedLightmapSample(); } return; } } } } } } if (!bFoundLightmapSample) { RestoreSelectedLightmapSample(); GCurrentSelectedLightmapSample = FSelectedLightmapSample(); } }
void FStaticMeshEditorViewportClient::Draw(const FSceneView* View,FPrimitiveDrawInterface* PDI) { FEditorViewportClient::Draw(View, PDI); if (bShowCollision && StaticMesh->BodySetup) { const FColor SelectedColor(149, 223, 157); const FColor UnselectedColor(157, 149, 223); // Draw bodies FKAggregateGeom* AggGeom = &StaticMesh->BodySetup->AggGeom; for (int32 i = 0; i < AggGeom->SphereElems.Num(); ++i) { HSMECollisionProxy* HitProxy = new HSMECollisionProxy(KPT_Sphere, i); PDI->SetHitProxy(HitProxy); const FColor CollisionColor = StaticMeshEditorPtr.Pin()->IsSelectedPrim(HitProxy->PrimData) ? SelectedColor : UnselectedColor; const FKSphereElem& SphereElem = AggGeom->SphereElems[i]; const FTransform ElemTM = SphereElem.GetTransform(); SphereElem.DrawElemWire(PDI, ElemTM, 1.f, CollisionColor); PDI->SetHitProxy(NULL); } for (int32 i = 0; i < AggGeom->BoxElems.Num(); ++i) { HSMECollisionProxy* HitProxy = new HSMECollisionProxy(KPT_Box, i); PDI->SetHitProxy(HitProxy); const FColor CollisionColor = StaticMeshEditorPtr.Pin()->IsSelectedPrim(HitProxy->PrimData) ? SelectedColor : UnselectedColor; const FKBoxElem& BoxElem = AggGeom->BoxElems[i]; const FTransform ElemTM = BoxElem.GetTransform(); BoxElem.DrawElemWire(PDI, ElemTM, 1.f, CollisionColor); PDI->SetHitProxy(NULL); } for (int32 i = 0; i < AggGeom->SphylElems.Num(); ++i) { HSMECollisionProxy* HitProxy = new HSMECollisionProxy(KPT_Sphyl, i); PDI->SetHitProxy(HitProxy); const FColor CollisionColor = StaticMeshEditorPtr.Pin()->IsSelectedPrim(HitProxy->PrimData) ? SelectedColor : UnselectedColor; const FKSphylElem& SphylElem = AggGeom->SphylElems[i]; const FTransform ElemTM = SphylElem.GetTransform(); SphylElem.DrawElemWire(PDI, ElemTM, 1.f, CollisionColor); PDI->SetHitProxy(NULL); } for (int32 i = 0; i < AggGeom->ConvexElems.Num(); ++i) { HSMECollisionProxy* HitProxy = new HSMECollisionProxy(KPT_Convex, i); PDI->SetHitProxy(HitProxy); const FColor CollisionColor = StaticMeshEditorPtr.Pin()->IsSelectedPrim(HitProxy->PrimData) ? SelectedColor : UnselectedColor; const FKConvexElem& ConvexElem = AggGeom->ConvexElems[i]; const FTransform ElemTM = ConvexElem.GetTransform(); ConvexElem.DrawElemWire(PDI, ElemTM, CollisionColor); PDI->SetHitProxy(NULL); } } if( bShowSockets ) { const FColor SocketColor = FColor(255, 128, 128); for(int32 i=0; i < StaticMesh->Sockets.Num(); i++) { UStaticMeshSocket* Socket = StaticMesh->Sockets[i]; if(Socket) { FMatrix SocketTM; Socket->GetSocketMatrix(SocketTM, StaticMeshComponent); PDI->SetHitProxy( new HSMESocketProxy(i) ); DrawWireDiamond(PDI, SocketTM, 5.f, SocketColor, SDPG_Foreground); PDI->SetHitProxy( NULL ); } } } // Draw any edges that are currently selected by the user if( SelectedEdgeIndices.Num() > 0 ) { for(int32 VertexIndex = 0; VertexIndex < SelectedEdgeVertices.Num(); VertexIndex += 2) { FVector EdgeVertices[ 2 ]; EdgeVertices[ 0 ] = SelectedEdgeVertices[VertexIndex]; EdgeVertices[ 1 ] = SelectedEdgeVertices[VertexIndex + 1]; PDI->DrawLine( StaticMeshComponent->ComponentToWorld.TransformPosition( EdgeVertices[ 0 ] ), StaticMeshComponent->ComponentToWorld.TransformPosition( EdgeVertices[ 1 ] ), FColor( 255, 255, 0 ), SDPG_World ); } } if( bDrawNormals || bDrawTangents || bDrawBinormals ) { FStaticMeshLODResources& LODModel = StaticMesh->RenderData->LODResources[StaticMeshEditorPtr.Pin()->GetCurrentLODIndex()]; FIndexArrayView Indices = LODModel.IndexBuffer.GetArrayView(); uint32 NumIndices = Indices.Num(); FMatrix LocalToWorldInverseTranspose = StaticMeshComponent->ComponentToWorld.ToMatrixWithScale().InverseFast().GetTransposed(); for (uint32 i = 0; i < NumIndices; i++) { const FVector& VertexPos = LODModel.PositionVertexBuffer.VertexPosition( Indices[i] ); const FVector WorldPos = StaticMeshComponent->ComponentToWorld.TransformPosition( VertexPos ); const FVector& Normal = LODModel.VertexBuffer.VertexTangentZ( Indices[i] ); const FVector& Binormal = LODModel.VertexBuffer.VertexTangentY( Indices[i] ); const FVector& Tangent = LODModel.VertexBuffer.VertexTangentX( Indices[i] ); const float Len = 5.0f; if( bDrawNormals ) { PDI->DrawLine( WorldPos, WorldPos+LocalToWorldInverseTranspose.TransformVector( Normal ).SafeNormal() * Len, FLinearColor( 0.0f, 1.0f, 0.0f), SDPG_World ); } if( bDrawTangents ) { PDI->DrawLine( WorldPos, WorldPos+LocalToWorldInverseTranspose.TransformVector( Tangent ).SafeNormal() * Len, FLinearColor( 1.0f, 0.0f, 0.0f), SDPG_World ); } if( bDrawBinormals ) { PDI->DrawLine( WorldPos, WorldPos+LocalToWorldInverseTranspose.TransformVector( Binormal ).SafeNormal() * Len, FLinearColor( 0.0f, 0.0f, 1.0f), SDPG_World ); } } } if( bShowPivot ) { FUnrealEdUtils::DrawWidget(View, PDI, StaticMeshComponent->ComponentToWorld.ToMatrixWithScale(), 0, 0, EAxisList::All, EWidgetMovementMode::WMM_Translate, false); } if( bDrawAdditionalData ) { const TArray<UAssetUserData*>* UserDataArray = StaticMesh->GetAssetUserDataArray(); if (UserDataArray != NULL) { for (int32 AdditionalDataIndex = 0; AdditionalDataIndex < UserDataArray->Num(); ++AdditionalDataIndex) { if ((*UserDataArray)[AdditionalDataIndex] != NULL) { (*UserDataArray)[AdditionalDataIndex]->Draw(PDI, View); } } } } }
/** * Constructs a raw mesh from legacy render data. */ static void BuildRawMeshFromRenderData( FRawMesh& OutRawMesh, struct FMeshBuildSettings& OutBuildSettings, FLegacyStaticMeshRenderData& RenderData, const TCHAR* MeshName ) { FStaticMeshTriangle* RawTriangles = (FStaticMeshTriangle*)RenderData.RawTriangles.Lock(LOCK_READ_ONLY); bool bBuiltFromRawTriangles = BuildRawMeshFromRawTriangles( OutRawMesh, OutBuildSettings, RawTriangles, RenderData.RawTriangles.GetElementCount(), MeshName ); RenderData.RawTriangles.Unlock(); if (bBuiltFromRawTriangles) { return; } OutRawMesh.Empty(); FIndexArrayView Indices = RenderData.IndexBuffer.GetArrayView(); int32 NumVertices = RenderData.PositionVertexBuffer.GetNumVertices(); int32 NumTriangles = Indices.Num() / 3; int32 NumWedges = NumTriangles * 3; // Copy vertex positions. { OutRawMesh.VertexPositions.AddUninitialized(NumVertices); for (int32 i = 0; i < NumVertices; ++i) { OutRawMesh.VertexPositions[i] = RenderData.PositionVertexBuffer.VertexPosition(i); } } // Copy per-wedge texture coordinates. for (uint32 TexCoordIndex = 0; TexCoordIndex < RenderData.VertexBuffer.GetNumTexCoords(); ++TexCoordIndex) { OutRawMesh.WedgeTexCoords[TexCoordIndex].AddUninitialized(NumWedges); for (int32 i = 0; i < NumWedges; ++i) { uint32 VertIndex = Indices[i]; OutRawMesh.WedgeTexCoords[TexCoordIndex][i] = RenderData.VertexBuffer.GetVertexUV(VertIndex, TexCoordIndex); } } // Copy per-wedge colors if they exist. if (RenderData.ColorVertexBuffer.GetNumVertices() > 0) { OutRawMesh.WedgeColors.AddUninitialized(NumWedges); for (int32 i = 0; i < NumWedges; ++i) { uint32 VertIndex = Indices[i]; OutRawMesh.WedgeColors[i] = RenderData.ColorVertexBuffer.VertexColor(VertIndex); } } // Copy per-wedge tangents. { OutRawMesh.WedgeTangentX.AddUninitialized(NumWedges); OutRawMesh.WedgeTangentY.AddUninitialized(NumWedges); OutRawMesh.WedgeTangentZ.AddUninitialized(NumWedges); for (int32 i = 0; i < NumWedges; ++i) { uint32 VertIndex = Indices[i]; OutRawMesh.WedgeTangentX[i] = RenderData.VertexBuffer.VertexTangentX(VertIndex); OutRawMesh.WedgeTangentY[i] = RenderData.VertexBuffer.VertexTangentY(VertIndex); OutRawMesh.WedgeTangentZ[i] = RenderData.VertexBuffer.VertexTangentZ(VertIndex); } } // Copy per-face information. { OutRawMesh.FaceMaterialIndices.AddZeroed(NumTriangles); OutRawMesh.FaceSmoothingMasks.AddZeroed(NumTriangles); OutRawMesh.WedgeIndices.AddZeroed(NumWedges); for (int32 SectionIndex = 0; SectionIndex < RenderData.Elements.Num(); ++SectionIndex) { const FLegacyStaticMeshElement& Section = RenderData.Elements[SectionIndex]; int32 FirstFace = Section.FirstIndex / 3; int32 LastFace = FirstFace + Section.NumTriangles; for (int32 i = FirstFace; i < LastFace; ++i) { OutRawMesh.FaceMaterialIndices[i] = Section.MaterialIndex; // Smoothing group information has been lost but is already baked in to the tangent basis. for (int32 j = 0; j < 3; ++j) { OutRawMesh.WedgeIndices[i * 3 + j] = Indices[i * 3 + j]; } } } } check(OutRawMesh.IsValid()); OutBuildSettings.bRecomputeNormals = false; OutBuildSettings.bRecomputeTangents = false; OutBuildSettings.bRemoveDegenerates = false; OutBuildSettings.bUseFullPrecisionUVs = RenderData.VertexBuffer.GetUseFullPrecisionUVs(); }
static void StaticMeshToSlateRenderData(const UStaticMesh& DataSource, TArray<FSlateMeshVertex>& OutSlateVerts, TArray<uint32>& OutIndexes, FVector2D& OutExtentMin, FVector2D& OutExtentMax ) { OutExtentMin = FVector2D(FLT_MAX, FLT_MAX); OutExtentMax = FVector2D(-FLT_MAX, -FLT_MAX); const FStaticMeshLODResources& LOD = DataSource.RenderData->LODResources[0]; const int32 NumSections = LOD.Sections.Num(); if (NumSections > 1) { UE_LOG(LogUMG, Warning, TEXT("StaticMesh %s has %d sections. SMeshWidget expects a static mesh with 1 section."), *DataSource.GetName(), NumSections); } else { // Populate Vertex Data { const uint32 NumVerts = LOD.PositionVertexBuffer.GetNumVertices(); OutSlateVerts.Empty(); OutSlateVerts.Reserve(NumVerts); static const int32 MAX_SUPPORTED_UV_SETS = 6; const int32 TexCoordsPerVertex = LOD.GetNumTexCoords(); if (TexCoordsPerVertex > MAX_SUPPORTED_UV_SETS) { UE_LOG(LogStaticMesh, Warning, TEXT("[%s] has %d UV sets; slate vertex data supports at most %d"), *DataSource.GetName(), TexCoordsPerVertex, MAX_SUPPORTED_UV_SETS); } for (uint32 i = 0; i < NumVerts; ++i) { // Copy Position const FVector& Position = LOD.PositionVertexBuffer.VertexPosition(i); OutExtentMin.X = FMath::Min(Position.X, OutExtentMin.X); OutExtentMin.Y = FMath::Min(Position.Y, OutExtentMin.Y); OutExtentMax.X = FMath::Max(Position.X, OutExtentMax.X); OutExtentMax.Y = FMath::Max(Position.Y, OutExtentMax.Y); // Copy Color FColor Color = (LOD.ColorVertexBuffer.GetNumVertices() > 0) ? LOD.ColorVertexBuffer.VertexColor(i) : FColor::White; // Copy all the UVs that we have, and as many as we can fit. const FVector2D& UV0 = (TexCoordsPerVertex > 0) ? LOD.VertexBuffer.GetVertexUV(i, 0) : FVector2D(1, 1); const FVector2D& UV1 = (TexCoordsPerVertex > 1) ? LOD.VertexBuffer.GetVertexUV(i, 1) : FVector2D(1, 1); const FVector2D& UV2 = (TexCoordsPerVertex > 2) ? LOD.VertexBuffer.GetVertexUV(i, 2) : FVector2D(1, 1); const FVector2D& UV3 = (TexCoordsPerVertex > 3) ? LOD.VertexBuffer.GetVertexUV(i, 3) : FVector2D(1, 1); const FVector2D& UV4 = (TexCoordsPerVertex > 4) ? LOD.VertexBuffer.GetVertexUV(i, 4) : FVector2D(1, 1); const FVector2D& UV5 = (TexCoordsPerVertex > 5) ? LOD.VertexBuffer.GetVertexUV(i, 5) : FVector2D(1, 1); OutSlateVerts.Add(FSlateMeshVertex( FVector2D(Position.X, Position.Y), Color, UV0, UV1, UV2, UV3, UV4, UV5 )); } } // Populate Index data { FIndexArrayView SourceIndexes = LOD.IndexBuffer.GetArrayView(); const int32 NumIndexes = SourceIndexes.Num(); OutIndexes.Empty(); OutIndexes.Reserve(NumIndexes); for (int32 i = 0; i < NumIndexes; ++i) { OutIndexes.Add(SourceIndexes[i]); } // Sort the index buffer such that verts are drawn in Z-order. // Assume that all triangles are coplanar with Z == SomeValue. ensure(NumIndexes % 3 == 0); for (int32 a = 0; a < NumIndexes; a += 3) { for (int32 b = 0; b < NumIndexes; b += 3) { const float VertADepth = LOD.PositionVertexBuffer.VertexPosition(OutIndexes[a]).Z; const float VertBDepth = LOD.PositionVertexBuffer.VertexPosition(OutIndexes[b]).Z; if ( VertADepth < VertBDepth ) { // Swap the order in which triangles will be drawn Swap(OutIndexes[a + 0], OutIndexes[b + 0]); Swap(OutIndexes[a + 1], OutIndexes[b + 1]); Swap(OutIndexes[a + 2], OutIndexes[b + 2]); } } } } } }