// Copyright Epic Games, Inc. All Rights Reserved. #include "ProxyLODMeshConvertUtils.h" #include "ProxyLODMeshUtilities.h" #include "StaticMeshOperations.h" static void ResetStaticMeshDescription(FMeshDescription& MeshDecription) { MeshDecription.Empty(); FStaticMeshAttributes Attributes(MeshDecription); Attributes.Register(); } // Convert QuadMesh to Triangles by splitting void ProxyLOD::MixedPolyMeshToRawMesh(const FMixedPolyMesh& SimpleMesh, FMeshDescription& DstRawMesh) { TRACE_CPUPROFILER_EVENT_SCOPE(ProxyLOD::MixedPolyMeshToRawMesh) ResetStaticMeshDescription(DstRawMesh); FStaticMeshAttributes Attributes(DstRawMesh); TVertexAttributesRef VertexPositions = Attributes.GetVertexPositions(); TEdgeAttributesRef EdgeHardnesses = Attributes.GetEdgeHardnesses(); TPolygonGroupAttributesRef PolygonGroupImportedMaterialSlotNames = Attributes.GetPolygonGroupMaterialSlotNames(); TVertexInstanceAttributesRef VertexInstanceNormals = Attributes.GetVertexInstanceNormals(); TVertexInstanceAttributesRef VertexInstanceTangents = Attributes.GetVertexInstanceTangents(); TVertexInstanceAttributesRef VertexInstanceBinormalSigns = Attributes.GetVertexInstanceBinormalSigns(); TVertexInstanceAttributesRef VertexInstanceColors = Attributes.GetVertexInstanceColors(); TVertexInstanceAttributesRef VertexInstanceUVs = Attributes.GetVertexInstanceUVs(); // Splitting a quad doesn't introduce any new verts. const uint32 DstNumVerts = SimpleMesh.Points.size(); const uint32 NumQuads = SimpleMesh.Quads.size(); // Each quad becomes 2 triangles. const uint32 DstNumTris = 2 * NumQuads + SimpleMesh.Triangles.size(); // Each Triangle has 3 corners const uint32 DstNumIndexes = 3 * DstNumTris; if (VertexInstanceUVs.GetNumChannels() < 1) { VertexInstanceUVs.SetNumChannels(1); } FPolygonGroupID PolygonGroupID = INDEX_NONE; if (DstRawMesh.PolygonGroups().Num() == 0) { PolygonGroupID = DstRawMesh.CreatePolygonGroup(); PolygonGroupImportedMaterialSlotNames[PolygonGroupID] = FName(*FString::Printf(TEXT("ProxyLOD_Material_%d"), FMath::Rand())); } else { PolygonGroupID = DstRawMesh.PolygonGroups().GetFirstValidID(); } // Copy the vertices over TMap VertexIDMap; VertexIDMap.Reserve(DstNumVerts); { for (uint32 i = 0, I = DstNumVerts; i < I; ++i) { const openvdb::Vec3s& Vertex = SimpleMesh.Points[i]; const FVertexID NewVertexID = DstRawMesh.CreateVertex(); VertexPositions[NewVertexID] = FVector3f(Vertex[0], Vertex[1], Vertex[2]); VertexIDMap.Add(i, NewVertexID); } } // Connectivity: auto CreateTriangle = [&DstRawMesh, PolygonGroupID, &VertexInstanceNormals, &VertexInstanceTangents, &VertexInstanceBinormalSigns, &VertexInstanceColors, &VertexInstanceUVs, &EdgeHardnesses](FVertexID TriangleIndex[3]) { TArray VertexInstanceIDs; VertexInstanceIDs.SetNum(3); for (int32 Corner = 0; Corner < 3; ++Corner) { VertexInstanceIDs[Corner] = DstRawMesh.CreateVertexInstance(TriangleIndex[Corner]); VertexInstanceTangents[VertexInstanceIDs[Corner]] = FVector3f(1, 0, 0); VertexInstanceNormals[VertexInstanceIDs[Corner]] = FVector3f(0, 0, 1); VertexInstanceBinormalSigns[VertexInstanceIDs[Corner]] = GetBasisDeterminantSign((FVector)VertexInstanceTangents[VertexInstanceIDs[Corner]].GetSafeNormal(), (FVector)(VertexInstanceNormals[VertexInstanceIDs[Corner]] ^ VertexInstanceTangents[VertexInstanceIDs[Corner]]).GetSafeNormal(), (FVector)VertexInstanceNormals[VertexInstanceIDs[Corner]].GetSafeNormal()); VertexInstanceColors[VertexInstanceIDs[Corner]] = FVector4f(0.0f); VertexInstanceUVs.Set(VertexInstanceIDs[Corner], 0, FVector2f(0.0f, 0.0f)); } // Insert a polygon into the mesh DstRawMesh.CreatePolygon(PolygonGroupID, VertexInstanceIDs); }; { for (uint32 q = 0, Q = NumQuads; q < Q; ++q) { const openvdb::Vec4I& Quad = SimpleMesh.Quads[q]; // add as two triangles to raw mesh bool bClockWiseTriangle = true; #if (PROXYLOD_CLOCKWISE_TRIANGLES != 1) bClockWiseTriangle = false; #endif FVertexID VertexIndexes[3]; VertexIndexes[0] = VertexIDMap[Quad[0]]; VertexIndexes[1] = bClockWiseTriangle ? VertexIDMap[Quad[1]] : VertexIDMap[Quad[3]]; VertexIndexes[2] = VertexIDMap[Quad[2]]; CreateTriangle(VertexIndexes); VertexIndexes[0] = VertexIDMap[Quad[2]]; VertexIndexes[1] = bClockWiseTriangle ? VertexIDMap[Quad[3]] : VertexIDMap[Quad[1]]; VertexIndexes[2] = VertexIDMap[Quad[0]]; CreateTriangle(VertexIndexes); } // add the SimpleMesh triangles. uint32 IndexStop = SimpleMesh.Triangles.size(); for (uint32 t = 0, T = IndexStop; t < T; ++t) { const openvdb::Vec3I& Tri = SimpleMesh.Triangles[t]; bool bClockWiseTriangle = true; #if (PROXYLOD_CLOCKWISE_TRIANGLES != 1) bClockWiseTriangle = false; #endif FVertexID VertexIndexes[3]; VertexIndexes[0] = bClockWiseTriangle ? VertexIDMap[Tri[0]] : VertexIDMap[Tri[2]]; VertexIndexes[1] = VertexIDMap[Tri[1]]; VertexIndexes[2] = bClockWiseTriangle ? VertexIDMap[Tri[2]] : VertexIDMap[Tri[0]]; CreateTriangle(VertexIndexes); } } } void ProxyLOD::AOSMeshToRawMesh(const FAOSMesh& AOSMesh, FMeshDescription& OutRawMesh) { ResetStaticMeshDescription(OutRawMesh); FStaticMeshAttributes Attributes(OutRawMesh); TVertexAttributesRef VertexPositions = Attributes.GetVertexPositions(); TEdgeAttributesRef EdgeHardnesses = Attributes.GetEdgeHardnesses(); TPolygonGroupAttributesRef PolygonGroupImportedMaterialSlotNames = Attributes.GetPolygonGroupMaterialSlotNames(); TVertexInstanceAttributesRef VertexInstanceNormals = Attributes.GetVertexInstanceNormals(); TVertexInstanceAttributesRef VertexInstanceTangents = Attributes.GetVertexInstanceTangents(); TVertexInstanceAttributesRef VertexInstanceBinormalSigns = Attributes.GetVertexInstanceBinormalSigns(); TVertexInstanceAttributesRef VertexInstanceColors = Attributes.GetVertexInstanceColors(); TVertexInstanceAttributesRef VertexInstanceUVs = Attributes.GetVertexInstanceUVs(); const uint32 DstNumPositions = AOSMesh.GetNumVertexes(); const uint32 DstNumIndexes = AOSMesh.GetNumIndexes(); if (VertexInstanceUVs.GetNumChannels() < 1) { VertexInstanceUVs.SetNumChannels(1); } FPolygonGroupID PolygonGroupID = INDEX_NONE; if (OutRawMesh.PolygonGroups().Num() == 0) { PolygonGroupID = OutRawMesh.CreatePolygonGroup(); PolygonGroupImportedMaterialSlotNames[PolygonGroupID] = FName(*FString::Printf(TEXT("ProxyLOD_Material_%d"), FMath::Rand())); } else { PolygonGroupID = OutRawMesh.PolygonGroups().GetFirstValidID(); } checkSlow(DstNumIndexes % 3 == 0); // Copy the vertices over TMap VertexIDMap; VertexIDMap.Reserve(DstNumPositions); { const auto& AOSVertexes = AOSMesh.Vertexes; for (uint32 i = 0, I = DstNumPositions; i < I; ++i) { const FVector3f& Position = AOSVertexes[i].GetPos(); const FVertexID NewVertexID = OutRawMesh.CreateVertex(); VertexPositions[NewVertexID] = Position; VertexIDMap.Add(i, NewVertexID); } checkSlow(VertexPositions.GetNumElements() == DstNumPositions); } const uint32* AOSIndexes = AOSMesh.Indexes; // Connectivity: auto CreateTriangle = [&OutRawMesh, PolygonGroupID, &VertexInstanceNormals, &VertexInstanceTangents, &VertexInstanceBinormalSigns, &VertexInstanceColors, &VertexInstanceUVs, &EdgeHardnesses](const FVertexID TriangleIndex[3], const FVector3f Normals[3]) { TArray VertexInstanceIDs; VertexInstanceIDs.SetNum(3); for (int32 Corner = 0; Corner < 3; ++Corner) { VertexInstanceIDs[Corner] = OutRawMesh.CreateVertexInstance(TriangleIndex[Corner]); VertexInstanceTangents[VertexInstanceIDs[Corner]] = FVector3f(1, 0, 0); VertexInstanceNormals[VertexInstanceIDs[Corner]] = Normals[Corner]; VertexInstanceBinormalSigns[VertexInstanceIDs[Corner]] = GetBasisDeterminantSign((FVector)VertexInstanceTangents[VertexInstanceIDs[Corner]].GetSafeNormal(), (FVector)(VertexInstanceNormals[VertexInstanceIDs[Corner]] ^ VertexInstanceTangents[VertexInstanceIDs[Corner]]).GetSafeNormal(), (FVector)VertexInstanceNormals[VertexInstanceIDs[Corner]].GetSafeNormal()); VertexInstanceColors[VertexInstanceIDs[Corner]] = FVector4f(1.0f); VertexInstanceUVs.Set(VertexInstanceIDs[Corner], 0, FVector2f(0.0f, 0.0f)); } // Insert a polygon into the mesh OutRawMesh.CreatePolygon(PolygonGroupID, VertexInstanceIDs); }; { uint32 IndexStop = DstNumIndexes/3; for (uint32 t = 0, T = IndexStop; t < T; ++t) { FVertexID VertexIndexes[3]; FVector3f Normals[3]; for (int32 Corner = 0; Corner < 3; ++Corner) { VertexIndexes[Corner] = VertexIDMap[AOSIndexes[(t*3) + Corner]]; const auto& AOSVertex = AOSMesh.Vertexes[AOSIndexes[(t*3) + Corner]]; Normals[Corner] = AOSVertex.Normal; } CreateTriangle(VertexIndexes, Normals); } } } void ProxyLOD::VertexDataMeshToRawMesh(const FVertexDataMesh& SrcVertexDataMesh, FMeshDescription& OutRawMesh) { ResetStaticMeshDescription(OutRawMesh); FStaticMeshAttributes Attributes(OutRawMesh); TVertexAttributesRef VertexPositions = Attributes.GetVertexPositions(); TEdgeAttributesRef EdgeHardnesses = Attributes.GetEdgeHardnesses(); TPolygonGroupAttributesRef PolygonGroupImportedMaterialSlotNames = Attributes.GetPolygonGroupMaterialSlotNames(); TVertexInstanceAttributesRef VertexInstanceNormals = Attributes.GetVertexInstanceNormals(); TVertexInstanceAttributesRef VertexInstanceTangents = Attributes.GetVertexInstanceTangents(); TVertexInstanceAttributesRef VertexInstanceBinormalSigns = Attributes.GetVertexInstanceBinormalSigns(); TVertexInstanceAttributesRef VertexInstanceColors = Attributes.GetVertexInstanceColors(); TVertexInstanceAttributesRef VertexInstanceUVs = Attributes.GetVertexInstanceUVs(); const uint32 DstNumPositions = SrcVertexDataMesh.Points.Num(); const uint32 DstNumIndexes = SrcVertexDataMesh.Indices.Num(); const uint32 SrcNumTriangles = DstNumIndexes / 3; FPolygonGroupID PolygonGroupID = INDEX_NONE; if (OutRawMesh.PolygonGroups().Num() == 0) { PolygonGroupID = OutRawMesh.CreatePolygonGroup(); PolygonGroupImportedMaterialSlotNames[PolygonGroupID] = FName(*FString::Printf(TEXT("ProxyLOD_Material_%d"), FMath::Rand())); } else { PolygonGroupID = OutRawMesh.PolygonGroups().GetFirstValidID(); } checkSlow(DstNumIndexes % 3 == 0); // Copy the vertices over TMap VertexIDMap; VertexIDMap.Reserve(DstNumPositions); { const TArray& SrcPositions = SrcVertexDataMesh.Points; for (uint32 i = 0, I = DstNumPositions; i < I; ++i) { const FVector3f& Position = SrcPositions[i]; const FVertexID NewVertexID = OutRawMesh.CreateVertex(); VertexPositions[NewVertexID] = Position; VertexIDMap.Add(i, NewVertexID); } checkSlow(VertexPositions.GetNumElements() == DstNumPositions); } const bool bSrcHasTangentSpace = SrcVertexDataMesh.Tangent.Num() != 0 && SrcVertexDataMesh.BiTangent.Num() != 0 && SrcVertexDataMesh.Normal.Num() != 0; // Connectivity: auto CreateTriangle = [&OutRawMesh, &SrcVertexDataMesh, bSrcHasTangentSpace, &VertexIDMap, PolygonGroupID, &VertexInstanceNormals, &VertexInstanceTangents, &VertexInstanceBinormalSigns, &VertexInstanceColors, &VertexInstanceUVs, &EdgeHardnesses](const uint32 TriangleIndices[3]) { int32 TriangleIndex = TriangleIndices[0] / 3; FVertexID VertexIndexes[3]; TArray VertexInstanceIDs; VertexInstanceIDs.SetNum(3); for (int32 Corner = 0; Corner < 3; ++Corner) { int32 SrcIndex = SrcVertexDataMesh.Indices[TriangleIndices[Corner]]; VertexIndexes[Corner] = VertexIDMap[SrcIndex]; VertexInstanceIDs[Corner] = OutRawMesh.CreateVertexInstance(VertexIndexes[Corner]); //Tangents if (bSrcHasTangentSpace) { FVector3f Tangent = SrcVertexDataMesh.Tangent[SrcIndex]; FVector3f BiTangent = SrcVertexDataMesh.BiTangent[SrcIndex]; FVector3f Normal = SrcVertexDataMesh.Normal[SrcIndex]; VertexInstanceTangents[VertexInstanceIDs[Corner]] = Tangent; VertexInstanceBinormalSigns[VertexInstanceIDs[Corner]] = GetBasisDeterminantSign((FVector)Tangent, (FVector)BiTangent, (FVector)Normal); VertexInstanceNormals[VertexInstanceIDs[Corner]] = Normal; } else { VertexInstanceTangents[VertexInstanceIDs[Corner]] = FVector3f(1, 0, 0); VertexInstanceNormals[VertexInstanceIDs[Corner]] = FVector3f(0, 0, 1); VertexInstanceBinormalSigns[VertexInstanceIDs[Corner]] = GetBasisDeterminantSign((FVector)VertexInstanceTangents[VertexInstanceIDs[Corner]].GetSafeNormal(), (FVector)(VertexInstanceNormals[VertexInstanceIDs[Corner]] ^ VertexInstanceTangents[VertexInstanceIDs[Corner]]).GetSafeNormal(), (FVector)VertexInstanceNormals[VertexInstanceIDs[Corner]].GetSafeNormal()); } //Color if (SrcVertexDataMesh.FaceColors.Num() == 0) { VertexInstanceColors[VertexInstanceIDs[Corner]] = FVector4f(1.0f); } else { VertexInstanceColors[VertexInstanceIDs[Corner]] = FVector4f(FLinearColor(SrcVertexDataMesh.FaceColors[TriangleIndex])); } //UVs if (SrcVertexDataMesh.UVs.Num() == 0) { VertexInstanceUVs.Set(VertexInstanceIDs[Corner], FVector2f(0.0f, 0.0f)); } else { VertexInstanceUVs.Set(VertexInstanceIDs[Corner], SrcVertexDataMesh.UVs[SrcIndex]); } } // Insert a polygon into the mesh OutRawMesh.CreatePolygon(PolygonGroupID, VertexInstanceIDs); }; { uint32 RangeStop = DstNumIndexes / 3; for (uint32 i = 0, I = RangeStop; i < I; ++i) { uint32 SrcIndexes[3]; for (int32 Corner = 0; Corner < 3; ++Corner) { SrcIndexes[Corner] = (i * 3) + Corner; } CreateTriangle(SrcIndexes); } checkSlow(OutRawMesh.VertexInstances().Num() == DstNumIndexes); } //Put everybody in the same smoothgroup by default TArray FaceSmoothingMasks; FaceSmoothingMasks.AddZeroed(SrcNumTriangles); if (SrcVertexDataMesh.FacePartition.Num() != 0) { for (uint32 FaceIndex = 0; FaceIndex < SrcNumTriangles; ++FaceIndex) { FaceSmoothingMasks[FaceIndex] = (1 << (SrcVertexDataMesh.FacePartition[FaceIndex] % 32)); } } FStaticMeshOperations::ConvertSmoothGroupToHardEdges(FaceSmoothingMasks, OutRawMesh); } // Converts a raw mesh to a vertex data mesh. This is potentially has some loss since the // raw mesh is nominally a per-index data structure and the vertex data mesh is a per-vertex structure. // In addition, this only transfers the first texture coordinate and ignores material ids and vertex colors. void ProxyLOD::RawMeshToVertexDataMesh(const FMeshDescription& SrcRawMesh, FVertexDataMesh& DstVertexDataMesh) { FStaticMeshConstAttributes Attributes(SrcRawMesh); TVertexAttributesConstRef VertexPositions = Attributes.GetVertexPositions(); TVertexInstanceAttributesConstRef VertexInstanceNormals = Attributes.GetVertexInstanceNormals(); TVertexInstanceAttributesConstRef VertexInstanceTangents = Attributes.GetVertexInstanceTangents(); TVertexInstanceAttributesConstRef VertexInstanceBinormalSigns = Attributes.GetVertexInstanceBinormalSigns(); TVertexInstanceAttributesConstRef VertexInstanceUVs = Attributes.GetVertexInstanceUVs(); const uint32 DstNumPositions = SrcRawMesh.Vertices().Num(); uint32 DstNumIndexes = SrcRawMesh.Triangles().Num() * 3; // Copy the vertices over TMap VertexIDToDstVertexIndex; VertexIDToDstVertexIndex.Reserve(SrcRawMesh.Vertices().Num()); { // Allocate the space for the verts in the VertexDataMesh ResizeArray(DstVertexDataMesh.Points, DstNumPositions); uint32 VertexCount = 0; for (const FVertexID& VertexID : SrcRawMesh.Vertices().GetElementIDs()) { DstVertexDataMesh.Points[VertexCount] = VertexPositions[VertexID]; VertexIDToDstVertexIndex.Add(VertexID, VertexCount); VertexCount++; } } // Connectivity: uint32 VertexInstanceCount = 0; TArray& DstIndices = DstVertexDataMesh.Indices; ResizeArray(DstIndices, DstNumIndexes); TArray& DstTangentArray = DstVertexDataMesh.Tangent; ResizeArray(DstTangentArray, DstNumPositions); TArray& DstBiTangentArray = DstVertexDataMesh.BiTangent; ResizeArray(DstBiTangentArray, DstNumPositions); TArray& DstNormalArray = DstVertexDataMesh.Normal; ResizeArray(DstNormalArray, DstNumPositions); TArray& DstUVs = DstVertexDataMesh.UVs; ResizeArray(DstUVs, DstNumPositions); //Iterate all triangle and add the indices for (const FTriangleID TriangleID : SrcRawMesh.Triangles().GetElementIDs()) { for (int32 Corner = 0; Corner < 3; ++Corner) { const FVertexInstanceID& VertexInstanceID = SrcRawMesh.GetTriangleVertexInstance(TriangleID, Corner); DstIndices[VertexInstanceCount] = VertexIDToDstVertexIndex[SrcRawMesh.GetVertexInstanceVertex(VertexInstanceID)]; // Copy the tangent space: // NB: The tangent space is stored per-index in the raw mesh, but only per-vertex in the vertex data mesh. // We assume that the raw mesh per-index data is really duplicated per-vertex data! DstTangentArray[DstIndices[VertexInstanceCount]] = VertexInstanceTangents[VertexInstanceID]; DstBiTangentArray[DstIndices[VertexInstanceCount]] = FVector3f::CrossProduct(VertexInstanceNormals[VertexInstanceID], VertexInstanceTangents[VertexInstanceID]).GetSafeNormal() * VertexInstanceBinormalSigns[VertexInstanceID]; DstNormalArray[DstIndices[VertexInstanceCount]] = VertexInstanceNormals[VertexInstanceID]; // Copy the UVs: // NB: The UVs is stored per-index in the raw mesh, but only per-vertex in the vertex data mesh. // We assume that the raw mesh per-index data is really duplicated per-vertex data! if (VertexInstanceUVs.GetNumChannels() == 0) { DstUVs[DstIndices[VertexInstanceCount]] = FVector2f(0.0f, 0.0f); } else { DstUVs[DstIndices[VertexInstanceCount]] = VertexInstanceUVs.Get(VertexInstanceID, 0); } VertexInstanceCount++; } } int32 NumTriangles = VertexInstanceCount / 3; TArray FaceSmoothingMasks; FaceSmoothingMasks.AddZeroed(NumTriangles); FStaticMeshOperations::ConvertHardEdgesToSmoothGroup(SrcRawMesh, FaceSmoothingMasks); TArray& DstFacePartition = DstVertexDataMesh.FacePartition; ResizeArray(DstFacePartition, NumTriangles); for (int32 FaceIndex = 0; FaceIndex < NumTriangles; ++FaceIndex) { DstFacePartition[FaceIndex] = 0; for (int32 BitIndex = 0; BitIndex < 32; ++BitIndex) { uint32 BitMask = FMath::Pow(2.f, BitIndex); DstFacePartition[FaceIndex] += ((FaceSmoothingMasks[FaceIndex] & BitMask) > 0); } } } template static void CopyIndexAndPos(const TAOSMesh& AOSMesh, FVertexDataMesh& VertexDataMesh) { const uint32 DstNumPositions = AOSMesh.GetNumVertexes(); const uint32 DstNumIndexes = AOSMesh.GetNumIndexes(); checkSlow(DstNumIndexes % 3 == 0); TArray& WedgeIndices = VertexDataMesh.Indices; ResizeArray(VertexDataMesh.Points, DstNumPositions); ResizeArray(WedgeIndices, DstNumIndexes); // Copy the vertices over { ProxyLOD::Parallel_For(ProxyLOD::FUIntRange(0, DstNumPositions), [&VertexDataMesh, &AOSMesh](const ProxyLOD::FUIntRange& Range) { FVector3f* Points = VertexDataMesh.Points.GetData(); for (uint32 i = Range.begin(), I = Range.end(); i < I; ++i) { const FVector3f& Position = AOSMesh.Vertexes[i].GetPos(); Points[i] = Position; } } ); } // Connectivity { ProxyLOD::Parallel_For(ProxyLOD::FUIntRange(0, DstNumIndexes), [&AOSMesh, &WedgeIndices](const ProxyLOD::FUIntRange& Range) { for (uint32 i = Range.begin(), I = Range.end(); i < I; ++i) { WedgeIndices[i] = AOSMesh.Indexes[i]; } } ); checkSlow(WedgeIndices.Num() == DstNumIndexes); } } template static void CopyNormals(const TAOSMesh& AOSMesh, FVertexDataMesh& VertexDataMesh) { // Copy the tangent space attributes. const uint32 DstNumPositions = AOSMesh.GetNumVertexes(); checkSlow(AOSMesh.GetNumIndexes() % 3 == 0); TArray& NormalArray = VertexDataMesh.Normal; ResizeArray(NormalArray, DstNumPositions); // Transfer the normal { ProxyLOD::Parallel_For(ProxyLOD::FUIntRange(0, DstNumPositions), [&AOSMesh, &NormalArray](const ProxyLOD::FUIntRange& Range) { for (uint32 i = Range.begin(), I = Range.end(); i < I; ++i) { const auto& AOSVertex = AOSMesh.Vertexes[i]; NormalArray[i] = AOSVertex.Normal; } } ); } } // Populate a VertexDataMesh with the information in the Array of Structs mesh. template static void AOSMeshToVertexDataMesh(const TAOSMesh& AOSMesh, FVertexDataMesh& VertexDataMesh) { TRACE_CPUPROFILER_EVENT_SCOPE(AOSMeshToVertexDataMesh) // Copy the topology and geometry of the mesh CopyIndexAndPos(AOSMesh, VertexDataMesh); // adds t = (1,0,0) bt = (0, 1, 0) n = (0, 0, 1) ProxyLOD::AddDefaultTangentSpace(VertexDataMesh); // Copy the tangent space attributes. CopyNormals(AOSMesh, VertexDataMesh); } // The posistion only specialization only adds a default tangent space template <> void AOSMeshToVertexDataMesh(const TAOSMesh& AOSMesh, FVertexDataMesh& VertexDataMesh) { // Copy the topology and geometry of the mesh CopyIndexAndPos(AOSMesh, VertexDataMesh); // adds t = (1,0,0) bt = (0, 1, 0) n = (0, 0, 1) ProxyLOD::AddDefaultTangentSpace(VertexDataMesh); } void ProxyLOD::ConvertMesh(const FAOSMesh& InMesh, FVertexDataMesh& OutMesh) { AOSMeshToVertexDataMesh(InMesh, OutMesh); } void ProxyLOD::ConvertMesh(const FAOSMesh& InMesh, FMeshDescription& OutMesh) { AOSMeshToRawMesh(InMesh, OutMesh); } void ProxyLOD::ConvertMesh(const FVertexDataMesh& InMesh, FMeshDescription& OutMesh) { VertexDataMeshToRawMesh(InMesh, OutMesh); } void ProxyLOD::ConvertMesh(const FMeshDescription& InMesh, FVertexDataMesh& OutMesh) { RawMeshToVertexDataMesh(InMesh, OutMesh); }