DirectXTK12/Inc/Model.h

354 строки
16 KiB
C++

//--------------------------------------------------------------------------------------
// File: Model.h
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// http://go.microsoft.com/fwlink/?LinkID=615561
//--------------------------------------------------------------------------------------
#pragma once
#if defined(_XBOX_ONE) && defined(_TITLE)
#include <d3d12_x.h>
#else
#include <d3d12.h>
#include <dxgiformat.h>
#endif
#include <DirectXMath.h>
#include <DirectXCollision.h>
#include <cstdint>
#include <functional>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <assert.h>
#include <wrl/client.h>
#include "GraphicsMemory.h"
#include "Effects.h"
namespace DirectX
{
class IEffect;
class IEffectFactory;
class ModelMesh;
//----------------------------------------------------------------------------------
// Model loading options
enum ModelLoaderFlags : uint32_t
{
ModelLoader_Default = 0x0,
ModelLoader_MaterialColorsSRGB = 0x1,
ModelLoader_AllowLargeModels = 0x2,
};
inline ModelLoaderFlags operator|(ModelLoaderFlags a, ModelLoaderFlags b) noexcept { return static_cast<ModelLoaderFlags>(static_cast<int>(a) | static_cast<int>(b)); }
//----------------------------------------------------------------------------------
// Each mesh part is a submesh with a single effect
class ModelMeshPart
{
public:
ModelMeshPart(uint32_t partIndex) noexcept;
ModelMeshPart(ModelMeshPart&&) = default;
ModelMeshPart& operator= (ModelMeshPart&&) = default;
ModelMeshPart(ModelMeshPart const&) = default;
ModelMeshPart& operator= (ModelMeshPart const&) = default;
virtual ~ModelMeshPart();
uint32_t partIndex; // Unique index assigned per-part in a model; used to index effects.
uint32_t materialIndex; // Index of the material spec to use
uint32_t indexCount;
uint32_t startIndex;
int32_t vertexOffset;
uint32_t vertexStride;
uint32_t vertexCount;
uint32_t indexBufferSize;
uint32_t vertexBufferSize;
D3D_PRIMITIVE_TOPOLOGY primitiveType;
DXGI_FORMAT indexFormat;
SharedGraphicsResource indexBuffer;
SharedGraphicsResource vertexBuffer;
Microsoft::WRL::ComPtr<ID3D12Resource> staticIndexBuffer;
Microsoft::WRL::ComPtr<ID3D12Resource> staticVertexBuffer;
std::shared_ptr<std::vector<D3D12_INPUT_ELEMENT_DESC>> vbDecl;
using Collection = std::vector<std::unique_ptr<ModelMeshPart>>;
using DrawCallback = std::function<void(_In_ ID3D12GraphicsCommandList* commandList, _In_ const ModelMeshPart& part)>;
// Draw mesh part
void __cdecl Draw(_In_ ID3D12GraphicsCommandList* commandList) const;
void __cdecl DrawInstanced(_In_ ID3D12GraphicsCommandList* commandList, uint32_t instanceCount, uint32_t startInstanceLocation = 0) const;
//
// Utilities for drawing multiple mesh parts
//
// Draw the mesh
static void __cdecl DrawMeshParts(_In_ ID3D12GraphicsCommandList* commandList, _In_ const ModelMeshPart::Collection& meshParts);
// Draw the mesh with an effect
static void __cdecl DrawMeshParts(_In_ ID3D12GraphicsCommandList* commandList, _In_ const ModelMeshPart::Collection& meshParts, _In_ IEffect* effect);
// Draw the mesh with a callback for each mesh part
static void __cdecl DrawMeshParts(_In_ ID3D12GraphicsCommandList* commandList, _In_ const ModelMeshPart::Collection& meshParts, DrawCallback callback);
// Draw the mesh with a range of effects that mesh parts will index into.
// Effects can be any IEffect pointer type (including smart pointer). Value or reference types will not compile.
// The iterator passed to this method should have random access capabilities for best performance.
template<typename TEffectIterator, typename TEffectIteratorCategory = typename TEffectIterator::iterator_category>
static void DrawMeshParts(
_In_ ID3D12GraphicsCommandList* commandList,
_In_ const ModelMeshPart::Collection& meshParts,
TEffectIterator partEffects)
{
// This assert is here to prevent accidental use of containers that would cause undesirable performance penalties.
static_assert(
std::is_base_of<std::random_access_iterator_tag, TEffectIteratorCategory>::value,
"Providing an iterator without random access capabilities -- such as from std::list -- is not supported.");
for (auto it = std::begin(meshParts); it != std::end(meshParts); ++it)
{
auto part = it->get();
assert(part != nullptr);
// Get the effect at the location specified by the part's material
TEffectIterator effect_iterator = partEffects;
std::advance(effect_iterator, part->partIndex);
// Apply the effect and draw
(*effect_iterator)->Apply(commandList);
part->Draw(commandList);
}
}
};
//----------------------------------------------------------------------------------
// A mesh consists of one or more model mesh parts
class ModelMesh
{
public:
ModelMesh() noexcept;
ModelMesh(ModelMesh&&) = default;
ModelMesh& operator= (ModelMesh&&) = default;
ModelMesh(ModelMesh const&) = default;
ModelMesh& operator= (ModelMesh const&) = default;
virtual ~ModelMesh();
BoundingSphere boundingSphere;
BoundingBox boundingBox;
ModelMeshPart::Collection opaqueMeshParts;
ModelMeshPart::Collection alphaMeshParts;
std::wstring name;
using Collection = std::vector<std::shared_ptr<ModelMesh>>;
// Draw the mesh
void __cdecl DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList) const;
void __cdecl DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList) const;
// Draw the mesh with an effect
void __cdecl DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, _In_ IEffect* effect) const;
void __cdecl DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, _In_ IEffect* effect) const;
// Draw the mesh with a callback for each mesh part
void __cdecl DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, ModelMeshPart::DrawCallback callback) const;
void __cdecl DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, ModelMeshPart::DrawCallback callback) const;
// Draw the mesh with a range of effects that mesh parts will index into.
// TEffectPtr can be any IEffect pointer type (including smart pointer). Value or reference types will not compile.
template<typename TEffectIterator, typename TEffectIteratorCategory = typename TEffectIterator::iterator_category>
void DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, TEffectIterator effects) const
{
ModelMeshPart::DrawMeshParts<TEffectIterator, TEffectIteratorCategory>(commandList, opaqueMeshParts, effects);
}
template<typename TEffectIterator, typename TEffectIteratorCategory = typename TEffectIterator::iterator_category>
void DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, TEffectIterator effects) const
{
ModelMeshPart::DrawMeshParts<TEffectIterator, TEffectIteratorCategory>(commandList, alphaMeshParts, effects);
}
};
//----------------------------------------------------------------------------------
// A model consists of one or more meshes
class Model
{
public:
Model() noexcept;
Model(Model&&) = default;
Model& operator= (Model&&) = default;
Model(Model const&) = default;
Model& operator= (Model const&) = default;
virtual ~Model();
using ModelMaterialInfo = IEffectFactory::EffectInfo;
using ModelMaterialInfoCollection = std::vector<ModelMaterialInfo>;
using TextureCollection = std::vector<std::wstring>;
//
// NOTE
//
// The Model::Draw functions use variadic templates and perfect-forwarding in order to support future overloads to the ModelMesh::Draw
// family of functions. This means that a new ModelMesh::Draw overload can be added, removed or altered, but the Model::Draw* routines
// will still remain compatible. The correct ModelMesh::Draw overload will be selected by the compiler depending on the arguments you
// provide to Model::Draw*.
//
// Draw all the opaque meshes in the model
template<typename... TForwardArgs> void DrawOpaque(_In_ ID3D12GraphicsCommandList* commandList, TForwardArgs&&... args) const
{
// Draw opaque parts
for (auto it = std::begin(meshes); it != std::end(meshes); ++it)
{
auto mesh = it->get();
assert(mesh != nullptr);
mesh->DrawOpaque(commandList, std::forward<TForwardArgs>(args)...);
}
}
// Draw all the alpha meshes in the model
template<typename... TForwardArgs> void DrawAlpha(_In_ ID3D12GraphicsCommandList* commandList, TForwardArgs&&... args) const
{
// Draw opaque parts
for (auto it = std::begin(meshes); it != std::end(meshes); ++it)
{
auto mesh = it->get();
assert(mesh != nullptr);
mesh->DrawAlpha(commandList, std::forward<TForwardArgs>(args)...);
}
}
// Draw all the meshes in the model
template<typename... TForwardArgs> void Draw(_In_ ID3D12GraphicsCommandList* commandList, TForwardArgs&&... args) const
{
DrawOpaque(commandList, std::forward<TForwardArgs>(args)...);
DrawAlpha(commandList, std::forward<TForwardArgs>(args)...);
}
// Load texture resources into an existing Effect Texture Factory
int __cdecl LoadTextures(IEffectTextureFactory& texFactory, int destinationDescriptorOffset = 0) const;
// Load texture resources into a new Effect Texture Factory
std::unique_ptr<EffectTextureFactory> __cdecl LoadTextures(
_In_ ID3D12Device* device,
ResourceUploadBatch& resourceUploadBatch,
_In_opt_z_ const wchar_t* texturesPath = nullptr,
D3D12_DESCRIPTOR_HEAP_FLAGS flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE) const;
// Load VB/IB resources for static geometry
void __cdecl LoadStaticBuffers(
_In_ ID3D12Device* device,
ResourceUploadBatch& resourceUploadBatch,
bool keepMemory = false);
// Create effects using the default effect factory
std::vector<std::shared_ptr<IEffect>> __cdecl CreateEffects(
const EffectPipelineStateDescription& opaquePipelineState,
const EffectPipelineStateDescription& alphaPipelineState,
_In_ ID3D12DescriptorHeap* textureDescriptorHeap,
_In_ ID3D12DescriptorHeap* samplerDescriptorHeap,
int textureDescriptorOffset = 0,
int samplerDescriptorOffset = 0) const;
// Create effects using a custom effect factory
std::vector<std::shared_ptr<IEffect>> __cdecl CreateEffects(
IEffectFactory& fxFactory,
const EffectPipelineStateDescription& opaquePipelineState,
const EffectPipelineStateDescription& alphaPipelineState,
int textureDescriptorOffset = 0,
int samplerDescriptorOffset = 0) const;
// Loads a model from a DirectX SDK .SDKMESH file
static std::unique_ptr<Model> __cdecl CreateFromSDKMESH(
_In_opt_ ID3D12Device* device,
_In_reads_bytes_(dataSize) const uint8_t* meshData, _In_ size_t dataSize,
ModelLoaderFlags flags = ModelLoader_Default);
static std::unique_ptr<Model> __cdecl CreateFromSDKMESH(
_In_opt_ ID3D12Device* device,
_In_z_ const wchar_t* szFileName,
ModelLoaderFlags flags = ModelLoader_Default);
// Loads a model from a .VBO file
static std::unique_ptr<Model> __cdecl CreateFromVBO(
_In_opt_ ID3D12Device* device,
_In_reads_bytes_(dataSize) const uint8_t* meshData, _In_ size_t dataSize,
ModelLoaderFlags flags = ModelLoader_Default);
static std::unique_ptr<Model> __cdecl CreateFromVBO(
_In_opt_ ID3D12Device* device,
_In_z_ const wchar_t* szFileName,
ModelLoaderFlags flags = ModelLoader_Default);
// Utility function for getting a GPU descriptor for a mesh part/material index. If there is no texture the
// descriptor will be zero.
D3D12_GPU_DESCRIPTOR_HANDLE __cdecl GetGpuTextureHandleForMaterialIndex(uint32_t materialIndex, _In_ ID3D12DescriptorHeap* heap, _In_ size_t descriptorSize, _In_ size_t descriptorOffset) const
{
D3D12_GPU_DESCRIPTOR_HANDLE handle = {};
if (materialIndex >= materials.size())
return handle;
int textureIndex = materials[materialIndex].diffuseTextureIndex;
if (textureIndex == -1)
return handle;
handle = heap->GetGPUDescriptorHandleForHeapStart();
handle.ptr += static_cast<UINT64>(descriptorSize * (UINT64(textureIndex) + UINT64(descriptorOffset)));
return handle;
}
// Utility function for updating the matrices in a list of effects. This will SetWorld, SetView and SetProjection
// on any effect in the list that derives from IEffectMatrices.
static void XM_CALLCONV UpdateEffectMatrices(
_In_ std::vector<std::shared_ptr<IEffect>>& effectList,
DirectX::FXMMATRIX world,
DirectX::CXMMATRIX view,
DirectX::CXMMATRIX proj);
// Utility function to transition VB/IB resources for static geometry.
void __cdecl Transition(
_In_ ID3D12GraphicsCommandList* commandList,
D3D12_RESOURCE_STATES stateBeforeVB,
D3D12_RESOURCE_STATES stateAfterVB,
D3D12_RESOURCE_STATES stateBeforeIB,
D3D12_RESOURCE_STATES stateAfterIB);
ModelMesh::Collection meshes;
ModelMaterialInfoCollection materials;
TextureCollection textureNames;
std::wstring name;
private:
std::shared_ptr<IEffect> __cdecl CreateEffectForMeshPart(
IEffectFactory& fxFactory,
const EffectPipelineStateDescription& opaquePipelineState,
const EffectPipelineStateDescription& alphaPipelineState,
int textureDescriptorOffset,
int samplerDescriptorOffset,
_In_ const ModelMeshPart* part) const;
};
}