Merge remote-tracking branch 'origin/master' into 1.13

# Conflicts:
#	Flax.flaxproj
#	Source/Engine/Level/Actors/StaticModel.cpp
#	Source/Engine/Level/Prefabs/Prefab.cpp
#	Source/Engine/Tools/ModelTool/ModelTool.cpp
This commit is contained in:
2026-06-03 17:15:38 +02:00
91 changed files with 1290 additions and 579 deletions
+52 -5
View File
@@ -589,6 +589,10 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj
SERIALIZE(TriangleReduction);
SERIALIZE(SloppyOptimization);
SERIALIZE(LODTargetError);
SERIALIZE(LODTargetErrorAbsolute);
SERIALIZE(LODLockBorder);
SERIALIZE(LODPreserveUVs);
SERIALIZE(LODPreserveUVsWeight);
SERIALIZE(ImportMaterials);
SERIALIZE(CreateEmptyMaterialSlots);
SERIALIZE(ImportMaterialsAsInstances);
@@ -646,6 +650,10 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi
DESERIALIZE(TriangleReduction);
DESERIALIZE(SloppyOptimization);
DESERIALIZE(LODTargetError);
DESERIALIZE(LODTargetErrorAbsolute);
DESERIALIZE(LODLockBorder);
DESERIALIZE(LODPreserveUVs);
DESERIALIZE(LODPreserveUVsWeight);
DESERIALIZE(ImportMaterials);
DESERIALIZE(CreateEmptyMaterialSlots);
DESERIALIZE(ImportMaterialsAsInstances);
@@ -1945,8 +1953,9 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
// Automatic LOD generation
if (options.GenerateLODs && options.LODCount > 1 && data.LODs.HasItems() && options.TriangleReduction < 1.0f - ZeroTolerance)
{
InitMeshOpt();
PROFILE_CPU_NAMED("GenerateLODs");
auto lodStartTime = DateTime::NowUTC();
InitMeshOpt();
float triangleReduction = Math::Saturate(options.TriangleReduction);
int32 lodCount = Math::Max(options.LODCount, data.LODs.Count());
int32 baseLOD = Math::Clamp(options.BaseLOD, 0, lodCount - 1);
@@ -1983,13 +1992,51 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option
continue;
indices.Clear();
indices.Resize(srcMeshIndexCount);
int32 dstMeshIndexCount = {};
int32 dstMeshIndexCount = 0;
if (options.SloppyOptimization)
{
PROFILE_CPU_NAMED("meshopt_simplifySloppy");
dstMeshIndexCount = (int32)meshopt_simplifySloppy(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget, options.LODTargetError);
}
else
dstMeshIndexCount = (int32)meshopt_simplify(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget, options.LODTargetError);
if (dstMeshIndexCount <= 0 || dstMeshIndexCount > indices.Count())
continue;
{
// Build simplification flags
unsigned int simplifyOptions = 0;
if (options.LODLockBorder)
simplifyOptions |= meshopt_SimplifyLockBorder;
if (options.LODTargetErrorAbsolute)
simplifyOptions |= meshopt_SimplifyErrorAbsolute;
if (options.LODPreserveUVs && srcMesh->UVs.HasItems())
{
// Pack UV channels as attributes for meshopt_simplifyWithAttributes
int32 uvChannelCount = srcMesh->UVs.Count();
int32 attributeCount = uvChannelCount * 2; // 2 floats (U, V) per channel
Array<float> attributes;
attributes.Resize(srcMeshVertexCount * attributeCount);
Array<float> attributeWeights;
attributeWeights.Resize(attributeCount);
for (int32 ch = 0; ch < uvChannelCount; ch++)
{
for (int32 v = 0; v < srcMeshVertexCount; v++)
{
Float2 uv = srcMesh->UVs[ch][v];
attributes[v * attributeCount + ch * 2 + 0] = uv.X;
attributes[v * attributeCount + ch * 2 + 1] = uv.Y;
}
attributeWeights[ch * 2 + 0] = options.LODPreserveUVsWeight;
attributeWeights[ch * 2 + 1] = options.LODPreserveUVsWeight;
}
PROFILE_CPU_NAMED("meshopt_simplifyWithAttributes");
dstMeshIndexCount = (int32)meshopt_simplifyWithAttributes(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), attributes.Get(), sizeof(float) * attributeCount, attributeWeights.Get(), attributeCount, nullptr, dstMeshIndexCountTarget, options.LODTargetError, simplifyOptions, nullptr);
}
else
{
PROFILE_CPU_NAMED("meshopt_simplify");
dstMeshIndexCount = (int32)meshopt_simplify(indices.Get(), srcMesh->Indices.Get(), srcMeshIndexCount, (const float*)srcMesh->Positions.Get(), srcMeshVertexCount, sizeof(Float3), dstMeshIndexCountTarget, options.LODTargetError, simplifyOptions, nullptr);
}
}
if (dstMeshIndexCount <= 0 || dstMeshIndexCount >= indices.Count())
continue; // Skip if failed to generate LOD or it doesn't have less vertices than source
indices.Resize(dstMeshIndexCount);
// Generate simplified vertex buffer remapping table (use only vertices from LOD index buffer)