diff --git a/Source/Engine/Content/Assets/ModelBase.cpp b/Source/Engine/Content/Assets/ModelBase.cpp index 243e429a5..e993bbec6 100644 --- a/Source/Engine/Content/Assets/ModelBase.cpp +++ b/Source/Engine/Content/Assets/ModelBase.cpp @@ -665,6 +665,8 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l Array> vbElements; const bool useSeparatePositions = !isSkinned; const bool useSeparateColors = !isSkinned; + PixelFormat positionsFormat = modelData.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; for (const Int4& indices : mesh.BlendIndices) @@ -677,14 +679,13 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l } { byte vbIndex = 0; - // TODO: add option to quantize vertex positions (eg. 16-bit) // TODO: add option to quantize vertex attributes (eg. 8-bit blend weights, 8-bit texcoords) // Position if (useSeparatePositions) { auto& vb0 = vbElements.AddOne(); - vb0.Add({ VertexElement::Types::Position, vbIndex, 0, 0, PixelFormat::R32G32B32_Float }); + vb0.Add({ VertexElement::Types::Position, vbIndex, 0, 0, positionsFormat }); vbIndex++; } @@ -692,13 +693,13 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l { auto& vb = vbElements.AddOne(); if (!useSeparatePositions) - vb.Add({ VertexElement::Types::Position, vbIndex, 0, 0, PixelFormat::R32G32B32_Float }); + vb.Add({ VertexElement::Types::Position, vbIndex, 0, 0, positionsFormat }); for (int32 channelIdx = 0; channelIdx < mesh.UVs.Count(); channelIdx++) { auto& channel = mesh.UVs.Get()[channelIdx]; if (channel.HasItems()) { - vb.Add({ (VertexElement::Types)((int32)VertexElement::Types::TexCoord0 + channelIdx), vbIndex, 0, 0, PixelFormat::R16G16_Float }); + vb.Add({ (VertexElement::Types)((int32)VertexElement::Types::TexCoord0 + channelIdx), vbIndex, 0, 0, texCoordsFormat }); } } vb.Add({ VertexElement::Types::Normal, vbIndex, 0, 0, PixelFormat::R10G10B10A2_UNorm }); @@ -733,7 +734,7 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l // Write vertex buffers for (int32 vbIndex = 0; vbIndex < vbCount; vbIndex++) { - if (useSeparatePositions && vbIndex == 0) + if (useSeparatePositions && vbIndex == 0 && positionsFormat == PixelFormat::R32G32B32_Float) { // Fast path for vertex positions of static models using the first buffer stream.WriteBytes(mesh.Positions.Get(), sizeof(Float3) * vertices); @@ -751,7 +752,15 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l case VertexElement::Types::Position: { const Float3 position = mesh.Positions.Get()[vertex]; - stream.Write(position); + if (positionsFormat == PixelFormat::R16G16B16A16_Float) + { + const Half4 positionEnc(Float4(position, 0.0f)); + stream.Write(positionEnc); + } + else //if (positionsFormat == PixelFormat::R32G32B32_Float) + { + stream.Write(position); + } break; } case VertexElement::Types::Color: @@ -817,8 +826,16 @@ bool ModelBase::SaveLOD(WriteStream& stream, const ModelData& modelData, int32 l { const int32 channelIdx = (int32)element.Type - (int32)VertexElement::Types::TexCoord0; const Float2 uv = mesh.UVs.Get()[channelIdx].Get()[vertex]; - const Half2 uvEnc(uv); - stream.Write(uvEnc); + if (texCoordsFormat == PixelFormat::R8G8_UNorm) + { + stream.Write((uint8)Math::Clamp((int32)(uv.X * 255), 0, 255)); + stream.Write((uint8)Math::Clamp((int32)(uv.Y * 255), 0, 255)); + } + else //if (texCoordsFormat == PixelFormat::R16G16_Float) + { + const Half2 uvEnc(uv); + stream.Write(uvEnc); + } break; } default: diff --git a/Source/Engine/Graphics/Models/ModelData.h b/Source/Engine/Graphics/Models/ModelData.h index 4566e764b..8e401b973 100644 --- a/Source/Engine/Graphics/Models/ModelData.h +++ b/Source/Engine/Graphics/Models/ModelData.h @@ -428,6 +428,21 @@ public: /// Array Animations; +public: + // See ModelTool::PositionFormat + enum class PositionFormats + { + Float32, + Float16, + } PositionFormat = PositionFormats::Float32; + + // See ModelTool::TexCoordFormats + enum class TexCoordFormats + { + Float16, + UNorm8, + } TexCoordFormat = TexCoordFormats::Float16; + public: /// /// Automatically calculates the screen size for every model LOD for a proper transitions. diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 51584504d..795b69452 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -676,6 +676,9 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(CalculateBoneOffsetMatrices); SERIALIZE(LightmapUVsSource); SERIALIZE(CollisionMeshesPrefix); + SERIALIZE(CollisionType); + SERIALIZE(PositionFormat); + SERIALIZE(TexCoordFormat); SERIALIZE(Scale); SERIALIZE(Rotation); SERIALIZE(Translation); @@ -727,6 +730,9 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(CalculateBoneOffsetMatrices); DESERIALIZE(LightmapUVsSource); DESERIALIZE(CollisionMeshesPrefix); + DESERIALIZE(CollisionType); + DESERIALIZE(PositionFormat); + DESERIALIZE(TexCoordFormat); DESERIALIZE(Scale); DESERIALIZE(Rotation); DESERIALIZE(Translation); @@ -1137,6 +1143,10 @@ bool ModelTool::ImportModel(const String& path, ModelData& data, Options& option if (ImportData(path, data, options, errorMsg)) return true; + // Copy over data format options + data.PositionFormat = (ModelData::PositionFormats)options.PositionFormat; + data.TexCoordFormat = (ModelData::TexCoordFormats)options.TexCoordFormat; + // Validate result data if (EnumHasAnyFlags(options.ImportTypes, ImportDataTypes::Geometry)) { diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index 7e0587c3d..b09583471 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -142,6 +142,28 @@ public: ExtractCenterOfMass = 2, }; + /// + /// Declares the imported vertex positions data formats. + /// + 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, + }; + + /// + /// Declares the imported vertex texture coordinates data formats. + /// + API_ENUM(Attributes="HideInEditor") enum class TexCoordFormats + { + // XY channels with 16-bit precision (4 bytes per vertex). + Float16, + // 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, + }; + /// /// Model import options. /// @@ -201,6 +223,14 @@ public: API_FIELD(Attributes="EditorOrder(105), EditorDisplay(\"Geometry\"), VisibleIf(nameof(ShowGeometry))") CollisionDataType CollisionType = CollisionDataType::ConvexMesh; + 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; + // 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; + public: // Transform // Custom uniform import scale.