#pragma once #include #include #include #include namespace orange { 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); } }; Result ParseOBJ(StringView buffer) { MeshData data{ MeshVertexType::Static }; Vector positions; Vector uvs; Vector normals; std::unordered_map indexTracker; const char* obj = buffer.data; const char* end = buffer.data + buffer.size; while (obj != end) { SmallVector 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(obj, end)) vtx[i] = *r_float; } if (element == "v") positions.EmplaceBack(vtx[0], -vtx[2], vtx[1]); // TODO remove hack and make this customizable else if (element == "vt") uvs.EmplaceBack(vtx[0], 1.0f - vtx[1]); // Fix the space from gl -> vk/dx else if (element == "vn") normals.EmplaceBack(vtx[0], vtx[1], vtx[2]); } else if (element == "g" || element == "o") { stream::ConsumeSpace(obj, end); SmallVector 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(obj, end)) indices[i][j] = *r_int - 1; // OBJ indexing starts at one. } } } // Do backwards so we can have front faces // be CCW. for (int i = 2; i >= 0; i--) { 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(); auto iter = indexTracker.find(vertex); size_t vertexIdx; if (iter == indexTracker.end()) { data.bounds.Extend(vertex.pos); vertexIdx = vertices.PushBack(vertex); indexTracker.emplace(vertex, vertexIdx); } else vertexIdx = iter->second; Assert(vertexIdx < MaxMeshIndex); data.indices.PushBack(IndexType(vertexIdx)); } } else if (!element.Empty()) { element.PushBack('\0'); log::info("Unknown element: %s", element.Data()); } stream::AdvancePast(obj, end, "\n"); }; return Result::Success(data); } }