**Refactor meshes format to support custom vertex layouts and new flexible api to access mesh data**

#3044 #2667
This commit is contained in:
Wojtek Figat
2025-01-06 22:47:19 +01:00
parent 29bfef677f
commit db4d7d2a05
65 changed files with 4428 additions and 3106 deletions

View File

@@ -9,12 +9,12 @@
#include "Engine/Content/Assets/SkinnedModel.h"
#include "Engine/Serialization/FileWriteStream.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Graphics/Models/MeshAccessor.h"
#include "Engine/Core/DeleteMe.h"
ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
{
// Prepare
auto asset = (Model*)context.Asset.Get();
auto asset = (ModelBase*)context.Asset.Get();
auto lock = asset->Storage->LockSafe();
auto path = GET_OUTPUT_PATH(context, "obj");
const int32 lodIndex = 0;
@@ -26,7 +26,6 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
const auto chunk = asset->GetChunk(chunkIndex);
if (!chunk)
return ExportAssetResult::CannotLoadData;
MemoryReadStream stream(chunk->Get(), chunk->Size());
FileWriteStream* output = FileWriteStream::Open(path);
if (output == nullptr)
@@ -37,62 +36,61 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
output->WriteText(StringAnsi::Format("# Exported model {0}\n", name.Get()));
// Extract all meshes
const auto& lod = asset->LODs[lodIndex];
int32 vertexStart = 1; // OBJ counts vertices from 1 not from 0
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
if (asset->GetLODsCount() <= lodIndex)
return ExportAssetResult::Error;
Array<MeshBase*> meshes;
asset->GetMeshes(meshes, lodIndex);
uint32 vertexStart = 1; // OBJ counts vertices from 1 not from 0
ModelBase::MeshData meshData;
byte meshVersion = stream.ReadByte();
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++)
{
auto& mesh = lod.Meshes[meshIndex];
// #MODEL_DATA_FORMAT_USAGE
uint32 vertices;
stream.ReadUint32(&vertices);
uint32 triangles;
stream.ReadUint32(&triangles);
uint32 indicesCount = triangles * 3;
bool use16BitIndexBuffer = indicesCount <= MAX_uint16;
uint32 ibStride = use16BitIndexBuffer ? sizeof(uint16) : sizeof(uint32);
if (vertices == 0 || triangles == 0)
auto mesh = meshes[meshIndex];
if (asset->LoadMesh(stream, meshVersion, mesh, &meshData))
return ExportAssetResult::CannotLoadData;
if (meshData.Vertices == 0 || meshData.Triangles == 0)
return ExportAssetResult::Error;
auto vb0 = stream.Move<VB0ElementType>(vertices);
auto vb1 = stream.Move<VB1ElementType>(vertices);
bool hasColors = stream.ReadBool();
VB2ElementType18* vb2 = nullptr;
if (hasColors)
{
vb2 = stream.Move<VB2ElementType18>(vertices);
}
auto ib = stream.Move<byte>(indicesCount * ibStride);
MeshAccessor accessor;
if (accessor.LoadFromMeshData(&meshData))
return ExportAssetResult::CannotLoadAsset;
output->WriteText(StringAnsi::Format("# Mesh {0}\n", meshIndex));
for (uint32 i = 0; i < vertices; i++)
auto positionStream = accessor.Position();
if (!positionStream.IsValid())
return ExportAssetResult::Error;
for (uint32 i = 0; i < meshData.Vertices; i++)
{
auto v = vb0[i].Position;
auto v = positionStream.GetFloat3(i);
output->WriteText(StringAnsi::Format("v {0} {1} {2}\n", v.X, v.Y, v.Z));
}
output->WriteChar('\n');
for (uint32 i = 0; i < vertices; i++)
auto texCoordStream = accessor.TexCoord();
if (texCoordStream.IsValid())
{
auto v = vb1[i].TexCoord.ToFloat2();
output->WriteText(StringAnsi::Format("vt {0} {1}\n", v.X, v.Y));
for (uint32 i = 0; i < meshData.Vertices; i++)
{
auto v = texCoordStream.GetFloat2(i);
output->WriteText(StringAnsi::Format("vt {0} {1}\n", v.X, v.Y));
}
output->WriteChar('\n');
}
output->WriteChar('\n');
for (uint32 i = 0; i < vertices; i++)
auto normalStream = accessor.Normal();
if (normalStream.IsValid())
{
auto v = vb1[i].Normal.ToFloat3() * 2.0f - 1.0f;
output->WriteText(StringAnsi::Format("vn {0} {1} {2}\n", v.X, v.Y, v.Z));
for (uint32 i = 0; i < meshData.Vertices; i++)
{
auto v = normalStream.GetFloat3(i) * 2.0f - 1.0f;
output->WriteText(StringAnsi::Format("vn {0} {1} {2}\n", v.X, v.Y, v.Z));
}
output->WriteChar('\n');
}
output->WriteChar('\n');
if (use16BitIndexBuffer)
if (meshData.IBStride == sizeof(uint16))
{
auto t = (uint16*)ib;
for (uint32 i = 0; i < triangles; i++)
auto t = (const uint16*)meshData.IBData;
for (uint32 i = 0; i < meshData.Triangles; i++)
{
auto i0 = vertexStart + *t++;
auto i1 = vertexStart + *t++;
@@ -102,8 +100,8 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
}
else
{
auto t = (uint32*)ib;
for (uint32 i = 0; i < triangles; i++)
auto t = (const uint32*)meshData.IBData;
for (uint32 i = 0; i < meshData.Triangles; i++)
{
auto i0 = vertexStart + *t++;
auto i1 = vertexStart + *t++;
@@ -111,10 +109,9 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
output->WriteText(StringAnsi::Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2));
}
}
output->WriteChar('\n');
vertexStart += vertices;
vertexStart += meshData.Vertices;
}
if (output->HasError())
@@ -125,120 +122,8 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context)
ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context)
{
// Prepare
auto asset = (SkinnedModel*)context.Asset.Get();
auto lock = asset->Storage->LockSafe();
auto path = GET_OUTPUT_PATH(context, "obj");
const int32 lodIndex = 0;
// Fetch chunk with data
const auto chunkIndex = 1;
if (asset->LoadChunk(chunkIndex))
return ExportAssetResult::CannotLoadData;
const auto chunk = asset->GetChunk(chunkIndex);
if (!chunk)
return ExportAssetResult::CannotLoadData;
MemoryReadStream stream(chunk->Get(), chunk->Size());
FileWriteStream* output = FileWriteStream::Open(path);
if (output == nullptr)
return ExportAssetResult::Error;
DeleteMe<FileWriteStream> outputDeleteMe(output);
const auto name = StringUtils::GetFileNameWithoutExtension(asset->GetPath()).ToStringAnsi();
output->WriteText(StringAnsi::Format("# Exported model {0}\n", name.Get()));
// Extract all meshes
const auto& lod = asset->LODs[lodIndex];
int32 vertexStart = 1; // OBJ counts vertices from 1 not from 0
byte version = stream.ReadByte();
for (int32 meshIndex = 0; meshIndex < lod.Meshes.Count(); meshIndex++)
{
auto& mesh = lod.Meshes[meshIndex];
// #MODEL_DATA_FORMAT_USAGE
uint32 vertices;
stream.ReadUint32(&vertices);
uint32 triangles;
stream.ReadUint32(&triangles);
uint16 blendShapesCount;
stream.ReadUint16(&blendShapesCount);
for (int32 blendShapeIndex = 0; blendShapeIndex < blendShapesCount; blendShapeIndex++)
{
stream.ReadBool();
uint32 tmp;
stream.ReadUint32(&tmp);
stream.ReadUint32(&tmp);
uint32 blendShapeVertices;
stream.ReadUint32(&blendShapeVertices);
stream.Move(blendShapeVertices * sizeof(BlendShapeVertex));
}
uint32 indicesCount = triangles * 3;
bool use16BitIndexBuffer = indicesCount <= MAX_uint16;
uint32 ibStride = use16BitIndexBuffer ? sizeof(uint16) : sizeof(uint32);
if (vertices == 0 || triangles == 0)
return ExportAssetResult::Error;
auto vb0 = stream.Move<VB0SkinnedElementType>(vertices);
auto ib = stream.Move<byte>(indicesCount * ibStride);
output->WriteText(StringAnsi::Format("# Mesh {0}\n", meshIndex));
for (uint32 i = 0; i < vertices; i++)
{
auto v = vb0[i].Position;
output->WriteText(StringAnsi::Format("v {0} {1} {2}\n", v.X, v.Y, v.Z));
}
output->WriteChar('\n');
for (uint32 i = 0; i < vertices; i++)
{
auto v = vb0[i].TexCoord.ToFloat2();
output->WriteText(StringAnsi::Format("vt {0} {1}\n", v.X, v.Y));
}
output->WriteChar('\n');
for (uint32 i = 0; i < vertices; i++)
{
auto v = vb0[i].Normal.ToFloat3() * 2.0f - 1.0f;
output->WriteText(StringAnsi::Format("vn {0} {1} {2}\n", v.X, v.Y, v.Z));
}
output->WriteChar('\n');
if (use16BitIndexBuffer)
{
auto t = (uint16*)ib;
for (uint32 i = 0; i < triangles; i++)
{
auto i0 = vertexStart + *t++;
auto i1 = vertexStart + *t++;
auto i2 = vertexStart + *t++;
output->WriteText(StringAnsi::Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2));
}
}
else
{
auto t = (uint32*)ib;
for (uint32 i = 0; i < triangles; i++)
{
auto i0 = vertexStart + *t++;
auto i1 = vertexStart + *t++;
auto i2 = vertexStart + *t++;
output->WriteText(StringAnsi::Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2));
}
}
output->WriteChar('\n');
vertexStart += vertices;
}
if (output->HasError())
return ExportAssetResult::Error;
return ExportAssetResult::Ok;
// The same code, except SkinnedModel::LoadMesh will be used to read Blend Shapes data
return ExportModel(context);
}
#endif