Files
Machinist_700km/Plugins/HoudiniEngine/Source/HoudiniEngineRuntime/Private/HoudiniStaticMesh.cpp
T
Andron666 9c38e93fa4 part7
2022-12-05 20:31:35 +05:00

466 lines
15 KiB
C++

/*
* Copyright (c) <2021> Side Effects Software Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. The name of Side Effects Software may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "HoudiniStaticMesh.h"
#include "HoudiniEngineRuntimePrivatePCH.h"
#include "Async/ParallelFor.h"
#include "MeshUtilitiesCommon.h"
UHoudiniStaticMesh::UHoudiniStaticMesh(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bHasNormals = false;
bHasTangents = false;
bHasColors = false;
NumUVLayers = 0;
bHasPerFaceMaterials = false;
}
void UHoudiniStaticMesh::Initialize(uint32 InNumVertices, uint32 InNumTriangles, uint32 InNumUVLayers, uint32 InInitialNumStaticMaterials, bool bInHasNormals, bool bInHasTangents, bool bInHasColors, bool bInHasPerFaceMaterials)
{
// Initialize the vertex positions and triangle indices arrays
VertexPositions.Init(FVector::ZeroVector, InNumVertices);
TriangleIndices.Init(FIntVector(-1, -1, -1), InNumTriangles);
if (InInitialNumStaticMaterials > 0)
StaticMaterials.Init(FStaticMaterial(), InInitialNumStaticMaterials);
else
StaticMaterials.Empty();
SetNumUVLayers(InNumUVLayers);
SetHasNormals(bInHasNormals);
SetHasTangents(bInHasTangents);
SetHasColors(bInHasColors);
SetHasPerFaceMaterials(bInHasPerFaceMaterials);
}
void UHoudiniStaticMesh::SetHasPerFaceMaterials(bool bInHasPerFaceMaterials)
{
bHasPerFaceMaterials = bInHasPerFaceMaterials;
if (bHasPerFaceMaterials)
MaterialIDsPerTriangle.Init(-1, GetNumTriangles());
else
MaterialIDsPerTriangle.Empty();
}
void UHoudiniStaticMesh::SetHasNormals(bool bInHasNormals)
{
bHasNormals = bInHasNormals;
if (bHasNormals)
VertexInstanceNormals.Init(FVector(0, 0, 1), GetNumVertexInstances());
else
VertexInstanceNormals.Empty();
}
void UHoudiniStaticMesh::SetHasTangents(bool bInHasTangents)
{
bHasTangents = bInHasTangents;
if (bHasTangents)
{
VertexInstanceUTangents.Init(FVector(1, 0, 0), GetNumVertexInstances());
VertexInstanceVTangents.Init(FVector(0, 1, 0), GetNumVertexInstances());
}
else
{
VertexInstanceUTangents.Empty();
VertexInstanceVTangents.Empty();
}
}
void UHoudiniStaticMesh::SetHasColors(bool bInHasColors)
{
bHasColors = bInHasColors;
if (bHasColors)
VertexInstanceColors.Init(FColor(127, 127, 127), GetNumVertexInstances());
else
VertexInstanceColors.Empty();
}
void UHoudiniStaticMesh::SetNumUVLayers(uint32 InNumUVLayers)
{
NumUVLayers = InNumUVLayers;
if (NumUVLayers > 0)
VertexInstanceUVs.Init(FVector2D::ZeroVector, GetNumVertexInstances() * NumUVLayers);
else
VertexInstanceUVs.Empty();
}
void UHoudiniStaticMesh::SetNumStaticMaterials(uint32 InNumMaterials)
{
if (InNumMaterials > 0)
StaticMaterials.SetNum(InNumMaterials);
else
StaticMaterials.Empty();
}
void UHoudiniStaticMesh::SetVertexPosition(uint32 InVertexIndex, const FVector& InPosition)
{
check(VertexPositions.IsValidIndex(InVertexIndex));
VertexPositions[InVertexIndex] = InPosition;
}
void UHoudiniStaticMesh::SetTriangleVertexIndices(uint32 InTriangleIndex, const FIntVector& InTriangleVertexIndices)
{
check(TriangleIndices.IsValidIndex(InTriangleIndex));
check(VertexPositions.IsValidIndex(InTriangleVertexIndices[0]));
check(VertexPositions.IsValidIndex(InTriangleVertexIndices[1]));
check(VertexPositions.IsValidIndex(InTriangleVertexIndices[2]));
TriangleIndices[InTriangleIndex] = InTriangleVertexIndices;
}
void UHoudiniStaticMesh::SetTriangleVertexNormal(uint32 InTriangleIndex, uint8 InTriangleVertexIndex, const FVector& InNormal)
{
if (!bHasNormals)
{
return;
}
check(TriangleIndices.IsValidIndex(InTriangleIndex));
const uint32 VertexInstanceIndex = InTriangleIndex * 3 + InTriangleVertexIndex;
check(VertexInstanceNormals.IsValidIndex(VertexInstanceIndex));
VertexInstanceNormals[VertexInstanceIndex] = InNormal;
}
void UHoudiniStaticMesh::SetTriangleVertexUTangent(uint32 InTriangleIndex, uint8 InTriangleVertexIndex, const FVector& InUTangent)
{
if (!bHasTangents)
{
return;
}
check(TriangleIndices.IsValidIndex(InTriangleIndex));
const uint32 VertexInstanceIndex = InTriangleIndex * 3 + InTriangleVertexIndex;
check(VertexInstanceUTangents.IsValidIndex(VertexInstanceIndex));
VertexInstanceUTangents[VertexInstanceIndex] = InUTangent;
}
void UHoudiniStaticMesh::SetTriangleVertexVTangent(uint32 InTriangleIndex, uint8 InTriangleVertexIndex, const FVector& InVTangent)
{
if (!bHasTangents)
{
return;
}
check(TriangleIndices.IsValidIndex(InTriangleIndex));
const uint32 VertexInstanceIndex = InTriangleIndex * 3 + InTriangleVertexIndex;
check(VertexInstanceVTangents.IsValidIndex(VertexInstanceIndex));
VertexInstanceVTangents[VertexInstanceIndex] = InVTangent;
}
void UHoudiniStaticMesh::SetTriangleVertexColor(uint32 InTriangleIndex, uint8 InTriangleVertexIndex, const FColor& InColor)
{
if (!bHasColors)
{
return;
}
check(TriangleIndices.IsValidIndex(InTriangleIndex));
const uint32 VertexInstanceIndex = InTriangleIndex * 3 + InTriangleVertexIndex;
check(VertexInstanceColors.IsValidIndex(VertexInstanceIndex));
VertexInstanceColors[VertexInstanceIndex] = InColor;
}
void UHoudiniStaticMesh::SetTriangleVertexUV(uint32 InTriangleIndex, uint8 InTriangleVertexIndex, uint8 InUVLayer, const FVector2D& InUV)
{
if (NumUVLayers <= 0)
{
return;
}
check(TriangleIndices.IsValidIndex(InTriangleIndex));
const uint32 VertexInstanceUVIndex = InUVLayer * GetNumVertexInstances() + InTriangleIndex * 3 + InTriangleVertexIndex;
check(VertexInstanceUVs.IsValidIndex(VertexInstanceUVIndex));
VertexInstanceUVs[VertexInstanceUVIndex] = InUV;
}
void UHoudiniStaticMesh::SetTriangleMaterialID(uint32 InTriangleIndex, int32 InMaterialID)
{
if (!bHasPerFaceMaterials)
{
return;
}
check(TriangleIndices.IsValidIndex(InTriangleIndex));
check(MaterialIDsPerTriangle.IsValidIndex(InTriangleIndex));
MaterialIDsPerTriangle[InTriangleIndex] = InMaterialID;
}
void UHoudiniStaticMesh::SetStaticMaterial(uint32 InMaterialIndex, const FStaticMaterial& InStaticMaterial)
{
check(StaticMaterials.IsValidIndex(InMaterialIndex));
StaticMaterials[InMaterialIndex] = InStaticMaterial;
}
void UHoudiniStaticMesh::CalculateNormals(bool bInComputeWeightedNormals)
{
const int32 NumVertexInstances = GetNumVertexInstances();
// Pre-allocate space in the vertex instance normals array
VertexInstanceNormals.SetNum(NumVertexInstances);
const int32 NumTriangles = GetNumTriangles();
const int32 NumVertices = GetNumVertices();
// Setup a vertex normal array
TArray<FVector> VertexNormals;
VertexNormals.SetNum(NumVertices);
// Zero all entries in VertexNormals
// for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex)
ParallelFor(NumVertices, [&VertexNormals](int32 VertexIndex)
{
VertexNormals[VertexIndex] = FVector::ZeroVector;
});
// Calculate face normals and sum them for each vertex that shares the triangle
// for (int32 TriangleIndex = 0; TriangleIndex < NumTriangles; ++TriangleIndex)
ParallelFor(NumTriangles, [this, &VertexNormals, bInComputeWeightedNormals](int32 TriangleIndex)
{
const FIntVector& TriangleVertexIndices = TriangleIndices[TriangleIndex];
if (!VertexPositions.IsValidIndex(TriangleVertexIndices[0]) ||
!VertexPositions.IsValidIndex(TriangleVertexIndices[1]) ||
!VertexPositions.IsValidIndex(TriangleVertexIndices[2]))
{
HOUDINI_LOG_WARNING(
TEXT("[UHoudiniStaticMesh::CalculateNormals]: VertexPositions index out of range %d, %d, %d, Num %d"),
TriangleVertexIndices[0], TriangleVertexIndices[1], TriangleVertexIndices[2], VertexPositions.Num());
return;
}
const FVector& V0 = VertexPositions[TriangleVertexIndices[0]];
const FVector& V1 = VertexPositions[TriangleVertexIndices[1]];
const FVector& V2 = VertexPositions[TriangleVertexIndices[2]];
FVector TriangleNormal = FVector::CrossProduct(V2 - V0, V1 - V0);
float Area = TriangleNormal.Size();
TriangleNormal /= Area;
Area /= 2.0f;
const float Weight[3] = {
bInComputeWeightedNormals ? Area * TriangleUtilities::ComputeTriangleCornerAngle(V0, V1, V2) : 1.0f,
bInComputeWeightedNormals ? Area * TriangleUtilities::ComputeTriangleCornerAngle(V1, V2, V0) : 1.0f,
bInComputeWeightedNormals ? Area * TriangleUtilities::ComputeTriangleCornerAngle(V2, V0, V1) : 1.0f,
};
for (int CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
{
const FVector WeightedNormal = TriangleNormal * Weight[CornerIndex];
if (!WeightedNormal.IsNearlyZero(SMALL_NUMBER) && !WeightedNormal.ContainsNaN())
{
if (!VertexNormals.IsValidIndex(TriangleVertexIndices[CornerIndex]))
{
HOUDINI_LOG_WARNING(
TEXT("[UHoudiniStaticMesh::CalculateNormals]: VertexNormal index out of range %d, Num %d"),
TriangleVertexIndices[CornerIndex], VertexNormals.Num());
continue;
}
VertexNormals[TriangleVertexIndices[CornerIndex]] += WeightedNormal;
}
}
});
// Normalize the vertex normals
// for (int32 VertexIndex = 0; VertexIndex < NumVertices; ++VertexIndex)
ParallelFor(NumVertices, [&VertexNormals](int32 VertexIndex)
{
VertexNormals[VertexIndex].Normalize();
});
// Copy vertex normals to vertex instance normals
// for (int32 VertexInstanceIndex = 0; VertexInstanceIndex < NumVertexInstances; ++VertexInstanceIndex)
ParallelFor(NumVertexInstances, [this, &VertexNormals](int32 VertexInstanceIndex)
{
const int32 TriangleIndex = VertexInstanceIndex / 3;
const int32 CornerIndex = VertexInstanceIndex % 3;
const FIntVector& TriangleVertexIndices = TriangleIndices[TriangleIndex];
if (!VertexNormals.IsValidIndex(TriangleVertexIndices[CornerIndex]))
{
HOUDINI_LOG_WARNING(
TEXT("[UHoudiniStaticMesh::CalculateNormals]: VertexNormals index out of range %d, Num %d"),
TriangleVertexIndices[CornerIndex], VertexNormals.Num());
return;
}
VertexInstanceNormals[VertexInstanceIndex] = VertexNormals[TriangleVertexIndices[CornerIndex]];
});
bHasNormals = true;
}
void UHoudiniStaticMesh::CalculateTangents(bool bInComputeWeightedNormals)
{
const int32 NumVertexInstances = GetNumVertexInstances();
VertexInstanceUTangents.SetNum(NumVertexInstances);
VertexInstanceVTangents.SetNum(NumVertexInstances);
// Calculate normals first if we don't have any
if (!HasNormals() || VertexInstanceNormals.Num() != NumVertexInstances)
CalculateNormals(bInComputeWeightedNormals);
// for (int32 VertexInstanceIndex = 0; VertexInstanceIndex < NumVertexInstances; ++VertexInstanceIndex)
ParallelFor(NumVertexInstances, [this](int32 VertexInstanceIndex)
{
const FVector& Normal = VertexInstanceNormals[VertexInstanceIndex];
Normal.FindBestAxisVectors(
VertexInstanceUTangents[VertexInstanceIndex], VertexInstanceVTangents[VertexInstanceIndex]);
});
bHasTangents = true;
}
void UHoudiniStaticMesh::Optimize()
{
VertexPositions.Shrink();
TriangleIndices.Shrink();
VertexInstanceColors.Shrink();
VertexInstanceNormals.Shrink();
VertexInstanceUTangents.Shrink();
VertexInstanceVTangents.Shrink();
VertexInstanceUVs.Shrink();
MaterialIDsPerTriangle.Shrink();
StaticMaterials.Shrink();
}
FBox UHoudiniStaticMesh::CalcBounds() const
{
const uint32 NumVertices = VertexPositions.Num();
if (NumVertices == 0)
return FBox();
const FVector InitPosition = VertexPositions[0];
double MinX = InitPosition.X, MaxX = InitPosition.X, MinY = InitPosition.Y, MaxY = InitPosition.Y, MinZ = InitPosition.Z, MaxZ = InitPosition.Z;
for (uint32 VertIdx = 0; VertIdx < NumVertices; ++VertIdx)
{
const FVector Position = VertexPositions[VertIdx];
if (Position.X < MinX) MinX = Position.X; else if (Position.X > MaxX) MaxX = Position.X;
if (Position.Y < MinY) MinY = Position.Y; else if (Position.Y > MaxY) MaxY = Position.Y;
if (Position.Z < MinZ) MinZ = Position.Z; else if (Position.Z > MaxZ) MaxZ = Position.Z;
}
return FBox(FVector(MinX, MinY, MinZ), FVector(MaxX, MaxY, MaxZ));
}
UMaterialInterface* UHoudiniStaticMesh::GetMaterial(int32 InMaterialIndex)
{
check(StaticMaterials.IsValidIndex(InMaterialIndex));
return StaticMaterials[InMaterialIndex].MaterialInterface;
}
int32 UHoudiniStaticMesh::GetMaterialIndex(FName InMaterialSlotName) const
{
if (InMaterialSlotName == NAME_None)
return -1;
const uint32 NumMaterials = StaticMaterials.Num();
for (uint32 MaterialIndex = 0; MaterialIndex < NumMaterials; ++MaterialIndex)
{
if (StaticMaterials[MaterialIndex].MaterialSlotName == InMaterialSlotName)
return (int32)MaterialIndex;
}
return -1;
}
bool UHoudiniStaticMesh::IsValid(bool bInSkipVertexIndicesCheck) const
{
// Validate the number of vertices, indices and triangles. This is basically the same function as FRawMesh::IsValid()
const int32 NumVertices = GetNumVertices();
const int32 NumVertexInstances = GetNumVertexInstances();
const int32 NumTriangles = GetNumTriangles();
auto ValidateAttributeArraySize = [](int32 InArrayNum, int32 InExpectedSize)
{
return InArrayNum == 0 || InArrayNum == InExpectedSize;
};
bool bValid = NumVertices > 0
&& NumVertexInstances > 0
&& NumTriangles > 0
&& (NumVertexInstances / 3) == NumTriangles
&& ValidateAttributeArraySize(MaterialIDsPerTriangle.Num(), NumTriangles)
&& ValidateAttributeArraySize(VertexInstanceNormals.Num(), NumVertexInstances)
&& ValidateAttributeArraySize(VertexInstanceUTangents.Num(), NumVertexInstances)
&& ValidateAttributeArraySize(VertexInstanceVTangents.Num(), NumVertexInstances)
&& ValidateAttributeArraySize(VertexInstanceColors.Num(), NumVertexInstances)
&& NumUVLayers >= 0
&& VertexInstanceUVs.Num() == NumUVLayers * NumVertexInstances;
if (!bInSkipVertexIndicesCheck)
{
int32 TriangleIndex = 0;
while (bValid && TriangleIndex < NumTriangles)
{
bValid = bValid && (TriangleIndices[TriangleIndex].X < NumVertices);
bValid = bValid && (TriangleIndices[TriangleIndex].Y < NumVertices);
bValid = bValid && (TriangleIndices[TriangleIndex].Z < NumVertices);
TriangleIndex++;
}
}
return bValid;
}
void UHoudiniStaticMesh::Serialize(FArchive &InArchive)
{
Super::Serialize(InArchive);
VertexPositions.Shrink();
VertexPositions.BulkSerialize(InArchive);
TriangleIndices.Shrink();
TriangleIndices.BulkSerialize(InArchive);
VertexInstanceColors.Shrink();
VertexInstanceColors.BulkSerialize(InArchive);
VertexInstanceNormals.Shrink();
VertexInstanceNormals.BulkSerialize(InArchive);
VertexInstanceUTangents.Shrink();
VertexInstanceUTangents.BulkSerialize(InArchive);
VertexInstanceVTangents.Shrink();
VertexInstanceVTangents.BulkSerialize(InArchive);
VertexInstanceUVs.Shrink();
VertexInstanceUVs.BulkSerialize(InArchive);
MaterialIDsPerTriangle.Shrink();
MaterialIDsPerTriangle.BulkSerialize(InArchive);
}