//-------------------------------------------------------------------------------------- // File: sdkmeshdump.cpp // // DirectX SDK .SDKMESH/.ANIM file content examination utility // // SDKMESH format is generated by the legacy DirectX SDK's Content Exporter and // originally rendered by the DXUT helper class SDKMesh // http://go.microsoft.com/fwlink/?LinkId=226208 // // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. //-------------------------------------------------------------------------------------- #include #include #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 DXUT { // .SDKMESH files // SDKMESH_HEADER // SDKMESH_VERTEX_BUFFER_HEADER header->VertexStreamHeadersOffset // SDKMESH_INDEX_BUFFER_HEADER header->IndexStreamHeadersOffset // SDKMESH_MESH header->MeshDataOffset // SDKMESH_SUBSET header->SubsetDataOffset // SDKMESH_FRAME header->FrameDataOffset // SDKMESH_MATERIAL header->MaterialDataOffset // [header->NonBufferDataSize] // { [ header->NumVertexBuffers] // VB data // } // { [ header->NumIndexBuffers] // IB data // } // .SDDKANIM files // SDKANIMATION_FILE_HEADER // BYTE[] - Length of fileheader->AnimationDataSize // .SDKMESH uses Direct3D 9 decls, but only a subset of these is ever generated by the legacy DirectX SDK Content Exporter // D3DDECLUSAGE_POSITION / D3DDECLTYPE_FLOAT3 // (D3DDECLUSAGE_BLENDWEIGHT / D3DDECLTYPE_UBYTE4N // D3DDECLUSAGE_BLENDINDICES / D3DDECLTYPE_UBYTE4)? // (D3DDECLUSAGE_NORMAL / D3DDECLTYPE_FLOAT3, D3DDECLTYPE_FLOAT16_4, D3DDECLTYPE_SHORT4N, D3DDECLTYPE_UBYTE4N, or D3DDECLTYPE_DEC3N)? // (D3DDECLUSAGE_COLOR / D3DDECLTYPE_D3DCOLOR)? // (D3DDECLUSAGE_TEXCOORD / D3DDECLTYPE_FLOAT1, D3DDECLTYPE_FLOAT2 or D3DDECLTYPE_FLOAT16_2, D3DDECLTYPE_FLOAT3 or D3DDECLTYPE_FLOAT16_4, D3DDECLTYPE_FLOAT4 or D3DDECLTYPE_FLOAT16_4)* // (D3DDECLUSAGE_TANGENT / same as D3DDECLUSAGE_NORMAL)? // (D3DDECLUSAGE_BINORMAL / same as D3DDECLUSAGE_NORMAL)? enum D3DDECLUSAGE { D3DDECLUSAGE_POSITION = 0, D3DDECLUSAGE_BLENDWEIGHT = 1, D3DDECLUSAGE_BLENDINDICES = 2, D3DDECLUSAGE_NORMAL = 3, D3DDECLUSAGE_TEXCOORD = 5, D3DDECLUSAGE_TANGENT = 6, D3DDECLUSAGE_BINORMAL = 7, D3DDECLUSAGE_COLOR = 10, }; enum D3DDECLTYPE { D3DDECLTYPE_FLOAT1 = 0, // 1D float expanded to (value, 0., 0., 1.) D3DDECLTYPE_FLOAT2 = 1, // 2D float expanded to (value, value, 0., 1.) D3DDECLTYPE_FLOAT3 = 2, // 3D float expanded to (value, value, value, 1.) D3DDECLTYPE_FLOAT4 = 3, // 4D float D3DDECLTYPE_D3DCOLOR = 4, // 4D packed unsigned bytes mapped to 0. to 1. range // Input is in D3DCOLOR format (ARGB) expanded to (R, G, B, A) D3DDECLTYPE_UBYTE4 = 5, // 4D unsigned byte D3DDECLTYPE_UBYTE4N = 8, // Each of 4 bytes is normalized by dividing to 255.0 D3DDECLTYPE_SHORT4N = 10, // 4D signed short normalized (v[0]/32767.0,v[1]/32767.0,v[2]/32767.0,v[3]/32767.0) D3DDECLTYPE_DEC3N = 14, // 3D signed 10 10 10 format normalized and expanded to (v[0]/511.0, v[1]/511.0, v[2]/511.0, 1) D3DDECLTYPE_FLOAT16_2 = 15, // Two 16-bit floating point values, expanded to (value, value, 0, 1) D3DDECLTYPE_FLOAT16_4 = 16, // Four 16-bit floating point values D3DDECLTYPE_UNUSED = 17, // When the type field in a decl is unused. // These are extensions for DXGI-based versions of Direct3D D3DDECLTYPE_DXGI_R10G10B10A2_UNORM = 32 + DXGI_FORMAT_R10G10B10A2_UNORM, D3DDECLTYPE_DXGI_R11G11B10_FLOAT = 32 + DXGI_FORMAT_R11G11B10_FLOAT, }; #pragma pack(push,4) struct D3DVERTEXELEMENT9 { uint16_t Stream; // Stream index uint16_t Offset; // Offset in the stream in bytes uint8_t Type; // Data type uint8_t Method; // Processing method uint8_t Usage; // Semantics uint8_t UsageIndex; // Semantic index }; #pragma pack(pop) //-------------------------------------------------------------------------------------- // Hard Defines for the various structures //-------------------------------------------------------------------------------------- constexpr uint32_t SDKMESH_FILE_VERSION = 101; constexpr uint32_t SDKMESH_FILE_VERSION_V2 = 200; constexpr uint32_t MAX_VERTEX_ELEMENTS = 32; constexpr uint32_t MAX_VERTEX_STREAMS = 16; constexpr uint32_t MAX_FRAME_NAME = 100; constexpr uint32_t MAX_MESH_NAME = 100; constexpr uint32_t MAX_SUBSET_NAME = 100; constexpr uint32_t MAX_MATERIAL_NAME = 100; constexpr uint32_t MAX_TEXTURE_NAME = MAX_PATH; constexpr uint32_t MAX_MATERIAL_PATH = MAX_PATH; constexpr uint32_t INVALID_FRAME = uint32_t(-1); constexpr uint32_t INVALID_MESH = uint32_t(-1); constexpr uint32_t INVALID_MATERIAL = uint32_t(-1); constexpr uint32_t INVALID_SUBSET = uint32_t(-1); constexpr uint32_t INVALID_ANIMATION_DATA = uint32_t(-1); //-------------------------------------------------------------------------------------- // Enumerated Types. These will have mirrors in both D3D9 and D3D11 //-------------------------------------------------------------------------------------- enum SDKMESH_PRIMITIVE_TYPE { PT_TRIANGLE_LIST = 0, PT_TRIANGLE_STRIP, PT_LINE_LIST, PT_LINE_STRIP, PT_POINT_LIST, PT_TRIANGLE_LIST_ADJ, PT_TRIANGLE_STRIP_ADJ, PT_LINE_LIST_ADJ, PT_LINE_STRIP_ADJ, PT_QUAD_PATCH_LIST, PT_TRIANGLE_PATCH_LIST, }; enum SDKMESH_INDEX_TYPE { IT_16BIT = 0, IT_32BIT, }; enum FRAME_TRANSFORM_TYPE { FTT_RELATIVE = 0, FTT_ABSOLUTE, //This is not currently used but is here to support absolute transformations in the future }; //-------------------------------------------------------------------------------------- // Structures. //-------------------------------------------------------------------------------------- #pragma pack(push,8) struct SDKMESH_HEADER { //Basic Info and sizes uint32_t Version; uint8_t IsBigEndian; uint64_t HeaderSize; uint64_t NonBufferDataSize; uint64_t BufferDataSize; //Stats uint32_t NumVertexBuffers; uint32_t NumIndexBuffers; uint32_t NumMeshes; uint32_t NumTotalSubsets; uint32_t NumFrames; uint32_t NumMaterials; //Offsets to Data uint64_t VertexStreamHeadersOffset; uint64_t IndexStreamHeadersOffset; uint64_t MeshDataOffset; uint64_t SubsetDataOffset; uint64_t FrameDataOffset; uint64_t MaterialDataOffset; }; struct SDKMESH_VERTEX_BUFFER_HEADER { uint64_t NumVertices; uint64_t SizeBytes; uint64_t StrideBytes; D3DVERTEXELEMENT9 Decl[MAX_VERTEX_ELEMENTS]; uint64_t DataOffset; }; struct SDKMESH_INDEX_BUFFER_HEADER { uint64_t NumIndices; uint64_t SizeBytes; uint32_t IndexType; uint64_t DataOffset; }; struct SDKMESH_MESH { char Name[MAX_MESH_NAME]; uint8_t NumVertexBuffers; uint32_t VertexBuffers[MAX_VERTEX_STREAMS]; uint32_t IndexBuffer; uint32_t NumSubsets; uint32_t NumFrameInfluences; //aka bones DirectX::XMFLOAT3 BoundingBoxCenter; DirectX::XMFLOAT3 BoundingBoxExtents; union { uint64_t SubsetOffset; INT* pSubsets; }; union { uint64_t FrameInfluenceOffset; uint32_t* pFrameInfluences; }; }; struct SDKMESH_SUBSET { char Name[MAX_SUBSET_NAME]; uint32_t MaterialID; uint32_t PrimitiveType; uint64_t IndexStart; uint64_t IndexCount; uint64_t VertexStart; uint64_t VertexCount; }; struct SDKMESH_FRAME { char Name[MAX_FRAME_NAME]; uint32_t Mesh; uint32_t ParentFrame; uint32_t ChildFrame; uint32_t SiblingFrame; DirectX::XMFLOAT4X4 Matrix; uint32_t AnimationDataIndex; //Used to index which set of keyframes transforms this frame }; struct SDKMESH_MATERIAL { char Name[MAX_MATERIAL_NAME]; // Use MaterialInstancePath char MaterialInstancePath[MAX_MATERIAL_PATH]; // Or fall back to d3d8-type materials char DiffuseTexture[MAX_TEXTURE_NAME]; char NormalTexture[MAX_TEXTURE_NAME]; char SpecularTexture[MAX_TEXTURE_NAME]; DirectX::XMFLOAT4 Diffuse; DirectX::XMFLOAT4 Ambient; DirectX::XMFLOAT4 Specular; DirectX::XMFLOAT4 Emissive; float Power; uint64_t Force64_1; uint64_t Force64_2; uint64_t Force64_3; uint64_t Force64_4; uint64_t Force64_5; uint64_t Force64_6; }; struct SDKMESH_MATERIAL_V2 { char Name[MAX_MATERIAL_NAME]; // PBR materials char RMATexture[MAX_TEXTURE_NAME]; char AlbedoTexture[MAX_TEXTURE_NAME]; char NormalTexture[MAX_TEXTURE_NAME]; char EmissiveTexture[MAX_TEXTURE_NAME]; float Alpha; char Reserved[60]; uint64_t Force64_1; uint64_t Force64_2; uint64_t Force64_3; uint64_t Force64_4; uint64_t Force64_5; uint64_t Force64_6; }; struct SDKANIMATION_FILE_HEADER { uint32_t Version; uint8_t IsBigEndian; uint32_t FrameTransformType; uint32_t NumFrames; uint32_t NumAnimationKeys; uint32_t AnimationFPS; uint64_t AnimationDataSize; uint64_t AnimationDataOffset; }; struct SDKANIMATION_DATA { DirectX::XMFLOAT3 Translation; DirectX::XMFLOAT4 Orientation; DirectX::XMFLOAT3 Scaling; }; struct SDKANIMATION_FRAME_DATA { char FrameName[MAX_FRAME_NAME]; uint64_t DataOffset; }; #pragma pack(pop) }; // namespace static_assert(sizeof(DXUT::D3DVERTEXELEMENT9) == 8, "Direct3D9 Decl structure size incorrect"); static_assert(sizeof(DXUT::SDKMESH_HEADER) == 104, "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKMESH_VERTEX_BUFFER_HEADER) == 288, "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKMESH_INDEX_BUFFER_HEADER) == 32, "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKMESH_MESH) == 224, "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKMESH_SUBSET) == 144, "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKMESH_FRAME) == 184, "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKMESH_MATERIAL) == 1256, "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKMESH_MATERIAL_V2) == sizeof(DXUT::SDKMESH_MATERIAL), "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKANIMATION_FILE_HEADER) == 40, "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKANIMATION_DATA) == 40, "SDK Mesh structure size incorrect"); static_assert(sizeof(DXUT::SDKANIMATION_FRAME_DATA) == 112, "SDK Mesh structure size incorrect"); namespace { HRESULT LoadDataFromFile(_In_z_ const wchar_t* fileName, std::unique_ptr& data, size_t& dataSize, bool anim) { // 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 (anim) { if (fileInfo.EndOfFile.LowPart < sizeof(DXUT::SDKANIMATION_FILE_HEADER)) { return E_FAIL; } } else { if (fileInfo.EndOfFile.LowPart < sizeof(DXUT::SDKMESH_HEADER)) { 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; } //--------------------------------------------------------------------------------- bool DumpAnimation(const wchar_t* fname, const uint8_t* meshData, size_t dataSize) { using namespace DXUT; auto header = reinterpret_cast(meshData); if (header->IsBigEndian) { wprintf(L"ERROR: This tool does not support BigEndian SDKMESH_ANIM files\n"); return false; } if (header->Version != SDKMESH_FILE_VERSION) { wprintf(L"ERROR: Unsupported SDKMESH_ANIN file version %u. Requires %u\n", header->Version, SDKMESH_FILE_VERSION); return false; } if (!header->NumFrames) { wprintf(L"ERROR: No frames found\n"); return false; } if (!header->NumAnimationKeys) { wprintf(L"ERROR: No keys found\n"); return false; } const wchar_t* type = nullptr; switch (header->FrameTransformType) { case FTT_RELATIVE: type = L"Relative"; break; case FTT_ABSOLUTE: type = L"Absolute"; break; default: wprintf(L"ERROR: Unknown frame transform type %u\n", header->FrameTransformType); return false; } wprintf(L"ANIM %ls\n\tVersion %u, %ls\n\t%u frames, %u keys, %ls, %u FPS\n", fname, header->Version, (header->IsBigEndian) ? L"BigEndian" : L"LittleEndian", header->NumFrames, header->NumAnimationKeys, type, header->AnimationFPS); if (dataSize < header->AnimationDataOffset || (dataSize < (header->AnimationDataOffset + header->NumFrames * sizeof(SDKANIMATION_FRAME_DATA)))) { wprintf(L"ERROR: Not enough data in file for animation frames\n"); return false; } auto frameArray = reinterpret_cast(meshData + header->AnimationDataOffset); for (UINT j = 0; j < header->NumFrames; ++j) { wprintf(L"\t Frame %u '%hs'\n", j, frameArray[j].FrameName); size_t base = size_t(frameArray[j].DataOffset) + sizeof(SDKANIMATION_FILE_HEADER); if (dataSize < base || (dataSize < (base + header->NumAnimationKeys * sizeof(SDKANIMATION_DATA)))) { wprintf(L"ERROR: Not enough data in file for animation key\n"); return false; } } return true; } //--------------------------------------------------------------------------------- void DumpDecl(const DXUT::D3DVERTEXELEMENT9* decl, size_t max) { using namespace DXUT; for (size_t j = 0; j < max; ++j) { if (decl[j].Usage == 0xFF) break; if (decl[j].Type == D3DDECLTYPE_UNUSED) break; if (decl[j].Method != 0) { wprintf(L"WARNING: Direct3D9 era tessellation not supported, method %u\n", decl[j].Method); } const wchar_t* usage = nullptr; switch (decl[j].Usage) { case D3DDECLUSAGE_POSITION: usage = L"POSITION"; break; case D3DDECLUSAGE_BLENDWEIGHT: usage = L"BLENDWEIGHT"; break; case D3DDECLUSAGE_BLENDINDICES: usage = L"BLENDINDICES"; break; case D3DDECLUSAGE_NORMAL: usage = L"NORMAL"; break; case D3DDECLUSAGE_TEXCOORD: usage = L"TEXCOORD"; break; case D3DDECLUSAGE_TANGENT: usage = L"TANGENT"; break; case D3DDECLUSAGE_BINORMAL: usage = L"BINORMAL"; break; case D3DDECLUSAGE_COLOR: usage = L"COLOR"; break; default: usage = L"*Uknown*"; break; } const wchar_t* format = nullptr; switch (decl[j].Type) { case D3DDECLTYPE_FLOAT1: format = L"DXGI_FORMAT_R32_FLOAT"; break; case D3DDECLTYPE_FLOAT2: format = L"DXGI_FORMAT_R32G32_FLOAT"; break; case D3DDECLTYPE_FLOAT3: format = L"DXGI_FORMAT_R32G32B32_FLOAT"; break; case D3DDECLTYPE_FLOAT4: format = L"DXGI_FORMAT_R32G32B32A32_FLOAT"; break; case D3DDECLTYPE_D3DCOLOR: format = L"DXGI_FORMAT_B8G8R8A8_UNORM"; break; case D3DDECLTYPE_UBYTE4: format = L"DXGI_FORMAT_R8G8B8A8_UINT"; break; case D3DDECLTYPE_UBYTE4N: format = L"DXGI_FORMAT_R8G8B8A8_UNORM"; break; case D3DDECLTYPE_SHORT4N: format = L"DXGI_FORMAT_R16G16B16A16_SNORM"; break; case D3DDECLTYPE_DEC3N: format = L"*D3DDECLTYPE_DEC3N*"; break; case D3DDECLTYPE_FLOAT16_2: format = L"DXGI_FORMAT_R16G16_FLOAT"; break; case D3DDECLTYPE_FLOAT16_4: format = L"DXGI_FORMAT_R16G16B16A16_FLOAT"; break; case D3DDECLTYPE_DXGI_R10G10B10A2_UNORM: format = L"DXGI_FORMAT_R10G10B10A2_UNORM"; break; case D3DDECLTYPE_DXGI_R11G11B10_FLOAT: format = L"DXGI_FORMAT_R11G11B10_FLOAT"; break; default: format = L"*Unknown*"; break; } wprintf(L"\t\t\"%ls\", %u, %ls, %u, %u\n", usage, decl[j].UsageIndex, format, decl[j].Stream, decl[j].Offset); } } //--------------------------------------------------------------------------------- template void DumpIB(UINT id, 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"\tIB #%u - %zu indices (%zu faces) %ls\n", id, count, nFaces, (sizeof(index_t) == 2) ? L"IT_16BIT" : L"IT_32BIT"); 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"\t ...\n"); } continue; } wprintf(L"\t %6zu:\t%u, %u, %u\n", j + 1, *iptr, *(iptr + 1), *(iptr + 2)); } } //--------------------------------------------------------------------------------- bool DumpMesh(const wchar_t* fname, const uint8_t* meshData, size_t dataSize, bool fulldump) { using namespace DXUT; auto header = reinterpret_cast(meshData); size_t headerSize = sizeof(SDKMESH_HEADER) + header->NumVertexBuffers * sizeof(SDKMESH_VERTEX_BUFFER_HEADER) + header->NumIndexBuffers * sizeof(SDKMESH_INDEX_BUFFER_HEADER); if (header->HeaderSize != headerSize) { wprintf(L"ERROR: Not a valid SDKMESH file\n"); return false; } if (dataSize < headerSize) { wprintf(L"ERROR: Not enough data in file for header\n"); return false; } if (header->IsBigEndian) { wprintf(L"ERROR: This tool does not support BigEndian SDKMESH files\n"); return false; } if (header->Version != SDKMESH_FILE_VERSION && header->Version != SDKMESH_FILE_VERSION_V2) { wprintf(L"ERROR: Unsupported SDKMESH file version %u. Requires %u or %u\n", header->Version, SDKMESH_FILE_VERSION, SDKMESH_FILE_VERSION_V2); return false; } if (!header->NumMeshes) { wprintf(L"ERROR: No meshes found\n"); return false; } if (!header->NumVertexBuffers) { wprintf(L"ERROR: No VBs found\n"); return false; } if (!header->NumIndexBuffers) { wprintf(L"ERROR: No IBs found\n"); return false; } if (!header->NumTotalSubsets) { wprintf(L"ERROR: No subsets found\n"); return false; } if (!header->NumMaterials) { wprintf(L"ERROR: No materials found\n"); return false; } wprintf(L"SDKMESH %ls\n\tVersion %u, %ls\n\t%u meshes, %u VBs, %u IBs, %u subsets, %u frames, %u materials\n", fname, header->Version, (header->IsBigEndian) ? L"BigEndian" : L"LittleEndian", header->NumMeshes, header->NumVertexBuffers, header->NumIndexBuffers, header->NumTotalSubsets, header->NumFrames, header->NumMaterials); // Sub-headers if (dataSize < header->VertexStreamHeadersOffset || (dataSize < (header->VertexStreamHeadersOffset + header->NumVertexBuffers * sizeof(SDKMESH_VERTEX_BUFFER_HEADER)))) { wprintf(L"ERROR: Not enough data in file for sub-headers\n"); return false; } auto vbArray = reinterpret_cast(meshData + header->VertexStreamHeadersOffset); if (dataSize < header->IndexStreamHeadersOffset || (dataSize < (header->IndexStreamHeadersOffset + header->NumIndexBuffers * sizeof(SDKMESH_INDEX_BUFFER_HEADER)))) { wprintf(L"ERROR: Not enough data in file for sub-headers\n"); return false; } auto ibArray = reinterpret_cast(meshData + header->IndexStreamHeadersOffset); if (dataSize < header->MeshDataOffset || (dataSize < (header->MeshDataOffset + header->NumMeshes * sizeof(SDKMESH_MESH)))) { wprintf(L"ERROR: Not enough data in file for sub-headers\n"); return false; } auto meshArray = reinterpret_cast(meshData + header->MeshDataOffset); if (dataSize < header->SubsetDataOffset || (dataSize < (header->SubsetDataOffset + header->NumTotalSubsets * sizeof(SDKMESH_SUBSET)))) { wprintf(L"ERROR: Not enough data in file for sub-headers\n"); return false; } auto subsetArray = reinterpret_cast(meshData + header->SubsetDataOffset); if (dataSize < header->FrameDataOffset || (dataSize < (header->FrameDataOffset + header->NumFrames * sizeof(SDKMESH_FRAME)))) { wprintf(L"ERROR: Not enough data in file for sub-headers\n"); return false; } auto frameArray = reinterpret_cast(meshData + header->FrameDataOffset); if (dataSize < header->MaterialDataOffset || (dataSize < (header->MaterialDataOffset + header->NumMaterials * sizeof(SDKMESH_MATERIAL)))) { wprintf(L"ERROR: Not enough data in file for sub-headers\n"); return false; } const SDKMESH_MATERIAL* materialArray = nullptr; const SDKMESH_MATERIAL_V2* materialArray_v2 = nullptr; if (header->Version == DXUT::SDKMESH_FILE_VERSION_V2) { materialArray_v2 = reinterpret_cast(meshData + header->MaterialDataOffset); } else { materialArray = reinterpret_cast(meshData + header->MaterialDataOffset); } // Buffer data uint64_t bufferDataOffset = header->HeaderSize + header->NonBufferDataSize; if ((dataSize < bufferDataOffset) || (dataSize < bufferDataOffset + header->BufferDataSize)) { wprintf(L"ERROR: Not enough data in file for buffer data\n"); return false; } const uint8_t* bufferData = meshData + bufferDataOffset; // Vertex Buffers for (UINT j = 0; j < header->NumVertexBuffers; ++j) { auto& vh = vbArray[j]; if (dataSize < vh.DataOffset || (dataSize < vh.DataOffset + vh.SizeBytes)) { wprintf(L"ERROR: Not enough data in file for VB\n"); return false; } wprintf(L"\tVB #%u - %llu vertices, %llu stride\n", j, vh.NumVertices, vh.StrideBytes); DumpDecl(vh.Decl, MAX_VERTEX_ELEMENTS); } // Index Buffers for (UINT j = 0; j < header->NumIndexBuffers; ++j) { auto& ih = ibArray[j]; if (dataSize < ih.DataOffset || (dataSize < ih.DataOffset + ih.SizeBytes)) { wprintf(L"ERROR: Not enough data in file for IB\n"); return false; } auto indices = reinterpret_cast(bufferData + (ih.DataOffset - bufferDataOffset)); switch (ih.IndexType) { case DXUT::IT_16BIT: DumpIB(j, reinterpret_cast(indices), static_cast(ih.NumIndices), fulldump); break; case DXUT::IT_32BIT: DumpIB(j, reinterpret_cast(indices), static_cast(ih.NumIndices), fulldump); break; default: wprintf(L"WARNING: Unknown index type for IB #%u", j); break; } } // Meshes wprintf(L"\tMeshes\n"); for (UINT meshIndex = 0; meshIndex < header->NumMeshes; ++meshIndex) { auto& mh = meshArray[meshIndex]; if (!mh.NumSubsets || !mh.NumVertexBuffers || mh.IndexBuffer >= header->NumIndexBuffers || mh.VertexBuffers[0] >= header->NumVertexBuffers) { wprintf(L"WARNING: Invalid mesh '%hs' found - #%u\n", mh.Name, meshIndex); } else { wchar_t str[128] = {}; for (size_t j = 0; j < mh.NumVertexBuffers; ++j) { wchar_t tmp[32]; swprintf_s(tmp, L" %u", mh.VertexBuffers[j]); wcscat_s(str, tmp); } wprintf(L"\t Mesh #%u '%hs'\n\t\t%u VBs [%ls ], %u IB, %u subsets, %u bones\n", meshIndex, mh.Name, mh.NumVertexBuffers, str, mh.IndexBuffer, mh.NumSubsets, mh.NumFrameInfluences); wprintf(L"\t\tBoundsMin: (%f,%f,%f)\n", double(mh.BoundingBoxCenter.x) - double(mh.BoundingBoxExtents.x), double(mh.BoundingBoxCenter.y) - double(mh.BoundingBoxExtents.y), double(mh.BoundingBoxCenter.z) - double(mh.BoundingBoxExtents.z)); wprintf(L"\t\tBoundsMax: (%f,%f,%f)\n", double(mh.BoundingBoxCenter.x) + double(mh.BoundingBoxExtents.x), double(mh.BoundingBoxCenter.y) + double(mh.BoundingBoxExtents.y), double(mh.BoundingBoxCenter.z) + double(mh.BoundingBoxExtents.z)); if (dataSize < mh.SubsetOffset || (dataSize < mh.SubsetOffset + mh.NumSubsets * sizeof(UINT))) { wprintf(L"ERROR: Not enough data in file for subset\n"); return false; } auto subsets = reinterpret_cast(meshData + mh.SubsetOffset); wprintf(L"\t\tSubsets (%u):\n", mh.NumSubsets); for (UINT j = 0; j < mh.NumSubsets; ++j) { auto sIndex = subsets[j]; if (sIndex == INVALID_SUBSET || sIndex >= header->NumTotalSubsets) { wprintf(L"ERROR: Invalid subset reference found (%u, %u)\n", sIndex, header->NumTotalSubsets); return false; } else { auto& subset = subsetArray[sIndex]; const wchar_t* primType = nullptr; switch (subset.PrimitiveType) { case DXUT::PT_TRIANGLE_LIST: primType = L"TriangleList"; break; case DXUT::PT_TRIANGLE_STRIP: primType = L"TriangleStrip"; break; case DXUT::PT_LINE_LIST: primType = L"LineList"; break; case DXUT::PT_LINE_STRIP: primType = L"LineStrip"; break; case DXUT::PT_POINT_LIST: primType = L"PointList"; break; case DXUT::PT_TRIANGLE_LIST_ADJ: primType = L"TriangleListAdj"; break; case DXUT::PT_TRIANGLE_STRIP_ADJ: primType = L"TriangleStripAdj"; break; case DXUT::PT_LINE_LIST_ADJ: primType = L"LineListAdj"; break; case DXUT::PT_LINE_STRIP_ADJ: primType = L"LineStripAdj"; break; case DXUT::PT_QUAD_PATCH_LIST: case DXUT::PT_TRIANGLE_PATCH_LIST: wprintf(L"ERROR: Direct3D9 era tessellation not supported, found in subset %u '%hs'\n", sIndex, subset.Name); break; default: primType = L"*Unknown*"; break; } if (primType) { if (subset.MaterialID == INVALID_MATERIAL || subset.MaterialID >= header->NumMaterials) { wprintf(L"WARNING: Subset %u '%hs' has invalid material index (%u, %u)\n", sIndex, subset.Name, subset.MaterialID, header->NumMaterials); } else { wprintf(L"\t\t Subset %u '%hs'\n\t\t\t material %u, %ls\n\t\t\tindex start %llu, index count %llu\n\t\t\tvertex start %llu, vertex count %llu\n", sIndex, subset.Name, subset.MaterialID, primType, subset.IndexStart, subset.IndexCount, subset.VertexStart, subset.VertexCount); } } } } if (mh.NumFrameInfluences > 0) { if (dataSize < mh.FrameInfluenceOffset || (dataSize < mh.FrameInfluenceOffset + mh.NumFrameInfluences * sizeof(UINT))) { wprintf(L"ERROR: Not enough data in file for frame influences\n"); return false; } auto influences = reinterpret_cast(meshData + mh.FrameInfluenceOffset); for (UINT j = 0; j < mh.NumFrameInfluences; ++j) { if (influences[j] >= header->NumFrames) { wprintf(L"ERROR: Invalid frame influence reference (%u)\n", influences[j]); return false; } if (fulldump) { // TODO - } } } } } wprintf(L"\tMaterials\n"); if (materialArray_v2) { for (UINT matIndex = 0; matIndex < header->NumMaterials; ++matIndex) { auto& mat = materialArray_v2[matIndex]; wprintf(L"\t Material %u '%hs' (v2)\n", matIndex, mat.Name); if (!fulldump) continue; if (*mat.AlbedoTexture) { wprintf(L"\t\t Albedo: '%hs'\n", mat.AlbedoTexture); } if (*mat.NormalTexture) { wprintf(L"\t\t Normal: '%hs'\n", mat.NormalTexture); } if (*mat.RMATexture) { wprintf(L"\t\t RMA: '%hs'\n", mat.RMATexture); } if (*mat.EmissiveTexture) { wprintf(L"\t\t Emissive: '%hs'\n", mat.EmissiveTexture); } wprintf(L"\t\t Alpha: '%f'\n", double(mat.Alpha)); } } else { for (UINT matIndex = 0; matIndex < header->NumMaterials; ++matIndex) { auto& mat = materialArray[matIndex]; wprintf(L"\t Material %u '%hs'\n", matIndex, mat.Name); if (!fulldump) continue; if (*mat.MaterialInstancePath) { wprintf(L"\t\t Path: '%hs'\n", mat.MaterialInstancePath); } if (*mat.DiffuseTexture) { wprintf(L"\t\t Diffuse: '%hs'\n", mat.DiffuseTexture); } if (*mat.NormalTexture) { wprintf(L"\t\t Normal: '%hs'\n", mat.NormalTexture); } if (*mat.SpecularTexture) { wprintf(L"\t\t Specular: '%hs'\n", mat.SpecularTexture); } wprintf(L"\t\t A: (%f, %f, %f, %f)\n\t\t D: (%f, %f, %f, %f)\n\t\t S: (%f, %f, %f, %f) ^%f\n\t\t E: (%f, %f, %f, %f)\n", double(mat.Ambient.x), double(mat.Ambient.y), double(mat.Ambient.z), double(mat.Ambient.w), double(mat.Diffuse.x), double(mat.Diffuse.y), double(mat.Diffuse.z), double(mat.Diffuse.w), double(mat.Specular.x), double(mat.Specular.y), double(mat.Specular.z), double(mat.Specular.w), double(mat.Power), double(mat.Emissive.x), double(mat.Emissive.y), double(mat.Emissive.z), double(mat.Emissive.w)); } } if (header->NumFrames > 0) { wprintf(L"\tFrames\n"); for (UINT j = 0; j < header->NumFrames; ++j) { auto& frame = frameArray[j]; wprintf(L"\t Frame %u '%hs'\n", j, frame.Name); if (frame.Mesh != INVALID_MESH && frame.Mesh >= header->NumMeshes) { wprintf(L"ERROR: Invalid frame mesh reference (%u)\n", frame.Mesh); return false; } if (frame.ParentFrame != INVALID_FRAME && frame.ParentFrame >= header->NumFrames) { wprintf(L"ERROR: Invalid frame parent reference (%u)\n", frame.ParentFrame); return false; } if (frame.ChildFrame != INVALID_FRAME && frame.ChildFrame >= header->NumFrames) { wprintf(L"ERROR: Invalid frame child reference (%u)\n", frame.ChildFrame); return false; } if (frame.SiblingFrame != INVALID_FRAME && frame.SiblingFrame >= header->NumFrames) { wprintf(L"ERROR: Invalid frame sibling reference (%u)\n", frame.SiblingFrame); return false; } if (fulldump) { if (frame.ParentFrame != INVALID_FRAME) wprintf(L"\t\t Parent: %u ('%hs')\n", frame.ParentFrame, frameArray[frame.ParentFrame].Name); if (frame.ChildFrame != INVALID_FRAME) wprintf(L"\t\t Child: %u ('%hs')\n", frame.ChildFrame, frameArray[frame.ChildFrame].Name); if (frame.SiblingFrame != INVALID_FRAME) wprintf(L"\t\t Sibling: %u ('%hs')\n", frame.SiblingFrame, frameArray[frame.SiblingFrame].Name); if (frame.Mesh != INVALID_MESH) { wprintf(L"\t\t Mesh %u ('%hs')\n", frame.Mesh, meshArray[frame.Mesh].Name); } if (frame.AnimationDataIndex != INVALID_ANIMATION_DATA) { wprintf(L"\t\t Animation %u\n", frame.AnimationDataIndex); } // TODO - } } } return true; } } //--------------------------------------------------------------------------------- int wmain(int argc, wchar_t* argv[]) { if (argc != 2 && argc != 3) { wprintf(L"Usage: %ls | [-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 '%ls'\n", argv[2]); return -1; } } bool isanim = false; { wchar_t ext[_MAX_EXT]; _wsplitpath_s(argv[1], nullptr, 0, nullptr, 0, nullptr, 0, ext, _MAX_EXT); if (!_wcsicmp(ext, L".sdkmesh_anim")) isanim = true; } size_t dataSize = 0; std::unique_ptr data; HRESULT hr = LoadDataFromFile(argv[1], data, dataSize, isanim); if (FAILED(hr)) { wprintf(L"ERROR: failed to load %ls\n", argv[1]); return -1; } if (isanim) { if (!DumpAnimation(argv[1], data.get(), dataSize)) return -1; } else { if (!DumpMesh(argv[1], data.get(), dataSize, fulldump)) return -1; } return 0; }