Add and use Max Mesh Position Error to Build Settings for automatic mesh vertex positions storage

This commit is contained in:
Wojtek Figat
2025-09-12 17:53:14 +02:00
parent b537a80031
commit f323fdb592
4 changed files with 49 additions and 9 deletions

View File

@@ -3,6 +3,7 @@
#include "ModelBase.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Core/Config/BuildSettings.h"
#include "Engine/Content/WeakAssetReference.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Profiler/ProfilerMemory.h"
@@ -666,11 +667,42 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l
return true;
}
// Process mesh data if need to decide vertex buffer on format dynamically
auto positionFormat = modelData.PositionFormat;
if (positionFormat == ModelData::PositionFormats::Automatic)
{
const float maxPositionError = BuildSettings::Get()->MaxMeshPositionError; // In world-units
const float maxPositionErrorSq = maxPositionError * maxPositionError;
if (maxPositionErrorSq > 0.0f)
{
positionFormat = ModelData::PositionFormats::Float16;
const Float3* positions = mesh.Positions.Get();
for (int32 i = 0; i < mesh.Positions.Count(); i++)
{
// Encode to Half3 and decode back to see the position error
Float3 position = positions[i];
Half3 encoded(position);
Float3 decoded = encoded.ToFloat3();
if (Float3::DistanceSquared(position, decoded) > maxPositionErrorSq)
{
// Cannot use lower quality so go back to full precision
positionFormat = ModelData::PositionFormats::Float32;
break;
}
}
}
else
{
// Full precision as default
positionFormat = ModelData::PositionFormats::Float32;
}
}
// Define vertex buffers layout and packing
Array<GPUVertexLayout::Elements, FixedAllocation<MODEL_MAX_VB>> vbElements;
const bool useSeparatePositions = !isSkinned;
const bool useSeparateColors = !isSkinned;
PixelFormat positionsFormat = modelData.PositionFormat == ModelData::PositionFormats::Float32 ? PixelFormat::R32G32B32_Float : PixelFormat::R16G16B16A16_Float;
PixelFormat positionsFormat = positionFormat == ModelData::PositionFormats::Float32 ? PixelFormat::R32G32B32_Float : PixelFormat::R16G16B16A16_Float;
PixelFormat texCoordsFormat = modelData.TexCoordFormat == ModelData::TexCoordFormats::Float16 ? PixelFormat::R16G16_Float : PixelFormat::R8G8_UNorm;
PixelFormat blendIndicesFormat = PixelFormat::R8G8B8A8_UInt;
PixelFormat blendWeightsFormat = PixelFormat::R8G8B8A8_UNorm;
@@ -684,7 +716,6 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l
}
{
byte vbIndex = 0;
// TODO: add option to quantize vertex attributes (eg. 8-bit blend weights, 8-bit texcoords)
// Position
if (useSeparatePositions)

View File

@@ -89,6 +89,12 @@ public:
API_FIELD(Attributes="EditorOrder(2100), EditorDisplay(\"Content\")")
bool SkipDefaultFonts = false;
/// <summary>
/// The maximum acceptable mesh vertex position error (in world units) for data quantization. Use 0 to disable this feature. Affects meshes during import (or reimpport).
/// </summary>
API_FIELD(Attributes="EditorOrder(2200), EditorDisplay(\"Content\"), ValueCategory(Utils.ValueCategory.Distance)")
float MaxMeshPositionError = 0.5f;
/// <summary>
/// If checked, .NET Runtime won't be packaged with a game and will be required by user to be installed on system upon running game build. Available only on supported platforms such as Windows, Linux and macOS.
/// </summary>

View File

@@ -434,7 +434,8 @@ public:
{
Float32,
Float16,
} PositionFormat = PositionFormats::Float32;
Automatic,
} PositionFormat = PositionFormats::Automatic;
// See ModelTool::TexCoordFormats
enum class TexCoordFormats

View File

@@ -148,9 +148,11 @@ public:
API_ENUM(Attributes="HideInEditor") enum class PositionFormat
{
// XYZ channels with 32-bit precision (12 bytes per vertex).
Float32,
// XYZ(W) channels with 12-bit precision (8 bytes per vertex).
Float16,
Float32 = 0,
// XYZ(W) channels with 16-bit precision (8 bytes per vertex).
Float16 = 1,
// Selects the most memory-efficient format that can represent positions within a max error defined in project Build Settings.
Automatic = 2,
};
/// <summary>
@@ -159,9 +161,9 @@ public:
API_ENUM(Attributes="HideInEditor") enum class TexCoordFormats
{
// XY channels with 16-bit precision (4 bytes per vertex).
Float16,
Float16 = 0,
// XY channels with 8-bit precision (2 bytes per vertex). Valid only for normalized UVs within range [0; 1], scaled or negative UVs won't work.
UNorm8,
UNorm8 = 1,
};
/// <summary>
@@ -226,7 +228,7 @@ public:
public:
// The imported vertex positions data format to use by meshes. Changing this affects memory usage of the mesh data, performance and overall quality.
API_FIELD(Attributes = "EditorOrder(200), EditorDisplay(\"Data Format\"), VisibleIf(nameof(ShowGeometry))")
PositionFormat PositionFormat = PositionFormat::Float32;
PositionFormat PositionFormat = PositionFormat::Automatic;
// The imported vertex texture coordinates data format to use by meshes. Changing this affects memory usage of the mesh data, performance and overall quality.
API_FIELD(Attributes = "EditorOrder(205), EditorDisplay(\"Data Format\"), VisibleIf(nameof(ShowGeometry))")
TexCoordFormats TexCoordFormat = TexCoordFormats::Float16;