diff --git a/Shadows/SampleFramework11/Model.cpp b/Shadows/SampleFramework11/Model.cpp index 414a2a8..d352117 100644 --- a/Shadows/SampleFramework11/Model.cpp +++ b/Shadows/SampleFramework11/Model.cpp @@ -105,6 +105,8 @@ void Mesh::Initialize(ID3D11Device* device, SDKMesh& sdkMesh, uint32 meshIdx, bo indices.resize(indexSize * numIndices, 0); memcpy(indices.data(), sdkMesh.GetRawIndicesAt(ibIdx), indexSize * numIndices); + name = sdkMeshData.Name; + if(generateTangents) GenerateTangentFrame(); @@ -603,6 +605,7 @@ void Model::CreateFromSDKMeshFile(ID3D11Device* device, LPCWSTR fileName, const material.SpecularPower = mat->Power; material.DiffuseMapName = AnsiToWString(mat->DiffuseTexture); material.NormalMapName = AnsiToWString(mat->NormalTexture); + material.Name = mat->Name; // Add the normal map prefix if (normalMapSuffix && material.DiffuseMapName.length() > 0 @@ -855,4 +858,126 @@ void Model::ReadFromFile(const wchar* path, ID3D11Device* device) Win32Call(CloseHandle(fileHandle)); } +static uint32 VertexElemIndex(const std::vector& inputElements, const char* semanticName, uint32 semanticIndex) +{ + for(uint32 elemIdx = 0; elemIdx < inputElements.size(); ++elemIdx) + { + const D3D11_INPUT_ELEMENT_DESC& elem = inputElements[elemIdx]; + if(strcmp(semanticName, elem.SemanticName) == 0 && elem.SemanticIndex == semanticIndex) + return elemIdx; + } + + return uint32(-1); +} + +void Model::SaveAsOBJ(const wchar* path) +{ + { + // Save the .mtl file + std::wstring mtlPath = GetFilePathWithoutExtension(path) + L".mtl"; + std::string mtlContents; + for(const MeshMaterial& material : meshMaterials) + { + mtlContents += MakeString("newmtl %s\n", material.Name.c_str()); + mtlContents += "illum 4\n"; + mtlContents += MakeString("Kd %f %f %f\n", material.DiffuseAlbedo.x, material.DiffuseAlbedo.y, material.DiffuseAlbedo.z); + mtlContents += MakeString("Ka %f %f %f\n", material.AmbientAlbedo.x, material.AmbientAlbedo.y, material.AmbientAlbedo.z); + mtlContents += "Tf 1.00 1.00 1.00\n"; + mtlContents += MakeString("map_Kd %ls\n", material.DiffuseMapName.c_str()); + if(material.NormalMapName != L"default-normalmap.dds") + mtlContents += MakeString("bump %ls\n", material.NormalMapName.c_str()); + mtlContents += "Ni 1.00\n"; + mtlContents += MakeString("Ks %f %f %f\n", material.SpecularAlbedo.x, material.SpecularAlbedo.y, material.SpecularAlbedo.z); + mtlContents += MakeString("Ns %f\n", material.SpecularPower); + } + + WriteStringAsFile(mtlPath.c_str(), mtlContents); + } + + { + std::string objContents; + objContents += "mtllib " + WStringToAnsi(GetFileNameWithoutExtension(path).c_str()) + ".mtl\n"; + + // Write out the vertex data first + for(const Mesh& mesh : meshes) + { + const uint32 posIdx = VertexElemIndex(mesh.inputElements, "POSITION", 0); + Assert_(posIdx != uint32(-1)); + + const D3D11_INPUT_ELEMENT_DESC& posElem = mesh.inputElements[posIdx]; + Assert_(posElem.Format == DXGI_FORMAT_R32G32B32_FLOAT); + + for(uint32 vtxIdx = 0; vtxIdx < mesh.numVertices; ++vtxIdx) + { + const Float3* pos = (const Float3*)&mesh.vertices[vtxIdx * mesh.vertexStride + posElem.AlignedByteOffset]; + objContents += MakeString("v %f %f %f\n", pos->x, pos->y, pos->z); + } + } + + for(const Mesh& mesh : meshes) + { + const uint32 uvIdx = VertexElemIndex(mesh.inputElements, "TEXCOORD", 0); + Assert_(uvIdx != uint32(-1)); + + const D3D11_INPUT_ELEMENT_DESC& uvElem = mesh.inputElements[uvIdx]; + Assert_(uvElem.Format == DXGI_FORMAT_R32G32_FLOAT); + + for(uint32 vtxIdx = 0; vtxIdx < mesh.numVertices; ++vtxIdx) + { + const Float2* uv = (const Float2*)&mesh.vertices[vtxIdx * mesh.vertexStride + uvElem.AlignedByteOffset]; + objContents += MakeString("vt %f %f\n", uv->x, 1.0f - uv->y); + } + } + + for(const Mesh& mesh : meshes) + { + const uint32 nmlIdx = VertexElemIndex(mesh.inputElements, "NORMAL", 0); + Assert_(nmlIdx != uint32(-1)); + + const D3D11_INPUT_ELEMENT_DESC& nmlElem = mesh.inputElements[nmlIdx]; + Assert_(nmlElem.Format == DXGI_FORMAT_R32G32B32_FLOAT); + + for(uint32 vtxIdx = 0; vtxIdx < mesh.numVertices; ++vtxIdx) + { + const Float3* nml = (const Float3*)&mesh.vertices[vtxIdx * mesh.vertexStride + nmlElem.AlignedByteOffset]; + objContents += MakeString("vn %f %f %f\n", nml->x, nml->y, nml->z); + } + } + + // Write out submesh material + faces + uint32 indexOffset = 0; + for(const Mesh& mesh : meshes) + { + for(uint32 meshPartIdx = 0; meshPartIdx < mesh.meshParts.size(); ++meshPartIdx) + { + const MeshPart& meshPart = mesh.meshParts[meshPartIdx]; + objContents += MakeString("g %s_%u\n", mesh.name.c_str(), meshPartIdx); + objContents += MakeString("usemtl %s\n", meshMaterials[meshPart.MaterialIdx].Name.c_str()); + + const uint16* indices16 = (const uint16*)(mesh.indices.data() + (meshPart.IndexStart * 2)); + const uint32* indices32 = (const uint32*)(mesh.indices.data() + (meshPart.IndexStart * 4)); + + Assert_(meshPart.IndexCount % 3 == 0); + const uint32 numTris = meshPart.IndexCount / 3; + for(uint32 triIdx = 0; triIdx < numTris; ++triIdx) + { + const uint32 idx0 = mesh.indexType == Mesh::Index16Bit ? indices16[triIdx * 3 + 0] : indices32[triIdx * 3 + 0]; + const uint32 idx1 = mesh.indexType == Mesh::Index16Bit ? indices16[triIdx * 3 + 1] : indices32[triIdx * 3 + 1]; + const uint32 idx2 = mesh.indexType == Mesh::Index16Bit ? indices16[triIdx * 3 + 2] : indices32[triIdx * 3 + 2]; + + const uint32 finalIdx0 = idx0 + indexOffset + 1; + const uint32 finalIdx1 = idx1 + indexOffset + 1; + const uint32 finalIdx2 = idx2 + indexOffset + 1; + + objContents += MakeString("f %u/%u/%u %u/%u/%u %u/%u/%u\n", finalIdx0, finalIdx0, finalIdx0, finalIdx1, finalIdx1, finalIdx1, finalIdx2, finalIdx2, finalIdx2); + } + } + + indexOffset += mesh.numVertices; + } + + WriteStringAsFile(path, objContents); + } +} + } \ No newline at end of file diff --git a/Shadows/SampleFramework11/Model.h b/Shadows/SampleFramework11/Model.h index 244145c..4ffb675 100644 --- a/Shadows/SampleFramework11/Model.h +++ b/Shadows/SampleFramework11/Model.h @@ -31,6 +31,7 @@ struct MeshMaterial std::wstring NormalMapName; ID3D11ShaderResourceViewPtr DiffuseMap; ID3D11ShaderResourceViewPtr NormalMap; + std::string Name; MeshMaterial() : SpecularPower(1.0f), Alpha(1.0f) { @@ -123,6 +124,8 @@ class Mesh std::vector vertices; std::vector indices; + + std::string name; }; class Model @@ -148,6 +151,8 @@ class Model void WriteToFile(const wchar* path, ID3D11Device* device, ID3D11DeviceContext* context); void ReadFromFile(const wchar* path, ID3D11Device* device); + void SaveAsOBJ(const wchar* path); + // Accessors std::vector& Materials() { return meshMaterials; }; const std::vector& Materials() const { return meshMaterials; }; diff --git a/Shadows/SampleFramework11/Utility.cpp b/Shadows/SampleFramework11/Utility.cpp index 3ffa505..b885615 100644 --- a/Shadows/SampleFramework11/Utility.cpp +++ b/Shadows/SampleFramework11/Utility.cpp @@ -21,6 +21,24 @@ namespace SampleFramework11 { +std::wstring MakeString(const wchar* format, ...) +{ + wchar buffer[1024 * 8] = { 0 }; + va_list args; + va_start(args, format); + vswprintf_s(buffer, ArraySize_(buffer), format, args); + return std::wstring(buffer); +} + +std::string MakeString(const char* format, ...) +{ + char buffer[1024 * 8] = { 0 }; + va_list args; + va_start(args, format); + vsprintf_s(buffer, ArraySize_(buffer), format, args); + return std::string(buffer); +} + // Converts from cartesian to barycentric coordinates XMFLOAT3 CartesianToBarycentric(float x, float y, const XMFLOAT2& pos1, const XMFLOAT2& pos2, const XMFLOAT2& pos3) { diff --git a/Shadows/SampleFramework11/Utility.h b/Shadows/SampleFramework11/Utility.h index 2bc3e0d..c1a6d2f 100644 --- a/Shadows/SampleFramework11/Utility.h +++ b/Shadows/SampleFramework11/Utility.h @@ -19,6 +19,8 @@ namespace SampleFramework11 { +#define ArraySize_(x) ((sizeof(x) / sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) + // Returns a size suitable for creating a constant buffer, by rounding up // to the next multiple of 16 inline UINT CBSize(UINT size) @@ -100,6 +102,9 @@ template inline std::string ToAnsiString(const T& val) return stream.str(); } +std::wstring MakeString(const wchar* format, ...); +std::string MakeString(const char* format, ...); + // Outputs a string to the debugger output inline void DebugPrint(const std::wstring& str) { diff --git a/Shadows/Shadows.cpp b/Shadows/Shadows.cpp index c9a3223..c197a93 100644 --- a/Shadows/Shadows.cpp +++ b/Shadows/Shadows.cpp @@ -105,6 +105,8 @@ void ShadowsApp::Initialize() models[i].CreateFromSDKMeshFile(device, path.c_str()); } + // models[0].SaveAsOBJ(L"..\\Content\\Models\\Powerplant\\Powerplant.obj"); + wstring characterPath(L"..\\Content\\Models\\Soldier\\Soldier.sdkmesh"); characterMesh.CreateFromSDKMeshFile(device, characterPath.c_str());