//-------------------------------------------------------------------------------------- // File: ModelLoadCMO.cpp // // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. // // http://go.microsoft.com/fwlink/?LinkId=248929 //-------------------------------------------------------------------------------------- #include "pch.h" #include "Model.h" #include "DDSTextureLoader.h" #include "DirectXHelpers.h" #include "Effects.h" #include "VertexTypes.h" #include "BinaryReader.h" #include "PlatformHelpers.h" using namespace DirectX; using Microsoft::WRL::ComPtr; //-------------------------------------------------------------------------------------- // .CMO files are built by Visual Studio 2012 and an example renderer is provided // in the VS Direct3D Starter Kit // http://code.msdn.microsoft.com/Visual-Studio-3D-Starter-455a15f1 //-------------------------------------------------------------------------------------- namespace VSD3DStarter { // .CMO files // UINT - Mesh count // { [Mesh count] // UINT - Length of name // wchar_t[] - Name of mesh (if length > 0) // UINT - Material count // { [Material count] // UINT - Length of material name // wchar_t[] - Name of material (if length > 0) // Material structure // UINT - Length of pixel shader name // wchar_t[] - Name of pixel shader (if length > 0) // { [8] // UINT - Length of texture name // wchar_t[] - Name of texture (if length > 0) // } // } // BYTE - 1 if there is skeletal animation data present // UINT - SubMesh count // { [SubMesh count] // SubMesh structure // } // UINT - IB Count // { [IB Count] // UINT - Number of USHORTs in IB // USHORT[] - Array of indices // } // UINT - VB Count // { [VB Count] // UINT - Number of verts in VB // Vertex[] - Array of vertices // } // UINT - Skinning VB Count // { [Skinning VB Count] // UINT - Number of verts in Skinning VB // SkinningVertex[] - Array of skinning verts // } // MeshExtents structure // [If skeleton animation data is not present, file ends here] // UINT - Bone count // { [Bone count] // UINT - Length of bone name // wchar_t[] - Bone name (if length > 0) // Bone structure // } // UINT - Animation clip count // { [Animation clip count] // UINT - Length of clip name // wchar_t[] - Clip name (if length > 0) // float - Start time // float - End time // UINT - Keyframe count // { [Keyframe count] // Keyframe structure // } // } // } #pragma pack(push,1) struct Material { DirectX::XMFLOAT4 Ambient; DirectX::XMFLOAT4 Diffuse; DirectX::XMFLOAT4 Specular; float SpecularPower; DirectX::XMFLOAT4 Emissive; DirectX::XMFLOAT4X4 UVTransform; }; const uint32_t MAX_TEXTURE = 8; struct SubMesh { UINT MaterialIndex; UINT IndexBufferIndex; UINT VertexBufferIndex; UINT StartIndex; UINT PrimCount; }; const uint32_t NUM_BONE_INFLUENCES = 4; static_assert(sizeof(VertexPositionNormalTangentColorTexture) == 52, "mismatch with CMO vertex type"); struct SkinningVertex { UINT boneIndex[NUM_BONE_INFLUENCES]; float boneWeight[NUM_BONE_INFLUENCES]; }; struct MeshExtents { float CenterX, CenterY, CenterZ; float Radius; float MinX, MinY, MinZ; float MaxX, MaxY, MaxZ; }; struct Bone { INT ParentIndex; DirectX::XMFLOAT4X4 InvBindPos; DirectX::XMFLOAT4X4 BindPos; DirectX::XMFLOAT4X4 LocalTransform; }; struct Clip { float StartTime; float EndTime; UINT keys; }; struct Keyframe { UINT BoneIndex; float Time; DirectX::XMFLOAT4X4 Transform; }; #pragma pack(pop) const Material s_defMaterial = { { 0.2f, 0.2f, 0.2f, 1.f }, { 0.8f, 0.8f, 0.8f, 1.f }, { 0.0f, 0.0f, 0.0f, 1.f }, 1.f, { 0.0f, 0.0f, 0.0f, 1.0f }, { 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f }, }; } // namespace static_assert(sizeof(VSD3DStarter::Material) == 132, "CMO Mesh structure size incorrect"); static_assert(sizeof(VSD3DStarter::SubMesh) == 20, "CMO Mesh structure size incorrect"); static_assert(sizeof(VSD3DStarter::SkinningVertex) == 32, "CMO Mesh structure size incorrect"); static_assert(sizeof(VSD3DStarter::MeshExtents) == 40, "CMO Mesh structure size incorrect"); static_assert(sizeof(VSD3DStarter::Bone) == 196, "CMO Mesh structure size incorrect"); static_assert(sizeof(VSD3DStarter::Clip) == 12, "CMO Mesh structure size incorrect"); static_assert(sizeof(VSD3DStarter::Keyframe) == 72, "CMO Mesh structure size incorrect"); namespace { //---------------------------------------------------------------------------------- struct MaterialRecordCMO { const VSD3DStarter::Material* pMaterial; std::wstring name; std::wstring pixelShader; std::wstring texture[VSD3DStarter::MAX_TEXTURE]; std::shared_ptr effect; ComPtr il; MaterialRecordCMO() noexcept : pMaterial(nullptr), texture{} {} }; // Helper for creating a D3D input layout. void CreateCMOInputLayout(_In_ ID3D11Device* device, _In_ IEffect* effect, _Outptr_ ID3D11InputLayout** pInputLayout, bool skinning) { if (skinning) { ThrowIfFailed( CreateInputLayoutFromEffect(device, effect, pInputLayout) ); } else { ThrowIfFailed( CreateInputLayoutFromEffect(device, effect, pInputLayout) ); } assert(pInputLayout != nullptr && *pInputLayout != nullptr); _Analysis_assume_(pInputLayout != nullptr && *pInputLayout != nullptr); SetDebugObjectName(*pInputLayout, "ModelCMO"); } // Shared VB input element description INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT; std::shared_ptr> g_vbdecl; std::shared_ptr> g_vbdeclSkinning; BOOL CALLBACK InitializeDecl(PINIT_ONCE initOnce, PVOID Parameter, PVOID *lpContext) { UNREFERENCED_PARAMETER(initOnce); UNREFERENCED_PARAMETER(Parameter); UNREFERENCED_PARAMETER(lpContext); g_vbdecl = std::make_shared>( VertexPositionNormalTangentColorTexture::InputElements, VertexPositionNormalTangentColorTexture::InputElements + VertexPositionNormalTangentColorTexture::InputElementCount); g_vbdeclSkinning = std::make_shared>( VertexPositionNormalTangentColorTextureSkinning::InputElements, VertexPositionNormalTangentColorTextureSkinning::InputElements + VertexPositionNormalTangentColorTextureSkinning::InputElementCount); return TRUE; } inline XMFLOAT3 GetMaterialColor(float r, float g, float b, bool srgb) { if (srgb) { XMVECTOR v = XMVectorSet(r, g, b, 1.f); v = XMColorSRGBToRGB(v); XMFLOAT3 result; XMStoreFloat3(&result, v); return result; } else { return XMFLOAT3(r, g, b); } } } //====================================================================================== // Model Loader //====================================================================================== _Use_decl_annotations_ std::unique_ptr DirectX::Model::CreateFromCMO( ID3D11Device* device, const uint8_t* meshData, size_t dataSize, IEffectFactory& fxFactory, ModelLoaderFlags flags) { if (!InitOnceExecuteOnce(&g_InitOnce, InitializeDecl, nullptr, nullptr)) throw std::exception("One-time initialization failed"); if (!device || !meshData) throw std::exception("Device and meshData cannot be null"); auto fxFactoryDGSL = dynamic_cast(&fxFactory); // Meshes auto nMesh = reinterpret_cast(meshData); size_t usedSize = sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); if (!*nMesh) throw std::exception("No meshes found"); auto model = std::make_unique(); for (UINT meshIndex = 0; meshIndex < *nMesh; ++meshIndex) { // Mesh name auto nName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); auto meshName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(wchar_t)*(*nName); if (dataSize < usedSize) throw std::exception("End of file"); auto mesh = std::make_shared(); mesh->name.assign(meshName, *nName); mesh->ccw = (flags & ModelLoader_CounterClockwise) != 0; mesh->pmalpha = (flags & ModelLoader_PremultipledAlpha) != 0; // Materials auto nMats = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); std::vector materials; materials.reserve(*nMats); for (UINT j = 0; j < *nMats; ++j) { MaterialRecordCMO m; // Material name nName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); auto matName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(wchar_t)*(*nName); if (dataSize < usedSize) throw std::exception("End of file"); m.name.assign(matName, *nName); // Material settings auto matSetting = reinterpret_cast(meshData + usedSize); usedSize += sizeof(VSD3DStarter::Material); if (dataSize < usedSize) throw std::exception("End of file"); m.pMaterial = matSetting; // Pixel shader name nName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); auto psName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(wchar_t)*(*nName); if (dataSize < usedSize) throw std::exception("End of file"); m.pixelShader.assign(psName, *nName); for (UINT t = 0; t < VSD3DStarter::MAX_TEXTURE; ++t) { nName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); auto txtName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(wchar_t)*(*nName); if (dataSize < usedSize) throw std::exception("End of file"); m.texture[t].assign(txtName, *nName); } materials.emplace_back(m); } assert(materials.size() == *nMats); if (materials.empty()) { // Add default material if none defined MaterialRecordCMO m; m.pMaterial = &VSD3DStarter::s_defMaterial; m.name = L"Default"; materials.emplace_back(m); } // Skeletal data? const BYTE* bSkeleton = meshData + usedSize; usedSize += sizeof(BYTE); if (dataSize < usedSize) throw std::exception("End of file"); // Submeshes auto nSubmesh = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); if (!*nSubmesh) throw std::exception("No submeshes found\n"); auto subMesh = reinterpret_cast(meshData + usedSize); usedSize += sizeof(VSD3DStarter::SubMesh) * (*nSubmesh); if (dataSize < usedSize) throw std::exception("End of file"); // Index buffers auto nIBs = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); if (!*nIBs) throw std::exception("No index buffers found\n"); struct IBData { size_t nIndices; const USHORT* ptr; }; std::vector ibData; ibData.reserve(*nIBs); std::vector> ibs; ibs.resize(*nIBs); for (UINT j = 0; j < *nIBs; ++j) { auto nIndexes = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); if (!*nIndexes) throw std::exception("Empty index buffer found\n"); uint64_t sizeInBytes = uint64_t(*(nIndexes)) * sizeof(USHORT); if (sizeInBytes > UINT32_MAX) throw std::exception("IB too large"); if (!(flags & ModelLoader_AllowLargeModels)) { if (sizeInBytes > (D3D11_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) throw std::exception("IB too large for DirectX 11"); } auto ibBytes = static_cast(sizeInBytes); auto indexes = reinterpret_cast(meshData + usedSize); usedSize += ibBytes; if (dataSize < usedSize) throw std::exception("End of file"); IBData ib; ib.nIndices = *nIndexes; ib.ptr = indexes; ibData.emplace_back(ib); D3D11_BUFFER_DESC desc = {}; desc.Usage = D3D11_USAGE_DEFAULT; desc.ByteWidth = static_cast(ibBytes); desc.BindFlags = D3D11_BIND_INDEX_BUFFER; D3D11_SUBRESOURCE_DATA initData = { indexes, 0, 0 }; ThrowIfFailed( device->CreateBuffer(&desc, &initData, &ibs[j]) ); SetDebugObjectName(ibs[j].Get(), "ModelCMO"); } assert(ibData.size() == *nIBs); assert(ibs.size() == *nIBs); // Vertex buffers auto nVBs = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); if (!*nVBs) throw std::exception("No vertex buffers found\n"); struct VBData { size_t nVerts; const VertexPositionNormalTangentColorTexture* ptr; const VSD3DStarter::SkinningVertex* skinPtr; }; std::vector vbData; vbData.reserve(*nVBs); for (UINT j = 0; j < *nVBs; ++j) { auto nVerts = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); if (!*nVerts) throw std::exception("Empty vertex buffer found\n"); size_t vbBytes = sizeof(VertexPositionNormalTangentColorTexture) * (*(nVerts)); auto verts = reinterpret_cast(meshData + usedSize); usedSize += vbBytes; if (dataSize < usedSize) throw std::exception("End of file"); VBData vb; vb.nVerts = *nVerts; vb.ptr = verts; vb.skinPtr = nullptr; vbData.emplace_back(vb); } assert(vbData.size() == *nVBs); // Skinning vertex buffers auto nSkinVBs = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); if (*nSkinVBs) { if (*nSkinVBs != *nVBs) throw std::exception("Number of VBs not equal to number of skin VBs"); for (UINT j = 0; j < *nSkinVBs; ++j) { auto nVerts = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); if (!*nVerts) throw std::exception("Empty skinning vertex buffer found\n"); if (vbData[j].nVerts != *nVerts) throw std::exception("Mismatched number of verts for skin VBs"); size_t vbBytes = sizeof(VSD3DStarter::SkinningVertex) * (*(nVerts)); auto verts = reinterpret_cast(meshData + usedSize); usedSize += vbBytes; if (dataSize < usedSize) throw std::exception("End of file"); vbData[j].skinPtr = verts; } } // Extents auto extents = reinterpret_cast(meshData + usedSize); usedSize += sizeof(VSD3DStarter::MeshExtents); if (dataSize < usedSize) throw std::exception("End of file"); mesh->boundingSphere.Center.x = extents->CenterX; mesh->boundingSphere.Center.y = extents->CenterY; mesh->boundingSphere.Center.z = extents->CenterZ; mesh->boundingSphere.Radius = extents->Radius; XMVECTOR min = XMVectorSet(extents->MinX, extents->MinY, extents->MinZ, 0.f); XMVECTOR max = XMVectorSet(extents->MaxX, extents->MaxY, extents->MaxZ, 0.f); BoundingBox::CreateFromPoints(mesh->boundingBox, min, max); #if 0 // Animation data if (*bSkeleton) { // Bones auto nBones = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); if (!*nBones) throw std::exception("Animation bone data is missing\n"); for (UINT j = 0; j < *nBones; ++j) { // Bone name nName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); auto boneName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(wchar_t)*(*nName); if (dataSize < usedSize) throw std::exception("End of file"); // TODO - What to do with bone name? boneName; // Bone settings auto bones = reinterpret_cast(meshData + usedSize); usedSize += sizeof(VSD3DStarter::Bone); if (dataSize < usedSize) throw std::exception("End of file"); // TODO - What to do with bone data? bones; } // Animation Clips auto nClips = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); for (UINT j = 0; j < *nClips; ++j) { // Clip name nName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(UINT); if (dataSize < usedSize) throw std::exception("End of file"); auto clipName = reinterpret_cast(meshData + usedSize); usedSize += sizeof(wchar_t)*(*nName); if (dataSize < usedSize) throw std::exception("End of file"); // TODO - What to do with clip name? clipName; auto clip = reinterpret_cast(meshData + usedSize); usedSize += sizeof(VSD3DStarter::Clip); if (dataSize < usedSize) throw std::exception("End of file"); if (!clip->keys) throw std::exception("Keyframes missing in clip"); auto keys = reinterpret_cast(meshData + usedSize); usedSize += sizeof(VSD3DStarter::Keyframe) * clip->keys; if (dataSize < usedSize) throw std::exception("End of file"); // TODO - What to do with keys and clip->StartTime, clip->EndTime? keys; } } #else UNREFERENCED_PARAMETER(bSkeleton); #endif bool enableSkinning = (*nSkinVBs) != 0; // Build vertex buffers std::vector> vbs; vbs.resize(*nVBs); const size_t stride = enableSkinning ? sizeof(VertexPositionNormalTangentColorTextureSkinning) : sizeof(VertexPositionNormalTangentColorTexture); for (UINT j = 0; j < *nVBs; ++j) { size_t nVerts = vbData[j].nVerts; uint64_t sizeInBytes = uint64_t(stride) * uint64_t(nVerts); if (sizeInBytes > UINT32_MAX) throw std::exception("VB too large"); if (!(flags & ModelLoader_AllowLargeModels)) { if (sizeInBytes > uint64_t(D3D11_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u)) throw std::exception("VB too large for DirectX 11"); } size_t bytes = static_cast(sizeInBytes); D3D11_BUFFER_DESC desc = {}; desc.Usage = D3D11_USAGE_DEFAULT; desc.ByteWidth = static_cast(bytes); desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; if (fxFactoryDGSL && !enableSkinning) { // Can use CMO vertex data directly D3D11_SUBRESOURCE_DATA initData = { vbData[j].ptr, 0, 0 }; ThrowIfFailed( device->CreateBuffer(&desc, &initData, &vbs[j]) ); } else { auto temp = std::make_unique(bytes + (sizeof(UINT) * nVerts)); auto visited = reinterpret_cast(temp.get() + bytes); memset(visited, 0xff, sizeof(UINT) * nVerts); assert(vbData[j].ptr != nullptr); if (enableSkinning) { // Combine CMO multi-stream data into a single stream auto skinptr = vbData[j].skinPtr; assert(skinptr != nullptr); uint8_t* ptr = temp.get(); auto sptr = vbData[j].ptr; for (size_t v = 0; v < nVerts; ++v) { *reinterpret_cast(ptr) = sptr[v]; auto skinv = reinterpret_cast(ptr); skinv->SetBlendIndices(*reinterpret_cast(skinptr[v].boneIndex)); skinv->SetBlendWeights(*reinterpret_cast(skinptr[v].boneWeight)); ptr += stride; } } else { memcpy(temp.get(), vbData[j].ptr, bytes); } if (!fxFactoryDGSL) { // Need to fix up VB tex coords for UV transform which is not supported by basic effects for (UINT k = 0; k < *nSubmesh; ++k) { auto& sm = subMesh[k]; if (sm.VertexBufferIndex != j) continue; if ((sm.IndexBufferIndex >= *nIBs) || (sm.MaterialIndex >= materials.size())) throw std::exception("Invalid submesh found\n"); XMMATRIX uvTransform = XMLoadFloat4x4(&materials[sm.MaterialIndex].pMaterial->UVTransform); auto ib = ibData[sm.IndexBufferIndex].ptr; size_t count = ibData[sm.IndexBufferIndex].nIndices; for (size_t q = 0; q < count; ++q) { size_t v = ib[q]; if (v >= nVerts) throw std::exception("Invalid index found\n"); auto verts = reinterpret_cast(temp.get() + (v * stride)); if (visited[v] == UINT(-1)) { visited[v] = sm.MaterialIndex; XMVECTOR t = XMLoadFloat2(&verts->textureCoordinate); t = XMVectorSelect(g_XMIdentityR3, t, g_XMSelect1110); t = XMVector4Transform(t, uvTransform); XMStoreFloat2(&verts->textureCoordinate, t); } else if (visited[v] != sm.MaterialIndex) { #ifdef _DEBUG XMMATRIX uv2 = XMLoadFloat4x4(&materials[visited[v]].pMaterial->UVTransform); if (XMVector4NotEqual(uvTransform.r[0], uv2.r[0]) || XMVector4NotEqual(uvTransform.r[1], uv2.r[1]) || XMVector4NotEqual(uvTransform.r[2], uv2.r[2]) || XMVector4NotEqual(uvTransform.r[3], uv2.r[3])) { DebugTrace("WARNING: %ls - mismatched UV transforms for the same vertex; texture coordinates may not be correct\n", mesh->name.c_str()); } #endif } } } } // Create vertex buffer from temporary buffer D3D11_SUBRESOURCE_DATA initData = { temp.get(), 0, 0 }; ThrowIfFailed( device->CreateBuffer(&desc, &initData, &vbs[j]) ); } SetDebugObjectName(vbs[j].Get(), "ModelCMO"); } assert(vbs.size() == *nVBs); // Create Effects bool srgb = (flags & ModelLoader_MaterialColorsSRGB) != 0; for (size_t j = 0; j < materials.size(); ++j) { auto& m = materials[j]; if (fxFactoryDGSL) { DGSLEffectFactory::DGSLEffectInfo info; info.name = m.name.c_str(); info.specularPower = m.pMaterial->SpecularPower; info.perVertexColor = true; info.enableSkinning = enableSkinning; info.alpha = m.pMaterial->Diffuse.w; info.ambientColor = GetMaterialColor(m.pMaterial->Ambient.x, m.pMaterial->Ambient.y, m.pMaterial->Ambient.z, srgb); info.diffuseColor = GetMaterialColor(m.pMaterial->Diffuse.x, m.pMaterial->Diffuse.y, m.pMaterial->Diffuse.z, srgb); info.specularColor = GetMaterialColor(m.pMaterial->Specular.x, m.pMaterial->Specular.y, m.pMaterial->Specular.z, srgb); info.emissiveColor = GetMaterialColor(m.pMaterial->Emissive.x, m.pMaterial->Emissive.y, m.pMaterial->Emissive.z, srgb); info.diffuseTexture = m.texture[0].empty() ? nullptr : m.texture[0].c_str(); info.specularTexture = m.texture[1].empty() ? nullptr : m.texture[1].c_str(); info.normalTexture = m.texture[2].empty() ? nullptr : m.texture[2].c_str(); info.emissiveTexture = m.texture[3].empty() ? nullptr : m.texture[3].c_str(); info.pixelShader = m.pixelShader.c_str(); constexpr int offset = DGSLEffectFactory::DGSLEffectInfo::BaseTextureOffset; for (int i = 0; i < (DGSLEffect::MaxTextures - offset); ++i) { info.textures[i] = m.texture[i + offset].empty() ? nullptr : m.texture[i + offset].c_str(); } m.effect = fxFactoryDGSL->CreateDGSLEffect(info, nullptr); auto dgslEffect = static_cast(m.effect.get()); dgslEffect->SetUVTransform(XMLoadFloat4x4(&m.pMaterial->UVTransform)); } else { EffectFactory::EffectInfo info; info.name = m.name.c_str(); info.specularPower = m.pMaterial->SpecularPower; info.perVertexColor = true; info.enableSkinning = enableSkinning; info.alpha = m.pMaterial->Diffuse.w; info.ambientColor = GetMaterialColor(m.pMaterial->Ambient.x, m.pMaterial->Ambient.y, m.pMaterial->Ambient.z, srgb); info.diffuseColor = GetMaterialColor(m.pMaterial->Diffuse.x, m.pMaterial->Diffuse.y, m.pMaterial->Diffuse.z, srgb); info.specularColor = GetMaterialColor(m.pMaterial->Specular.x, m.pMaterial->Specular.y, m.pMaterial->Specular.z, srgb); info.emissiveColor = GetMaterialColor(m.pMaterial->Emissive.x, m.pMaterial->Emissive.y, m.pMaterial->Emissive.z, srgb); info.diffuseTexture = m.texture[0].c_str(); m.effect = fxFactory.CreateEffect(info, nullptr); } CreateCMOInputLayout(device, m.effect.get(), &m.il, enableSkinning); } // Build mesh parts for (UINT j = 0; j < *nSubmesh; ++j) { auto& sm = subMesh[j]; if ((sm.IndexBufferIndex >= *nIBs) || (sm.VertexBufferIndex >= *nVBs) || (sm.MaterialIndex >= materials.size())) throw std::exception("Invalid submesh found\n"); auto& mat = materials[sm.MaterialIndex]; auto part = new ModelMeshPart(); if (mat.pMaterial->Diffuse.w < 1) part->isAlpha = true; part->indexCount = sm.PrimCount * 3; part->startIndex = sm.StartIndex; part->vertexStride = static_cast(stride); part->inputLayout = mat.il; part->indexBuffer = ibs[sm.IndexBufferIndex]; part->vertexBuffer = vbs[sm.VertexBufferIndex]; part->effect = mat.effect; part->vbDecl = enableSkinning ? g_vbdeclSkinning : g_vbdecl; mesh->meshParts.emplace_back(part); } model->meshes.emplace_back(mesh); } return model; } //-------------------------------------------------------------------------------------- _Use_decl_annotations_ std::unique_ptr DirectX::Model::CreateFromCMO( ID3D11Device* device, const wchar_t* szFileName, IEffectFactory& fxFactory, ModelLoaderFlags flags) { size_t dataSize = 0; std::unique_ptr data; HRESULT hr = BinaryReader::ReadEntireFile(szFileName, data, &dataSize); if (FAILED(hr)) { DebugTrace("ERROR: CreateFromCMO failed (%08X) loading '%ls'\n", static_cast(hr), szFileName); throw std::exception("CreateFromCMO"); } auto model = CreateFromCMO(device, data.get(), dataSize, fxFactory, flags); model->name = szFileName; return model; }