зеркало из https://github.com/microsoft/DirectXTK.git
843 строки
31 KiB
C++
843 строки
31 KiB
C++
//--------------------------------------------------------------------------------------
|
|
// File: ModelLoadCMO.cpp
|
|
//
|
|
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
//
|
|
// http://go.microsoft.com/fwlink/?LinkId=248929
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
#include "pch.h"
|
|
#include "Model.h"
|
|
#include "DirectXHelpers.h"
|
|
#include "Effects.h"
|
|
#include "VertexTypes.h"
|
|
#include "BinaryReader.h"
|
|
#include "PlatformHelpers.h"
|
|
|
|
using namespace DirectX;
|
|
using Microsoft::WRL::ComPtr;
|
|
|
|
#include "CMO.h"
|
|
|
|
static_assert(sizeof(VertexPositionNormalTangentColorTexture) == sizeof(VSD3DStarter::VertexPositionNormalTangentColorTexture), "mismatch with CMO vertex type");
|
|
|
|
|
|
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 CreateCMOInputLayout(_In_ ID3D11Device* device, _In_ IEffect* effect, _Outptr_ ID3D11InputLayout** pInputLayout, bool skinning)
|
|
{
|
|
if (skinning)
|
|
{
|
|
ThrowIfFailed(
|
|
CreateInputLayoutFromEffect<VertexPositionNormalTangentColorTextureSkinning>(device, effect, pInputLayout)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ThrowIfFailed(
|
|
CreateInputLayoutFromEffect<VertexPositionNormalTangentColorTexture>(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<ModelMeshPart::InputLayoutCollection> g_vbdecl;
|
|
std::shared_ptr<ModelMeshPart::InputLayoutCollection> 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<ModelMeshPart::InputLayoutCollection>(
|
|
VertexPositionNormalTangentColorTexture::InputElements,
|
|
VertexPositionNormalTangentColorTexture::InputElements + VertexPositionNormalTangentColorTexture::InputElementCount);
|
|
|
|
g_vbdeclSkinning = std::make_shared<ModelMeshPart::InputLayoutCollection>(
|
|
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<Model> DirectX::Model::CreateFromCMO(
|
|
ID3D11Device* device,
|
|
const uint8_t* meshData, size_t dataSize,
|
|
IEffectFactory& fxFactory,
|
|
ModelLoaderFlags flags,
|
|
size_t* animsOffset)
|
|
{
|
|
if (animsOffset)
|
|
{
|
|
*animsOffset = 0;
|
|
}
|
|
|
|
if (!InitOnceExecuteOnce(&g_InitOnce, InitializeDecl, nullptr, nullptr))
|
|
throw std::system_error(std::error_code(static_cast<int>(GetLastError()), std::system_category()), "InitOnceExecuteOnce");
|
|
|
|
if (!device || !meshData)
|
|
throw std::invalid_argument("Device and meshData cannot be null");
|
|
|
|
auto fxFactoryDGSL = dynamic_cast<DGSLEffectFactory*>(&fxFactory);
|
|
|
|
// Meshes
|
|
auto nMesh = reinterpret_cast<const uint32_t*>(meshData);
|
|
size_t usedSize = sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (!*nMesh)
|
|
throw std::runtime_error("No meshes found");
|
|
|
|
auto model = std::make_unique<Model>();
|
|
|
|
for (size_t meshIndex = 0; meshIndex < *nMesh; ++meshIndex)
|
|
{
|
|
// Mesh name
|
|
auto nName = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
auto meshName = reinterpret_cast<const wchar_t*>(static_cast<const void*>(meshData + usedSize)); // [CodeQL.SM02986]: The cast here is intentional.
|
|
|
|
usedSize += sizeof(wchar_t)*(*nName);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
auto mesh = std::make_shared<ModelMesh>();
|
|
mesh->name.assign(meshName, *nName);
|
|
mesh->ccw = (flags & ModelLoader_CounterClockwise) != 0;
|
|
mesh->pmalpha = (flags & ModelLoader_PremultipledAlpha) != 0;
|
|
|
|
// Materials
|
|
auto nMats = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
std::vector<MaterialRecordCMO> materials;
|
|
materials.reserve(*nMats);
|
|
for (size_t j = 0; j < *nMats; ++j)
|
|
{
|
|
MaterialRecordCMO m;
|
|
|
|
// Material name
|
|
nName = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
auto matName = reinterpret_cast<const wchar_t*>(static_cast<const void*>(meshData + usedSize)); // [CodeQL.SM02986]: The cast here is intentional.
|
|
|
|
usedSize += sizeof(wchar_t)*(*nName);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("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::runtime_error("End of file");
|
|
|
|
m.pMaterial = matSetting;
|
|
|
|
// Pixel shader name
|
|
nName = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
auto psName = reinterpret_cast<const wchar_t*>(static_cast<const void*>(meshData + usedSize)); // [CodeQL.SM02986]: The cast here is intentional.
|
|
|
|
usedSize += sizeof(wchar_t)*(*nName);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
m.pixelShader.assign(psName, *nName);
|
|
|
|
for (size_t t = 0; t < VSD3DStarter::MAX_TEXTURE; ++t)
|
|
{
|
|
nName = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
auto txtName = reinterpret_cast<const wchar_t*>(static_cast<const void*>(meshData + usedSize)); // [CodeQL.SM02986]: The cast here is intentional.
|
|
|
|
usedSize += sizeof(wchar_t)*(*nName);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("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 uint8_t* bSkeleton = meshData + usedSize;
|
|
usedSize += sizeof(uint8_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
// Submeshes
|
|
auto nSubmesh = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (!*nSubmesh)
|
|
throw std::runtime_error("No submeshes found\n");
|
|
|
|
auto subMesh = reinterpret_cast<const VSD3DStarter::SubMesh*>(meshData + usedSize);
|
|
usedSize += sizeof(VSD3DStarter::SubMesh) * (*nSubmesh);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
// Index buffers
|
|
auto nIBs = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (!*nIBs)
|
|
throw std::runtime_error("No index buffers found\n");
|
|
|
|
struct IBData
|
|
{
|
|
size_t nIndices;
|
|
const uint16_t* ptr;
|
|
};
|
|
|
|
std::vector<IBData> ibData;
|
|
ibData.reserve(*nIBs);
|
|
|
|
std::vector<ComPtr<ID3D11Buffer>> ibs;
|
|
ibs.resize(*nIBs);
|
|
|
|
for (size_t j = 0; j < *nIBs; ++j)
|
|
{
|
|
auto nIndexes = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (!*nIndexes)
|
|
throw std::runtime_error("Empty index buffer found\n");
|
|
|
|
const uint64_t sizeInBytes = uint64_t(*(nIndexes)) * sizeof(uint16_t);
|
|
|
|
if (sizeInBytes > UINT32_MAX)
|
|
throw std::runtime_error("IB too large");
|
|
|
|
if (!(flags & ModelLoader_AllowLargeModels))
|
|
{
|
|
if (sizeInBytes > (D3D11_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u))
|
|
throw std::runtime_error("IB too large for DirectX 11");
|
|
}
|
|
|
|
auto const ibBytes = static_cast<size_t>(sizeInBytes);
|
|
|
|
auto indexes = reinterpret_cast<const uint16_t*>(meshData + usedSize);
|
|
usedSize += ibBytes;
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("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 = { 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<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (!*nVBs)
|
|
throw std::runtime_error("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 (size_t j = 0; j < *nVBs; ++j)
|
|
{
|
|
auto nVerts = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (!*nVerts)
|
|
throw std::runtime_error("Empty vertex buffer found\n");
|
|
|
|
const size_t vbBytes = sizeof(VertexPositionNormalTangentColorTexture) * (*(nVerts));
|
|
|
|
auto verts = reinterpret_cast<const VertexPositionNormalTangentColorTexture*>(meshData + usedSize);
|
|
usedSize += vbBytes;
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("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 uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (*nSkinVBs)
|
|
{
|
|
if (*nSkinVBs != *nVBs)
|
|
throw std::runtime_error("Number of VBs not equal to number of skin VBs");
|
|
|
|
for (size_t j = 0; j < *nSkinVBs; ++j)
|
|
{
|
|
auto nVerts = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (!*nVerts)
|
|
throw std::runtime_error("Empty skinning vertex buffer found\n");
|
|
|
|
if (vbData[j].nVerts != *nVerts)
|
|
throw std::runtime_error("Mismatched number of verts for skin VBs");
|
|
|
|
const size_t vbBytes = sizeof(VSD3DStarter::SkinningVertex) * (*(nVerts));
|
|
|
|
auto verts = reinterpret_cast<const VSD3DStarter::SkinningVertex*>(meshData + usedSize);
|
|
usedSize += vbBytes;
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("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::runtime_error("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;
|
|
|
|
const XMVECTOR min = XMVectorSet(extents->MinX, extents->MinY, extents->MinZ, 0.f);
|
|
const XMVECTOR max = XMVectorSet(extents->MaxX, extents->MaxY, extents->MaxZ, 0.f);
|
|
BoundingBox::CreateFromPoints(mesh->boundingBox, min, max);
|
|
|
|
// Load model bones (if present and requested)
|
|
if (*bSkeleton && (flags & ModelLoader_IncludeBones))
|
|
{
|
|
// Bones
|
|
auto nBones = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (!*nBones)
|
|
throw std::runtime_error("Animation bone data is missing\n");
|
|
|
|
ModelBone::Collection bones;
|
|
bones.resize(*nBones);
|
|
auto transforms = ModelBone::MakeArray(*nBones);
|
|
auto invTransforms = ModelBone::MakeArray(*nBones);
|
|
|
|
for (uint32_t j = 0; j < *nBones; ++j)
|
|
{
|
|
// Bone name
|
|
nName = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
auto boneName = reinterpret_cast<const wchar_t*>(static_cast<const void*>(meshData + usedSize)); // [CodeQL.SM02986]: The cast here is intentional.
|
|
|
|
usedSize += sizeof(wchar_t) * (*nName);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
bones[j].name = boneName;
|
|
|
|
// Bone settings
|
|
auto cmobones = reinterpret_cast<const VSD3DStarter::Bone*>(meshData + usedSize);
|
|
usedSize += sizeof(VSD3DStarter::Bone);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
transforms[j] = XMLoadFloat4x4(&cmobones->LocalTransform);
|
|
invTransforms[j] = XMLoadFloat4x4(&cmobones->InvBindPos);
|
|
|
|
if (cmobones->ParentIndex < 0)
|
|
{
|
|
if (!j)
|
|
continue;
|
|
|
|
// Add as a sibling of the root bone
|
|
uint32_t index = 0;
|
|
for (size_t visited = 0;; ++visited)
|
|
{
|
|
if (visited >= *nBones)
|
|
throw std::runtime_error("Skeleton bones form an invalid graph");
|
|
|
|
const uint32_t sibling = bones[index].siblingIndex;
|
|
if (sibling == ModelBone::c_Invalid)
|
|
{
|
|
bones[index].siblingIndex = j;
|
|
break;
|
|
}
|
|
|
|
if (sibling >= *nBones)
|
|
throw std::runtime_error("Skeleton bones corrupt");
|
|
|
|
index = sibling;
|
|
}
|
|
}
|
|
else if (static_cast<uint32_t>(cmobones->ParentIndex) >= *nBones)
|
|
{
|
|
throw std::runtime_error("Skeleton bones corrupt");
|
|
}
|
|
else
|
|
{
|
|
if (!j)
|
|
throw std::runtime_error("First bone must be root!");
|
|
|
|
auto index = static_cast<uint32_t>(cmobones->ParentIndex);
|
|
|
|
bones[j].parentIndex = index;
|
|
|
|
// Add as the only child of the parent
|
|
if (bones[index].childIndex == ModelBone::c_Invalid)
|
|
{
|
|
bones[index].childIndex = j;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise add as a sibling of the parent's other children
|
|
index = bones[index].childIndex;
|
|
for (size_t visited = 0;; ++visited)
|
|
{
|
|
if (visited >= *nBones)
|
|
throw std::runtime_error("Skeleton bones form an invalid graph");
|
|
|
|
const uint32_t sibling = bones[index].siblingIndex;
|
|
if (sibling == ModelBone::c_Invalid)
|
|
{
|
|
bones[index].siblingIndex = j;
|
|
break;
|
|
}
|
|
|
|
if (sibling >= *nBones)
|
|
throw std::runtime_error("Skeleton bones corrupt");
|
|
|
|
index = sibling;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::swap(model->bones, bones);
|
|
std::swap(model->boneMatrices, transforms);
|
|
std::swap(model->invBindPoseMatrices, invTransforms);
|
|
|
|
// Animation Clips
|
|
if (animsOffset)
|
|
{
|
|
// Optional return for offset to start of animation clips in the CMO.
|
|
|
|
size_t offset = usedSize;
|
|
|
|
auto nClips = reinterpret_cast<const uint32_t*>(meshData + usedSize);
|
|
usedSize += sizeof(uint32_t);
|
|
if (dataSize < usedSize)
|
|
throw std::runtime_error("End of file");
|
|
|
|
if (*nClips > 0)
|
|
{
|
|
*animsOffset = offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
const bool enableSkinning = (*nSkinVBs) != 0 && !(flags & ModelLoader_DisableSkinning);
|
|
|
|
// Build vertex buffers
|
|
std::vector<ComPtr<ID3D11Buffer>> vbs;
|
|
vbs.resize(*nVBs);
|
|
|
|
const size_t stride = enableSkinning ? sizeof(VertexPositionNormalTangentColorTextureSkinning)
|
|
: sizeof(VertexPositionNormalTangentColorTexture);
|
|
|
|
for (size_t j = 0; j < *nVBs; ++j)
|
|
{
|
|
const size_t nVerts = vbData[j].nVerts;
|
|
|
|
const uint64_t sizeInBytes = uint64_t(stride) * uint64_t(nVerts);
|
|
|
|
if (sizeInBytes > UINT32_MAX)
|
|
throw std::runtime_error("VB too large");
|
|
|
|
if (!(flags & ModelLoader_AllowLargeModels))
|
|
{
|
|
if (sizeInBytes > uint64_t(D3D11_REQ_RESOURCE_SIZE_IN_MEGABYTES_EXPRESSION_A_TERM * 1024u * 1024u))
|
|
throw std::runtime_error("VB too large for DirectX 11");
|
|
}
|
|
|
|
const 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 = { vbData[j].ptr, 0, 0 };
|
|
|
|
ThrowIfFailed(
|
|
device->CreateBuffer(&desc, &initData, &vbs[j])
|
|
);
|
|
}
|
|
else
|
|
{
|
|
auto temp = std::make_unique<uint8_t[]>(bytes + (sizeof(uint32_t) * nVerts));
|
|
|
|
auto visited = reinterpret_cast<uint32_t*>(temp.get() + bytes);
|
|
memset(visited, 0xff, sizeof(uint32_t) * 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 (size_t k = 0; k < *nSubmesh; ++k)
|
|
{
|
|
auto& sm = subMesh[k];
|
|
|
|
if (sm.VertexBufferIndex != j)
|
|
continue;
|
|
|
|
if ((sm.IndexBufferIndex >= *nIBs)
|
|
|| (sm.MaterialIndex >= materials.size()))
|
|
throw std::out_of_range("Invalid submesh found\n");
|
|
|
|
const XMMATRIX uvTransform = XMLoadFloat4x4(&materials[sm.MaterialIndex].pMaterial->UVTransform);
|
|
|
|
auto ib = ibData[sm.IndexBufferIndex].ptr;
|
|
|
|
const 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::out_of_range("Invalid index found\n");
|
|
|
|
auto verts = reinterpret_cast<VertexPositionNormalTangentColorTexture*>(temp.get() + (v * stride));
|
|
if (visited[v] == uint32_t(-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
|
|
const 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
|
|
const 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<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 = 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 (size_t j = 0; j < *nSubmesh; ++j)
|
|
{
|
|
auto& sm = subMesh[j];
|
|
|
|
if ((sm.IndexBufferIndex >= *nIBs)
|
|
|| (sm.VertexBufferIndex >= *nVBs)
|
|
|| (sm.MaterialIndex >= materials.size()))
|
|
throw std::out_of_range("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* device,
|
|
const wchar_t* szFileName,
|
|
IEffectFactory& fxFactory,
|
|
ModelLoaderFlags flags,
|
|
size_t* animsOffset)
|
|
{
|
|
if (animsOffset)
|
|
{
|
|
*animsOffset = 0;
|
|
}
|
|
|
|
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",
|
|
static_cast<unsigned int>(hr), szFileName);
|
|
throw std::runtime_error("CreateFromCMO");
|
|
}
|
|
|
|
auto model = CreateFromCMO(device, data.get(), dataSize, fxFactory, flags, animsOffset);
|
|
|
|
model->name = szFileName;
|
|
|
|
return model;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
// Adapters for /Zc:wchar_t- clients
|
|
|
|
#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED)
|
|
|
|
_Use_decl_annotations_
|
|
std::unique_ptr<Model> Model::CreateFromCMO(
|
|
ID3D11Device* device,
|
|
const __wchar_t* szFileName,
|
|
IEffectFactory& fxFactory,
|
|
ModelLoaderFlags flags,
|
|
size_t* animsOffset)
|
|
{
|
|
return Model::CreateFromCMO(device, reinterpret_cast<const unsigned short*>(szFileName), fxFactory, flags, animsOffset);
|
|
}
|
|
|
|
#endif
|