//-------------------------------------------------------------------------------------- // File: Model.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 "CommonStates.h" #include "DirectXHelpers.h" #include "Effects.h" #include "PlatformHelpers.h" using namespace DirectX; #ifndef _CPPRTTI #error Model requires RTTI #endif //-------------------------------------------------------------------------------------- // ModelMeshPart //-------------------------------------------------------------------------------------- ModelMeshPart::ModelMeshPart() noexcept : indexCount(0), startIndex(0), vertexOffset(0), vertexStride(0), primitiveType(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST), indexFormat(DXGI_FORMAT_R16_UINT), isAlpha(false) { } ModelMeshPart::~ModelMeshPart() { } _Use_decl_annotations_ void ModelMeshPart::Draw( ID3D11DeviceContext* deviceContext, IEffect* ieffect, ID3D11InputLayout* iinputLayout, std::function setCustomState) const { deviceContext->IASetInputLayout(iinputLayout); auto vb = vertexBuffer.Get(); UINT vbStride = vertexStride; UINT vbOffset = 0; deviceContext->IASetVertexBuffers(0, 1, &vb, &vbStride, &vbOffset); // Note that if indexFormat is DXGI_FORMAT_R32_UINT, this model mesh part requires a Feature Level 9.2 or greater device deviceContext->IASetIndexBuffer(indexBuffer.Get(), indexFormat, 0); assert(ieffect != nullptr); ieffect->Apply(deviceContext); // Hook lets the caller replace our shaders or state settings with whatever else they see fit. if (setCustomState) { setCustomState(); } // Draw the primitive. deviceContext->IASetPrimitiveTopology(primitiveType); deviceContext->DrawIndexed(indexCount, startIndex, vertexOffset); } _Use_decl_annotations_ void ModelMeshPart::DrawInstanced( ID3D11DeviceContext* deviceContext, IEffect* ieffect, ID3D11InputLayout* iinputLayout, uint32_t instanceCount, uint32_t startInstanceLocation, std::function setCustomState) const { deviceContext->IASetInputLayout(iinputLayout); auto vb = vertexBuffer.Get(); UINT vbStride = vertexStride; UINT vbOffset = 0; deviceContext->IASetVertexBuffers(0, 1, &vb, &vbStride, &vbOffset); // Note that if indexFormat is DXGI_FORMAT_R32_UINT, this model mesh part requires a Feature Level 9.2 or greater device deviceContext->IASetIndexBuffer(indexBuffer.Get(), indexFormat, 0); assert(ieffect != nullptr); ieffect->Apply(deviceContext); // Hook lets the caller replace our shaders or state settings with whatever else they see fit. if (setCustomState) { setCustomState(); } // Draw the primitive. deviceContext->IASetPrimitiveTopology(primitiveType); deviceContext->DrawIndexedInstanced( indexCount, instanceCount, startIndex, vertexOffset, startInstanceLocation); } _Use_decl_annotations_ void ModelMeshPart::CreateInputLayout(ID3D11Device* d3dDevice, IEffect* ieffect, ID3D11InputLayout** iinputLayout) const { if (iinputLayout) { *iinputLayout = nullptr; } if (!vbDecl || vbDecl->empty()) throw std::exception("Model mesh part missing vertex buffer input elements data"); if (vbDecl->size() > D3D11_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT) throw std::exception("Model mesh part input layout size is too large for DirectX 11"); ThrowIfFailed( CreateInputLayoutFromEffect(d3dDevice, ieffect, vbDecl->data(), vbDecl->size(), iinputLayout) ); assert(iinputLayout != nullptr && *iinputLayout != nullptr); _Analysis_assume_(iinputLayout != nullptr && *iinputLayout != nullptr); } _Use_decl_annotations_ void ModelMeshPart::ModifyEffect(ID3D11Device* d3dDevice, std::shared_ptr& ieffect, bool isalpha) { if (!vbDecl || vbDecl->empty()) throw std::exception("Model mesh part missing vertex buffer input elements data"); if (vbDecl->size() > D3D11_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT) throw std::exception("Model mesh part input layout size is too large for DirectX 11"); assert(ieffect != nullptr); this->effect = ieffect; this->isAlpha = isalpha; assert(d3dDevice != nullptr); ThrowIfFailed( CreateInputLayoutFromEffect(d3dDevice, effect.get(), vbDecl->data(), vbDecl->size(), inputLayout.ReleaseAndGetAddressOf()) ); } //-------------------------------------------------------------------------------------- // ModelMesh //-------------------------------------------------------------------------------------- ModelMesh::ModelMesh() noexcept : ccw(true), pmalpha(true) { } ModelMesh::~ModelMesh() { } _Use_decl_annotations_ void ModelMesh::PrepareForRendering( ID3D11DeviceContext* deviceContext, const CommonStates& states, bool alpha, bool wireframe) const { assert(deviceContext != nullptr); // Set the blend and depth stencil state. ID3D11BlendState* blendState; ID3D11DepthStencilState* depthStencilState; if (alpha) { if (pmalpha) { blendState = states.AlphaBlend(); depthStencilState = states.DepthRead(); } else { blendState = states.NonPremultiplied(); depthStencilState = states.DepthRead(); } } else { blendState = states.Opaque(); depthStencilState = states.DepthDefault(); } deviceContext->OMSetBlendState(blendState, nullptr, 0xFFFFFFFF); deviceContext->OMSetDepthStencilState(depthStencilState, 0); // Set the rasterizer state. if (wireframe) deviceContext->RSSetState(states.Wireframe()); else deviceContext->RSSetState(ccw ? states.CullCounterClockwise() : states.CullClockwise()); // Set sampler state. ID3D11SamplerState* samplers[] = { states.LinearWrap(), states.LinearWrap(), }; deviceContext->PSSetSamplers(0, 2, samplers); } _Use_decl_annotations_ void XM_CALLCONV ModelMesh::Draw( ID3D11DeviceContext* deviceContext, FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection, bool alpha, std::function setCustomState) const { assert(deviceContext != nullptr); for (auto it = meshParts.cbegin(); it != meshParts.cend(); ++it) { auto part = (*it).get(); assert(part != nullptr); if (part->isAlpha != alpha) { // Skip alpha parts when drawing opaque or skip opaque parts if drawing alpha continue; } auto imatrices = dynamic_cast(part->effect.get()); if (imatrices) { imatrices->SetMatrices(world, view, projection); } part->Draw(deviceContext, part->effect.get(), part->inputLayout.Get(), setCustomState); } } //-------------------------------------------------------------------------------------- // Model //-------------------------------------------------------------------------------------- Model::~Model() { } _Use_decl_annotations_ void XM_CALLCONV Model::Draw( ID3D11DeviceContext* deviceContext, const CommonStates& states, FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection, bool wireframe, std::function setCustomState) const { assert(deviceContext != nullptr); // Draw opaque parts for (auto it = meshes.cbegin(); it != meshes.cend(); ++it) { auto mesh = it->get(); assert(mesh != nullptr); mesh->PrepareForRendering(deviceContext, states, false, wireframe); mesh->Draw(deviceContext, world, view, projection, false, setCustomState); } // Draw alpha parts for (auto it = meshes.cbegin(); it != meshes.cend(); ++it) { auto mesh = it->get(); assert(mesh != nullptr); mesh->PrepareForRendering(deviceContext, states, true, wireframe); mesh->Draw(deviceContext, world, view, projection, true, setCustomState); } } void Model::UpdateEffects(_In_ std::function setEffect) { if (mEffectCache.empty()) { // This cache ensures we only set each effect once (could be shared) for (auto mit = meshes.cbegin(); mit != meshes.cend(); ++mit) { auto mesh = mit->get(); assert(mesh != nullptr); for (auto it = mesh->meshParts.cbegin(); it != mesh->meshParts.cend(); ++it) { if ((*it)->effect) mEffectCache.insert((*it)->effect.get()); } } } assert(setEffect != nullptr); for (auto it = mEffectCache.begin(); it != mEffectCache.end(); ++it) { setEffect(*it); } }