//-------------------------------------------------------------------------------------- // File: Model.cpp // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // // 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() : 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 != 0 ); 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::CreateInputLayout( ID3D11Device* d3dDevice, IEffect* ieffect, ID3D11InputLayout** iinputLayout ) { if ( !vbDecl || vbDecl->empty() ) throw std::exception("Model mesh part missing vertex buffer input elements data"); void const* shaderByteCode; size_t byteCodeLength; assert( ieffect != 0 ); ieffect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength); assert( d3dDevice != 0 ); ThrowIfFailed( d3dDevice->CreateInputLayout(vbDecl->data(), static_cast( vbDecl->size() ), shaderByteCode, byteCodeLength, iinputLayout ) ); } _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"); assert( ieffect != 0 ); this->effect = ieffect; this->isAlpha = isalpha; void const* shaderByteCode; size_t byteCodeLength; effect->GetVertexShaderBytecode(&shaderByteCode, &byteCodeLength); assert( d3dDevice != 0 ); ThrowIfFailed( d3dDevice->CreateInputLayout(vbDecl->data(), static_cast( vbDecl->size() ), shaderByteCode, byteCodeLength, &inputLayout ) ); } //-------------------------------------------------------------------------------------- // ModelMesh //-------------------------------------------------------------------------------------- ModelMesh::ModelMesh() : ccw(true), pmalpha(true) { } ModelMesh::~ModelMesh() { } _Use_decl_annotations_ void ModelMesh::PrepareForRendering( ID3D11DeviceContext* deviceContext, CommonStates& states, bool alpha, bool wireframe ) const { assert( deviceContext != 0 ); // 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 != 0 ); for ( auto it = meshParts.cbegin(); it != meshParts.cend(); ++it ) { auto part = (*it).get(); assert( part != 0 ); 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->SetWorld( world ); imatrices->SetView( view ); imatrices->SetProjection( projection ); } part->Draw( deviceContext, part->effect.get(), part->inputLayout.Get(), setCustomState ); } } //-------------------------------------------------------------------------------------- // Model //-------------------------------------------------------------------------------------- Model::~Model() { } _Use_decl_annotations_ void XM_CALLCONV Model::Draw( ID3D11DeviceContext* deviceContext, CommonStates& states, FXMMATRIX world, CXMMATRIX view, CXMMATRIX projection, bool wireframe, std::function setCustomState ) const { assert( deviceContext != 0 ); // Draw opaque parts for( auto it = meshes.cbegin(); it != meshes.cend(); ++it ) { auto mesh = it->get(); assert( mesh != 0 ); 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 != 0 ); 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 != 0 ); for ( auto it = mesh->meshParts.cbegin(); it != mesh->meshParts.cend(); ++it ) { if ( (*it)->effect != 0 ) mEffectCache.insert( (*it)->effect.get() ); } } } assert( setEffect != 0 ); for( auto it = mEffectCache.begin(); it != mEffectCache.end(); ++it ) { setEffect( *it ); } }