//-------------------------------------------------------------------------------------- // File: vbodump.cpp // // VBO file content examination utility // // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. //-------------------------------------------------------------------------------------- #include #include #include #include #include #include using namespace DirectX; //--------------------------------------------------------------------------------- struct handle_closer { void operator()(HANDLE h) noexcept { if (h) CloseHandle(h); } }; using ScopedHandle = std::unique_ptr; inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; } //--------------------------------------------------------------------------------- namespace VBO { #pragma pack(push,1) struct header_t { uint32_t numVertices; uint32_t numIndices; }; struct vertex_t { DirectX::XMFLOAT3 position; DirectX::XMFLOAT3 normal; DirectX::XMFLOAT2 textureCoordinate; }; #pragma pack(pop) static_assert(sizeof(header_t) == 8, "VBO header size mismatch"); static_assert(sizeof(vertex_t) == 32, "VBO header size mismatch"); }; // namespace namespace { HRESULT LoadDataFromFile(_In_z_ const wchar_t* fileName, std::unique_ptr& data, size_t& dataSize) { // open the file #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) ScopedHandle hFile(safe_handle(CreateFile2(fileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr))); #else ScopedHandle hFile(safe_handle(CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr))); #endif if (!hFile) return HRESULT_FROM_WIN32(GetLastError()); // Get the file size FILE_STANDARD_INFO fileInfo; if (!GetFileInformationByHandleEx(hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo))) { return HRESULT_FROM_WIN32(GetLastError()); } // File is too big for 32-bit allocation, so reject read if (fileInfo.EndOfFile.HighPart > 0) { return E_FAIL; } // Need at least enough data to fill the header if (fileInfo.EndOfFile.LowPart < sizeof(VBO::header_t)) { return E_FAIL; } // create enough space for the file data data.reset(new (std::nothrow) uint8_t[fileInfo.EndOfFile.LowPart]); if (!data) { return E_OUTOFMEMORY; } // read the data in DWORD BytesRead = 0; if (!ReadFile(hFile.get(), data.get(), fileInfo.EndOfFile.LowPart, &BytesRead, nullptr)) { return HRESULT_FROM_WIN32(GetLastError()); } if (BytesRead < fileInfo.EndOfFile.LowPart) { return E_FAIL; } dataSize = fileInfo.EndOfFile.LowPart; return S_OK; } //--------------------------------------------------------------------------------- void DumpVB(const VBO::vertex_t* vb, size_t count, bool full) { bool ellipsis = false; size_t lowbound = 5; size_t hibound = (count > 5) ? (count - 5) : 0; wprintf(L"%zu vertices\n", count); if (!full) return; auto vptr = vb; for (size_t j = 0; j < count; ++j, ++vptr) { if ((j >= lowbound) && (j < hibound)) { if (!ellipsis) { ellipsis = true; wprintf(L"...\n"); } continue; } wprintf(L"%6zu:\tP:(%f, %f, %f)\n\tN:(%f, %f, %f)\n\tT:(%f, %f)\n", j + 1, double(vptr->position.x), double(vptr->position.y), double(vptr->position.z), double(vptr->normal.x), double(vptr->normal.y), double(vptr->normal.z), double(vptr->textureCoordinate.x), double(vptr->textureCoordinate.y)); } } //--------------------------------------------------------------------------------- template void DumpIB(const index_t* ib, size_t count, bool full) { bool ellipsis = false; size_t nFaces = count / 3; size_t lowbound = 5; size_t hibound = (nFaces > 5) ? (nFaces - 5) : 0; wprintf(L"%zu indices (%zu faces)\n", count, nFaces); if (!full) return; const index_t* iptr = ib; for (size_t j = 0; j < nFaces; ++j, iptr += 3) { if ((j >= lowbound) && (j < hibound)) { if (!ellipsis) { ellipsis = true; wprintf(L"...\n"); } continue; } wprintf(L"%6zu:\t%u, %u, %u\n", j + 1, *iptr, *(iptr + 1), *(iptr + 2)); } } } //--------------------------------------------------------------------------------- int wmain(int argc, wchar_t* argv[]) { using namespace VBO; if (argc != 2 && argc != 3) { wprintf(L"Usage: %s [-full]\n", argv[0]); return -1; } bool fulldump = false; if (argc == 3) { if (!_wcsicmp(argv[2], L"-full") || !_wcsicmp(argv[2], L"/full")) { fulldump = true; } else { wprintf(L"ERROR: Unknown switch '%s'\n", argv[2]); return -1; } } size_t dataSize = 0; std::unique_ptr data; HRESULT hr = LoadDataFromFile(argv[1], data, dataSize); if (FAILED(hr)) { wprintf(L"ERROR: failed to load %s\n", argv[1]); return -1; } const uint8_t* meshData = data.get(); auto header = reinterpret_cast(meshData); size_t usedSize = sizeof(header_t); if (!header->numVertices || !header->numIndices) { wprintf(L"ERROR: Needs both verts and indices (verts: %u, indices: %u)\n", header->numVertices, header->numIndices); return -1; } wprintf(L"VBO %s\n", argv[1]); if (header->numVertices >= UINT16_MAX) wprintf(L"WARNING: Contains more vertices than can be addresed by a 16-bit index buffer!\n"); auto vb = reinterpret_cast(meshData + usedSize); usedSize += sizeof(vertex_t) * header->numVertices; if (usedSize > dataSize) { wprintf(L"ERROR: Not enough data in VBO file for vertices\n"); return -1; } DumpVB(vb, header->numVertices, fulldump); auto ib = reinterpret_cast(meshData + usedSize); usedSize += sizeof(uint16_t) * header->numIndices; if (usedSize > dataSize) { wprintf(L"ERROR: Not enough data in VBO file for indices\n"); return -1; } DumpIB(ib, header->numIndices, fulldump); return 0; }