in Utilities/WaveFrontReader.h [66:412]
HRESULT Load(_In_z_ const wchar_t* szFileName, bool ccw = true)
{
Clear();
constexpr size_t MAX_POLY = 64;
using namespace DirectX;
std::wifstream InFile(szFileName);
if (!InFile)
return /* HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) */ static_cast<HRESULT>(0x80070002L);
InFile.imbue(std::locale::classic());
#ifdef WIN32
wchar_t fname[_MAX_FNAME] = {};
_wsplitpath_s(szFileName, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, nullptr, 0);
name = fname;
#else
auto path = std::filesystem::path(szFileName);
name = path.filename().c_str();
#endif
std::vector<XMFLOAT3> positions;
std::vector<XMFLOAT3> normals;
std::vector<XMFLOAT2> texCoords;
VertexCache vertexCache;
Material defmat;
wcscpy_s(defmat.strName, L"default");
materials.emplace_back(defmat);
uint32_t curSubset = 0;
wchar_t strMaterialFilename[MAX_PATH] = {};
for (;; )
{
std::wstring strCommand;
InFile >> strCommand;
if (!InFile)
break;
if (*strCommand.c_str() == L'#')
{
// Comment
}
else if (0 == wcscmp(strCommand.c_str(), L"o"))
{
// Object name ignored
}
else if (0 == wcscmp(strCommand.c_str(), L"g"))
{
// Group name ignored
}
else if (0 == wcscmp(strCommand.c_str(), L"s"))
{
// Smoothing group ignored
}
else if (0 == wcscmp(strCommand.c_str(), L"v"))
{
// Vertex Position
float x, y, z;
InFile >> x >> y >> z;
positions.emplace_back(XMFLOAT3(x, y, z));
}
else if (0 == wcscmp(strCommand.c_str(), L"vt"))
{
// Vertex TexCoord
float u, v;
InFile >> u >> v;
texCoords.emplace_back(XMFLOAT2(u, v));
hasTexcoords = true;
}
else if (0 == wcscmp(strCommand.c_str(), L"vn"))
{
// Vertex Normal
float x, y, z;
InFile >> x >> y >> z;
normals.emplace_back(XMFLOAT3(x, y, z));
hasNormals = true;
}
else if (0 == wcscmp(strCommand.c_str(), L"f"))
{
// Face
INT iPosition, iTexCoord, iNormal;
Vertex vertex;
uint32_t faceIndex[MAX_POLY];
size_t iFace = 0;
for (;;)
{
if (iFace >= MAX_POLY)
{
// Too many polygon verts for the reader
return E_FAIL;
}
memset(&vertex, 0, sizeof(vertex));
InFile >> iPosition;
uint32_t vertexIndex = 0;
if (!iPosition)
{
// 0 is not allowed for index
return E_UNEXPECTED;
}
else if (iPosition < 0)
{
// Negative values are relative indices
vertexIndex = uint32_t(ptrdiff_t(positions.size()) + iPosition);
}
else
{
// OBJ format uses 1-based arrays
vertexIndex = uint32_t(iPosition - 1);
}
if (vertexIndex >= positions.size())
return E_FAIL;
vertex.position = positions[vertexIndex];
if ('/' == InFile.peek())
{
InFile.ignore();
if ('/' != InFile.peek())
{
// Optional texture coordinate
InFile >> iTexCoord;
uint32_t coordIndex = 0;
if (!iTexCoord)
{
// 0 is not allowed for index
return E_UNEXPECTED;
}
else if (iTexCoord < 0)
{
// Negative values are relative indices
coordIndex = uint32_t(ptrdiff_t(texCoords.size()) + iTexCoord);
}
else
{
// OBJ format uses 1-based arrays
coordIndex = uint32_t(iTexCoord - 1);
}
if (coordIndex >= texCoords.size())
return E_FAIL;
vertex.textureCoordinate = texCoords[coordIndex];
}
if ('/' == InFile.peek())
{
InFile.ignore();
// Optional vertex normal
InFile >> iNormal;
uint32_t normIndex = 0;
if (!iNormal)
{
// 0 is not allowed for index
return E_UNEXPECTED;
}
else if (iNormal < 0)
{
// Negative values are relative indices
normIndex = uint32_t(ptrdiff_t(normals.size()) + iNormal);
}
else
{
// OBJ format uses 1-based arrays
normIndex = uint32_t(iNormal - 1);
}
if (normIndex >= normals.size())
return E_FAIL;
vertex.normal = normals[normIndex];
}
}
// If a duplicate vertex doesn't exist, add this vertex to the Vertices
// list. Store the index in the Indices array. The Vertices and Indices
// lists will eventually become the Vertex Buffer and Index Buffer for
// the mesh.
uint32_t index = AddVertex(vertexIndex, &vertex, vertexCache);
if (index == uint32_t(-1))
return E_OUTOFMEMORY;
#pragma warning( suppress : 4127 )
if (sizeof(index_t) == 2 && (index >= 0xFFFF))
{
// Too many indices for 16-bit IB!
return E_FAIL;
}
#pragma warning( suppress : 4127 )
else if (sizeof(index_t) == 4 && (index >= 0xFFFFFFFF))
{
// Too many indices for 32-bit IB!
return E_FAIL;
}
faceIndex[iFace] = index;
++iFace;
// Check for more face data or end of the face statement
bool faceEnd = false;
for (;;)
{
wchar_t p = InFile.peek();
if ('\n' == p || !InFile)
{
faceEnd = true;
break;
}
else if (isdigit(p) || p == '-' || p == '+')
break;
InFile.ignore();
}
if (faceEnd)
break;
}
if (iFace < 3)
{
// Need at least 3 points to form a triangle
return E_FAIL;
}
// Convert polygons to triangles
uint32_t i0 = faceIndex[0];
uint32_t i1 = faceIndex[1];
for (size_t j = 2; j < iFace; ++j)
{
uint32_t index = faceIndex[j];
indices.emplace_back(static_cast<index_t>(i0));
if (ccw)
{
indices.emplace_back(static_cast<index_t>(i1));
indices.emplace_back(static_cast<index_t>(index));
}
else
{
indices.emplace_back(static_cast<index_t>(index));
indices.emplace_back(static_cast<index_t>(i1));
}
attributes.emplace_back(curSubset);
i1 = index;
}
assert(attributes.size() * 3 == indices.size());
}
else if (0 == wcscmp(strCommand.c_str(), L"mtllib"))
{
// Material library
InFile >> strMaterialFilename;
}
else if (0 == wcscmp(strCommand.c_str(), L"usemtl"))
{
// Material
wchar_t strName[MAX_PATH] = {};
InFile >> strName;
bool bFound = false;
uint32_t count = 0;
for (auto it = materials.cbegin(); it != materials.cend(); ++it, ++count)
{
if (0 == wcscmp(it->strName, strName))
{
bFound = true;
curSubset = count;
break;
}
}
if (!bFound)
{
Material mat;
curSubset = static_cast<uint32_t>(materials.size());
wcscpy_s(mat.strName, MAX_PATH - 1, strName);
materials.emplace_back(mat);
}
}
else
{
#ifdef _DEBUG
// Unimplemented or unrecognized command
OutputDebugStringW(strCommand.c_str());
#endif
}
InFile.ignore(1000, L'\n');
}
if (positions.empty())
return E_FAIL;
// Cleanup
InFile.close();
BoundingBox::CreateFromPoints(bounds, positions.size(), positions.data(), sizeof(XMFLOAT3));
// If an associated material file was found, read that in as well.
if (*strMaterialFilename)
{
#ifdef WIN32
wchar_t ext[_MAX_EXT] = {};
_wsplitpath_s(strMaterialFilename, nullptr, 0, nullptr, 0, fname, _MAX_FNAME, ext, _MAX_EXT);
wchar_t drive[_MAX_DRIVE] = {};
wchar_t dir[_MAX_DIR] = {};
_wsplitpath_s(szFileName, drive, _MAX_DRIVE, dir, _MAX_DIR, nullptr, 0, nullptr, 0);
wchar_t szPath[MAX_PATH] = {};
_wmakepath_s(szPath, MAX_PATH, drive, dir, fname, ext);
HRESULT hr = LoadMTL(szPath);
if (FAILED(hr))
return hr;
#else
auto path = std::filesystem::path(szFileName);
auto mtlpath = std::filesystem::path(strMaterialFilename);
path.replace_filename(mtlpath.filename());
path.replace_extension(mtlpath.extension());
HRESULT hr = LoadMTL(path.c_str());
if (FAILED(hr))
return hr;
#endif
}
return S_OK;
}