text: //--------------------------------------------------------------------------------------
  // 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 "Effects.h"
  #include "VertexTypes.h"
  
  #include "DirectXHelpers.h"
  #include "PlatformHelpers.h"
  #include "BinaryReader.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<IEffect>        effect;
          ComPtr<ID3D11InputLayout>       il;
  
          MaterialRecordCMO() noexcept :
              pMaterial(nullptr),
              texture{} {}
      };
  
      // Helper for creating a D3D input layout.
      void CreateInputLayout(_In_ ID3D11Device* device, IEffect* effect, _Out_ ID3D11InputLayout** pInputLayout, bool skinning)
      {
          void const* shaderByteCode;
          size_t byteCodeLength;
  
          effect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength);
  
          if (skinning)
          {
              ThrowIfFailed(
                  device->CreateInputLayout(VertexPositionNormalTangentColorTextureSkinning::InputElements,
                  VertexPositionNormalTangentColorTextureSkinning::InputElementCount,
                  shaderByteCode, byteCodeLength,
                  pInputLayout)
              );
          }
          else
          {
              ThrowIfFailed(
                  device->CreateInputLayout(VertexPositionNormalTangentColorTexture::InputElements,
                  VertexPositionNormalTangentColorTexture::InputElementCount,
                  shaderByteCode, byteCodeLength,
                  pInputLayout)
              );
          }
  
          _Analysis_assume_(*pInputLayout != 0);
  
          SetDebugObjectName(*pInputLayout, "ModelCMO");
      }
  
      // Shared VB input element description
      INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT;
      std::shared_ptr<std::vector<D3D11_INPUT_ELEMENT_DESC>> g_vbdecl;
      std::shared_ptr<std::vector<D3D11_INPUT_ELEMENT_DESC>> 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<std::vector<D3D11_INPUT_ELEMENT_DESC>>(
              VertexPositionNormalTangentColorTexture::InputElements,
              VertexPositionNormalTangentColorTexture::InputElements + VertexPositionNormalTangentColorTexture::InputElementCount);
  
          g_vbdeclSkinning = std::make_shared<std::vector<D3D11_INPUT_ELEMENT_DESC>>(
              VertexPositionNormalTangentColorTextureSkinning::InputElements,
              VertexPositionNormalTangentColorTextureSkinning::InputElements + VertexPositionNormalTangentColorTextureSkinning::InputElementCount);
          return TRUE;
      }
  }
  
  
  //======================================================================================
  // Model Loader
  //======================================================================================
  
  _Use_decl_annotations_
  std::unique_ptr<Model> DirectX::Model::CreateFromCMO(ID3D11Device* d3dDevice, const uint8_t* meshData, size_t dataSize, IEffectFactory& fxFactory, bool ccw, bool pmalpha)
  {
      if (!InitOnceExecuteOnce(&g_InitOnce, InitializeDecl, nullptr, nullptr))
          throw std::exception("One-time initialization failed");
  
      if (!d3dDevice || !meshData)
          throw std::exception("Device and meshData cannot be null");
  
      auto fxFactoryDGSL = dynamic_cast<DGSLEffectFactory*>(&fxFactory);
  
      // Meshes
      auto nMesh = reinterpret_cast<const UINT*>(meshData);
      size_t usedSize = sizeof(UINT);
      if (dataSize < usedSize)
          throw std::exception("End of file");
  
      if (!*nMesh)
          throw std::exception("No meshes found");
  
      std::unique_ptr<Model> model(new Model());
  
      for (UINT meshIndex = 0; meshIndex < *nMesh; ++meshIndex)
      {
          // Mesh name
          auto nName = reinterpret_cast<const UINT*>(meshData + usedSize);
          usedSize += sizeof(UINT);
          if (dataSize < usedSize)
              throw std::exception("End of file");
  
          auto meshName = reinterpret_cast<const wchar_t*>(meshData + usedSize);
  
          usedSize += sizeof(wchar_t)*(*nName);
          if (dataSize < usedSize)
              throw std::exception("End of file");
  
          auto mesh = std::make_shared<ModelMesh>();
          mesh->name.assign(meshName, *nName);
          mesh->ccw = ccw;
          mesh->pmalpha = pmalpha;
  
          // Materials
          auto nMats = reinterpret_cast<const UINT*>(meshData + usedSize);
          usedSize += sizeof(UINT);
          if (dataSize < usedSize)
              throw std::exception("End of file");
  
          std::vector<MaterialRecordCMO> materials;
          materials.reserve(*nMats);
          for (UINT j = 0; j < *nMats; ++j)
          {
              MaterialRecordCMO m;
  
              // Material name
              nName = reinterpret_cast<const UINT*>(meshData + usedSize);
              usedSize += sizeof(UINT);
              if (dataSize < usedSize)
                  throw std::exception("End of file");
  
              auto matName = reinterpret_cast<const wchar_t*>(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<const VSD3DStarter::Material*>(meshData + usedSize);
              usedSize += sizeof(VSD3DStarter::Material);
              if (dataSize < usedSize)
                  throw std::exception("End of file");
  
              m.pMaterial = matSetting;
  
              // Pixel shader name
              nName = reinterpret_cast<const UINT*>(meshData + usedSize);
              usedSize += sizeof(UINT);
              if (dataSize < usedSize)
                  throw std::exception("End of file");
  
              auto psName = reinterpret_cast<const wchar_t*>(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<const UINT*>(meshData + usedSize);
                  usedSize += sizeof(UINT);
                  if (dataSize < usedSize)
                      throw std::exception("End of file");
  
                  auto txtName = reinterpret_cast<const wchar_t*>(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<const UINT*>(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<const VSD3DStarter::SubMesh*>(meshData + usedSize);
          usedSize += sizeof(VSD3DStarter::SubMesh) * (*nSubmesh);
          if (dataSize < usedSize)
              throw std::exception("End of file");
  
          // Index buffers
          auto nIBs = reinterpret_cast<const UINT*>(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;
          ibData.reserve(*nIBs);
  
          std::vector<ComPtr<ID3D11Buffer>> ibs;
          ibs.resize(*nIBs);
  
          for (UINT j = 0; j < *nIBs; ++j)
          {
              auto nIndexes = reinterpret_cast<const UINT*>(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 > (D3D11_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u))
                  throw std::exception("IB too large for DirectX 11");
  
              auto ibBytes = static_cast<size_t>(sizeInBytes);
  
              auto indexes = reinterpret_cast<const USHORT*>(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<UINT>(ibBytes);
              desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
  
              D3D11_SUBRESOURCE_DATA initData = {};
              initData.pSysMem = indexes;
  
              ThrowIfFailed(
                  d3dDevice->CreateBuffer(&desc, &initData, &ibs[j])
              );
  
              SetDebugObjectName(ibs[j].Get(), "ModelCMO");
          }
  
          assert(ibData.size() == *nIBs);
          assert(ibs.size() == *nIBs);
  
          // Vertex buffers
          auto nVBs = reinterpret_cast<const UINT*>(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;
          vbData.reserve(*nVBs);
          for (UINT j = 0; j < *nVBs; ++j)
          {
              auto nVerts = reinterpret_cast<const UINT*>(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<const VertexPositionNormalTangentColorTexture*>(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<const UINT*>(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<const UINT*>(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<const VSD3DStarter::SkinningVertex*>(meshData + usedSize);
                  usedSize += vbBytes;
                  if (dataSize < usedSize)
                      throw std::exception("End of file");
  
                  vbData[j].skinPtr = verts;
              }
          }
  
          // Extents
          auto extents = reinterpret_cast<const VSD3DStarter::MeshExtents*>(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<const UINT*>(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<const UINT*>(meshData + usedSize);
                  usedSize += sizeof(UINT);
                  if (dataSize < usedSize)
                      throw std::exception("End of file");
  
                  auto boneName = reinterpret_cast<const wchar_t*>(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<const VSD3DStarter::Bone*>(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<const UINT*>(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<const UINT*>(meshData + usedSize);
                  usedSize += sizeof(UINT);
                  if (dataSize < usedSize)
                      throw std::exception("End of file");
  
                  auto clipName = reinterpret_cast<const wchar_t*>(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<const VSD3DStarter::Clip*>(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<const VSD3DStarter::Keyframe*>(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<ComPtr<ID3D11Buffer>> 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 > 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<size_t>(sizeInBytes);
  
              D3D11_BUFFER_DESC desc = {};
              desc.Usage = D3D11_USAGE_DEFAULT;
              desc.ByteWidth = static_cast<UINT>(bytes);
              desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
  
              if (fxFactoryDGSL && !enableSkinning)
              {
                  // Can use CMO vertex data directly
                  D3D11_SUBRESOURCE_DATA initData = {};
                  initData.pSysMem = vbData[j].ptr;
  
                  ThrowIfFailed(
                      d3dDevice->CreateBuffer(&desc, &initData, &vbs[j])
                  );
              }
              else
              {
                  std::unique_ptr<uint8_t[]> temp(new uint8_t[bytes + (sizeof(UINT) * nVerts)]);
  
                  auto visited = reinterpret_cast<UINT*>(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<VertexPositionNormalTangentColorTexture*>(ptr) = sptr[v];
  
                          auto skinv = reinterpret_cast<VertexPositionNormalTangentColorTextureSkinning*>(ptr);
                          skinv->SetBlendIndices(*reinterpret_cast<const XMUINT4*>(skinptr[v].boneIndex));
                          skinv->SetBlendWeights(*reinterpret_cast<const XMFLOAT4*>(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<VertexPositionNormalTangentColorTexture*>(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 = {};
                  initData.pSysMem = temp.get();
  
                  ThrowIfFailed(
                      d3dDevice->CreateBuffer(&desc, &initData, &vbs[j])
                  );
              }
  
              SetDebugObjectName(vbs[j].Get(), "ModelCMO");
          }
  
          assert(vbs.size() == *nVBs);
  
          // Create Effects
          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 = XMFLOAT3(m.pMaterial->Ambient.x, m.pMaterial->Ambient.y, m.pMaterial->Ambient.z);
                  info.diffuseColor = XMFLOAT3(m.pMaterial->Diffuse.x, m.pMaterial->Diffuse.y, m.pMaterial->Diffuse.z);
                  info.specularColor = XMFLOAT3(m.pMaterial->Specular.x, m.pMaterial->Specular.y, m.pMaterial->Specular.z);
                  info.emissiveColor = XMFLOAT3(m.pMaterial->Emissive.x, m.pMaterial->Emissive.y, m.pMaterial->Emissive.z);
                  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();
  
                  const 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<DGSLEffect*>(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 = XMFLOAT3(m.pMaterial->Ambient.x, m.pMaterial->Ambient.y, m.pMaterial->Ambient.z);
                  info.diffuseColor = XMFLOAT3(m.pMaterial->Diffuse.x, m.pMaterial->Diffuse.y, m.pMaterial->Diffuse.z);
                  info.specularColor = XMFLOAT3(m.pMaterial->Specular.x, m.pMaterial->Specular.y, m.pMaterial->Specular.z);
                  info.emissiveColor = XMFLOAT3(m.pMaterial->Emissive.x, m.pMaterial->Emissive.y, m.pMaterial->Emissive.z);
                  info.diffuseTexture = m.texture[0].c_str();
  
                  m.effect = fxFactory.CreateEffect(info, nullptr);
              }
  
              CreateInputLayout(d3dDevice, 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<UINT>(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<Model> DirectX::Model::CreateFromCMO(ID3D11Device* d3dDevice, const wchar_t* szFileName, IEffectFactory& fxFactory, bool ccw, bool pmalpha)
  {
      size_t dataSize = 0;
      std::unique_ptr<uint8_t[]> data;
      HRESULT hr = BinaryReader::ReadEntireFile(szFileName, data, &dataSize);
      if (FAILED(hr))
      {
          DebugTrace("ERROR: CreateFromCMO failed (%08X) loading '%ls'\n", hr, szFileName);
          throw std::exception("CreateFromCMO");
      }
  
      auto model = CreateFromCMO(d3dDevice, data.get(), dataSize, fxFactory, ccw, pmalpha);
  
      model->name = szFileName;
  
      return model;
  }
  