2022-08-13 17:22:19 +01:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <Orange/Graphics/Mesh.h>
|
|
|
|
|
|
|
|
#include <Orange/Core/Parse.h>
|
2022-08-13 21:41:19 +01:00
|
|
|
#include <Orange/Core/Hash.h>
|
|
|
|
|
|
|
|
#include <unordered_map>
|
2022-08-13 17:22:19 +01:00
|
|
|
|
|
|
|
namespace orange
|
|
|
|
{
|
2022-08-13 21:41:19 +01:00
|
|
|
struct StaticVertexHash
|
|
|
|
{
|
|
|
|
size_t operator() (const StaticVertex& key) const
|
|
|
|
{
|
|
|
|
size_t hash = 0;
|
|
|
|
return hashCombine(hash, key.pos.x, key.pos.y, key.pos.z, key.uv.x, key.uv.y, key.normal.x, key.normal.y, key.normal.z);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2022-08-13 17:22:19 +01:00
|
|
|
Result<MeshData> ParseOBJ(StringView buffer)
|
|
|
|
{
|
|
|
|
MeshData data{ MeshVertexType::Static };
|
|
|
|
|
|
|
|
Vector<vec3> positions;
|
|
|
|
Vector<vec2> uvs;
|
|
|
|
Vector<vec3> normals;
|
|
|
|
|
2022-08-13 21:41:19 +01:00
|
|
|
std::unordered_map<StaticVertex, IndexType, StaticVertexHash> indexTracker;
|
|
|
|
|
2022-08-13 17:22:19 +01:00
|
|
|
const char* obj = buffer.data;
|
|
|
|
const char* end = buffer.data + buffer.size;
|
|
|
|
while (obj != end)
|
|
|
|
{
|
|
|
|
SmallVector<char, 8> element;
|
|
|
|
stream::ReadString(obj, end, " #\n", element);
|
|
|
|
|
|
|
|
if (element == "v" || element == "vt" || element == "vn")
|
|
|
|
{
|
|
|
|
float vtx[3]{};
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
stream::ConsumeSpace(obj, end);
|
|
|
|
if (auto r_float = stream::Parse<float>(obj, end))
|
|
|
|
vtx[i] = *r_float;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (element == "v")
|
2022-08-13 21:41:19 +01:00
|
|
|
positions.EmplaceBack(vtx[0], -vtx[2], vtx[1]); // TODO remove hack and make this customizable
|
2022-08-13 17:22:19 +01:00
|
|
|
else if (element == "vt")
|
2022-08-13 21:41:19 +01:00
|
|
|
uvs.EmplaceBack(vtx[0], 1.0f - vtx[1]); // Fix the space from gl -> vk/dx
|
2022-08-13 17:22:19 +01:00
|
|
|
else if (element == "vn")
|
|
|
|
normals.EmplaceBack(vtx[0], vtx[1], vtx[2]);
|
|
|
|
}
|
|
|
|
else if (element == "g" || element == "o")
|
|
|
|
{
|
|
|
|
stream::ConsumeSpace(obj, end);
|
|
|
|
SmallVector<char, 32> name;
|
|
|
|
stream::ReadString(obj, end, " #\n", name);
|
|
|
|
|
|
|
|
name.PushBack('\0');
|
|
|
|
if (element == "g")
|
|
|
|
log::info("Group name: %s", name.Data());
|
|
|
|
else
|
|
|
|
log::info("Object name: %s", name.Data());
|
|
|
|
}
|
|
|
|
else if (element == "f")
|
|
|
|
{
|
|
|
|
int32_t indices[3][3]{};
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
stream::ConsumeSpace(obj, end);
|
|
|
|
|
|
|
|
for (int j = 0; j < 3; j++)
|
|
|
|
{
|
|
|
|
indices[i][j] = -1;
|
|
|
|
|
|
|
|
if (j == 0 || stream::Consume(obj, end, "/"))
|
|
|
|
{
|
|
|
|
if (auto r_int = stream::Parse<uint32_t>(obj, end))
|
|
|
|
indices[i][j] = *r_int - 1; // OBJ indexing starts at one.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-13 21:41:19 +01:00
|
|
|
// Do backwards so we can have front faces
|
|
|
|
// be CCW.
|
|
|
|
for (int i = 2; i >= 0; i--)
|
2022-08-13 17:22:19 +01:00
|
|
|
{
|
|
|
|
StaticVertex vertex =
|
|
|
|
{
|
|
|
|
.pos = indices[i][0] != -1 ? positions[indices[i][0]] : vec3{},
|
|
|
|
.uv = indices[i][1] != -1 ? uvs [indices[i][1]] : vec2{},
|
|
|
|
.normal = indices[i][2] != -1 ? normals [indices[i][2]] : vec3{},
|
|
|
|
};
|
|
|
|
|
|
|
|
auto& vertices = data.vertexData.GetStaticVertices();
|
|
|
|
|
2022-08-13 21:41:19 +01:00
|
|
|
auto iter = indexTracker.find(vertex);
|
|
|
|
size_t vertexIdx;
|
|
|
|
if (iter == indexTracker.end())
|
2022-08-13 17:22:19 +01:00
|
|
|
{
|
|
|
|
data.bounds.Extend(vertex.pos);
|
|
|
|
vertexIdx = vertices.PushBack(vertex);
|
2022-08-13 21:41:19 +01:00
|
|
|
indexTracker.emplace(vertex, vertexIdx);
|
2022-08-13 17:22:19 +01:00
|
|
|
}
|
2022-08-13 21:41:19 +01:00
|
|
|
else
|
|
|
|
vertexIdx = iter->second;
|
2022-08-13 17:22:19 +01:00
|
|
|
|
2022-08-13 21:41:19 +01:00
|
|
|
Assert(vertexIdx < MaxMeshIndex);
|
|
|
|
data.indices.PushBack(IndexType(vertexIdx));
|
2022-08-13 17:22:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!element.Empty())
|
|
|
|
{
|
|
|
|
element.PushBack('\0');
|
|
|
|
log::info("Unknown element: %s", element.Data());
|
|
|
|
}
|
|
|
|
|
|
|
|
stream::AdvancePast(obj, end, "\n");
|
|
|
|
};
|
|
|
|
|
|
|
|
return Result<MeshData>::Success(data);
|
|
|
|
}
|
|
|
|
}
|