/**
 * 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();
}
/**
 * Constructs a raw mesh from legacy raw triangles.
 */
static bool BuildRawMeshFromRawTriangles(
	FRawMesh& OutRawMesh,
	FMeshBuildSettings& OutBuildSettings,
	struct FStaticMeshTriangle* RawTriangles,
	int32 NumTriangles,
	const TCHAR* MeshName
	)
{
	int32 NumWedges = NumTriangles * 3;
	OutRawMesh.Empty();
	OutRawMesh.FaceMaterialIndices.Empty(NumTriangles);
	OutRawMesh.FaceSmoothingMasks.Empty(NumWedges);
	OutRawMesh.VertexPositions.Empty(NumWedges);
	OutRawMesh.WedgeIndices.Empty(NumWedges);

	int32 NumTexCoords = 0;
	bool bHaveNormals = false;
	bool bHaveTangents = false;
	bool bHaveColors = false;
	bool bCorruptFlags = false;
	bool bCorruptNormals = false;
	bool bCorruptTangents = false;
	bool bCorruptPositions = false;

	for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex)
    {
		FStaticMeshTriangle* Tri = RawTriangles + TriIndex;
		OutRawMesh.FaceMaterialIndices.Add(Tri->MaterialIndex);
		OutRawMesh.FaceSmoothingMasks.Add(Tri->SmoothingMask);

		bCorruptFlags |= (RawTriangles[0].bExplicitNormals & 0xFFFFFFFE) != 0
			|| (RawTriangles[0].bOverrideTangentBasis & 0xFFFFFFFE) != 0;

		for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
        {
			FVector Position = Tri->Vertices[CornerIndex];
			OutRawMesh.WedgeIndices.Add(OutRawMesh.VertexPositions.Add(Position));
			NumTexCoords = FMath::Max(NumTexCoords, Tri->NumUVs);
			bHaveNormals |= Tri->TangentZ[CornerIndex] != FVector::ZeroVector;
			bHaveTangents |= (Tri->TangentX[CornerIndex] != FVector::ZeroVector
				&& Tri->TangentY[CornerIndex] != FVector::ZeroVector);
			bHaveColors |= Tri->Colors[CornerIndex] != FColor::White;

			bCorruptPositions |= Position.ContainsNaN();
			bCorruptTangents |= Tri->TangentX[CornerIndex].ContainsNaN()
				|| Tri->TangentY[CornerIndex].ContainsNaN();
			bCorruptNormals |= Tri->TangentZ[CornerIndex].ContainsNaN();
        }
    }

	bool bTooManyTexCoords = NumTexCoords > 8;
	NumTexCoords = FMath::Min(NumTexCoords, 8); // FStaticMeshTriangle has at most 8 texture coordinates.
	NumTexCoords = FMath::Min<int32>(NumTexCoords, MAX_MESH_TEXTURE_COORDS);

	for (int32 TexCoordIndex = 0; TexCoordIndex < FMath::Max(1, NumTexCoords); ++TexCoordIndex)
	{
		OutRawMesh.WedgeTexCoords[TexCoordIndex].AddZeroed(NumTriangles * 3);
	}

	for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex)
	{
		FStaticMeshTriangle* Tri = RawTriangles + TriIndex;
		for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
		{
			for (int32 TexCoordIndex = 0; TexCoordIndex < NumTexCoords; ++TexCoordIndex)
			{
				FVector2D UV = TexCoordIndex < Tri->NumUVs ? Tri->UVs[CornerIndex][TexCoordIndex] : FVector2D(0.0f,0.0f);
				OutRawMesh.WedgeTexCoords[TexCoordIndex][TriIndex * 3 + CornerIndex] = UV;
			}
		}
	}

	if (bHaveNormals && !bCorruptNormals)
	{
		OutRawMesh.WedgeTangentZ.AddZeroed(NumTriangles * 3);
		for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex)
		{
			if (RawTriangles[TriIndex].bExplicitNormals || RawTriangles[TriIndex].bOverrideTangentBasis)
			{
				for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
				{
					OutRawMesh.WedgeTangentZ[TriIndex * 3 + CornerIndex] =
						RawTriangles[TriIndex].TangentZ[CornerIndex];
				}
			}
		}
	}
	if (bHaveTangents && !bCorruptTangents)
	{
		OutRawMesh.WedgeTangentX.AddZeroed(NumTriangles * 3);
		OutRawMesh.WedgeTangentY.AddZeroed(NumTriangles * 3);
		for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex)
	{
			if (RawTriangles[TriIndex].bOverrideTangentBasis)
			{
				for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
				{
					OutRawMesh.WedgeTangentX[TriIndex * 3 + CornerIndex] =
						RawTriangles[TriIndex].TangentX[CornerIndex];
					OutRawMesh.WedgeTangentY[TriIndex * 3 + CornerIndex] =
						RawTriangles[TriIndex].TangentY[CornerIndex];
				}
			}
		}
	}

	if (bHaveColors)
	{
		OutRawMesh.WedgeColors.AddUninitialized(NumTriangles * 3);
		for (int32 TriIndex = 0; TriIndex < NumTriangles; ++TriIndex)
		{
			for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
			{
				OutRawMesh.WedgeColors[TriIndex * 3 + CornerIndex] =
					RawTriangles[TriIndex].Colors[CornerIndex];
			}
		}
	}

	if (bCorruptPositions || bCorruptFlags || bCorruptNormals || bCorruptTangents || bTooManyTexCoords)
	{
		UE_LOG(LogStaticMesh,Verbose,TEXT("Legacy raw triangles CORRUPT (%s%s%s%s%s) for %s"),
			bCorruptPositions ? TEXT("NaN positions,") : TEXT(""),
			bCorruptFlags ? TEXT("flags,") : TEXT(""),
			bCorruptNormals ? TEXT("NaN normals,") : TEXT(""),
			bCorruptTangents ? TEXT("NaN tangents,") : TEXT(""),
			bTooManyTexCoords ? TEXT(">8 texcoords") : TEXT(""),
			MeshName
			);
	}

	if (bCorruptPositions)
	{
		OutRawMesh = FRawMesh();
	}

	OutBuildSettings.bRecomputeTangents = !bHaveTangents || bCorruptTangents;
	OutBuildSettings.bRecomputeNormals = !bHaveNormals || bCorruptNormals;

	return !(bCorruptPositions || bCorruptFlags || bTooManyTexCoords) && OutRawMesh.IsValid();
}