Xbox-ATG-Samples/PCSamples/IntroGraphics/SimpleBezierPC/SimpleBezierPC.cpp

608 строки
20 KiB
C++

//--------------------------------------------------------------------------------------
// SimpleBezier.cpp
//
// This sample demonstrates the basic usage of the DirectX 11 tessellation feature to
// render a simple cubic Bezier patch.
//
// Advanced Technology Group (ATG)
// Copyright (C) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#include "pch.h"
#include "SimpleBezierPC.h"
#include "DirectXHelpers.h"
#include "ATGColors.h"
#include "ControllerFont.h"
#include "ReadData.h"
#include "FindMedia.h"
extern void ExitSample() noexcept;
using namespace DirectX;
using Microsoft::WRL::ComPtr;
#pragma region Globals
// Global variables
namespace
{
// Help menu text
const wchar_t* c_sampleTitle = L"Simple Bezier Sample";
const wchar_t* c_sampleDescription = L"Demonstrates how to create hull and domain shaders to draw a\ntessellated Bezier surface representing a Mobius strip.";
const ATG::HelpButtonAssignment c_helpButtons[] = {
{ ATG::HelpID::MENU_BUTTON, L"Show/Hide Help" },
{ ATG::HelpID::VIEW_BUTTON, L"Exit" },
{ ATG::HelpID::LEFT_STICK, L"Rotate Camera" },
{ ATG::HelpID::LEFT_TRIGGER, L"Decrease Subdivisions" },
{ ATG::HelpID::RIGHT_TRIGGER, L"Increase Subdivisions" },
{ ATG::HelpID::Y_BUTTON, L"Toggle Wireframe" },
{ ATG::HelpID::A_BUTTON, L"Fractional Partitioning (Even)" },
{ ATG::HelpID::B_BUTTON, L"Fractional Partitioning (Odd)" },
{ ATG::HelpID::X_BUTTON, L"Integer Partitioning" },
};
// Min and max divisions of the patch per side for the slider control
const float c_minDivs = 4;
const float c_maxDivs = 16;
// Startup subdivisions per side
const float c_defaultSubdivs = 8.0f;
// Camera's rotation angle per step
const float c_rotationAnglePerStep = XM_2PI / 360.0f;
// Initial camera setup
const XMVECTOR c_cameraEye = XMVectorSet(0.0f, 0.45f, 2.7f, 0.0f);
const XMVECTOR c_cameraAt = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
const XMVECTOR c_cameraUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
// Draw the mesh with shaded triangles at start.
const bool c_defaultWireframeRendering = false;
// Simple Bezier patch for a Mobius strip.
// 4 patches with 16 control points each.
const XMFLOAT3 c_mobiusStrip[64] = {
{ 1.0f, -0.5f, 0.0f },
{ 1.0f, -0.5f, 0.5f },
{ 0.5f, -0.3536f, 1.354f },
{ 0.0f, -0.3536f, 1.354f },
{ 1.0f, -0.1667f, 0.0f },
{ 1.0f, -0.1667f, 0.5f },
{ 0.5f, -0.1179f, 1.118f },
{ 0.0f, -0.1179f, 1.118f },
{ 1.0f, 0.1667f, 0.0f },
{ 1.0f, 0.1667f, 0.5f },
{ 0.5f, 0.1179f, 0.8821f },
{ 0.0f, 0.1179f, 0.8821f },
{ 1.0f, 0.5f, 0.0f },
{ 1.0f, 0.5f, 0.5f },
{ 0.5f, 0.3536f, 0.6464f },
{ 0.0f, 0.3536f, 0.6464f },
{ 0.0f, -0.3536f, 1.354f },
{ -0.5f, -0.3536f, 1.354f },
{ -1.5f, 0.0f, 0.5f },
{ -1.5f, 0.0f, 0.0f },
{ 0.0f, -0.1179f, 1.118f },
{ -0.5f, -0.1179f, 1.118f },
{ -1.167f, 0.0f, 0.5f },
{ -1.167f, 0.0f, 0.0f },
{ 0.0f, 0.1179f, 0.8821f },
{ -0.5f, 0.1179f, 0.8821f },
{ -0.8333f, 0.0f, 0.5f },
{ -0.8333f, 0.0f, 0.0f },
{ 0.0f, 0.3536f, 0.6464f },
{ -0.5f, 0.3536f, 0.6464f },
{ -0.5f, 0.0f, 0.5f },
{ -0.5f, 0.0f, 0.0f },
{ -1.5f, 0.0f, 0.0f },
{ -1.5f, 0.0f, -0.5f },
{ -0.5f, 0.3536f, -1.354f },
{ 0.0f, 0.3536f, -1.354f },
{ -1.167f, 0.0f, 0.0f },
{ -1.167f, 0.0f, -0.5f },
{ -0.5f, 0.1179f, -1.118f },
{ 0.0f, 0.1179f, -1.118f },
{ -0.8333f, 0.0f, 0.0f },
{ -0.8333f, 0.0f, -0.5f },
{ -0.5f, -0.1179f, -0.8821f },
{ 0.0f, -0.1179f, -0.8821f },
{ -0.5f, 0.0f, 0.0f },
{ -0.5f, 0.0f, -0.5f },
{ -0.5f, -0.3536f, -0.6464f },
{ 0.0f, -0.3536f, -0.6464f },
{ 0.0f, 0.3536f, -1.354f },
{ 0.5f, 0.3536f, -1.354f },
{ 1.0f, 0.5f, -0.5f },
{ 1.0f, 0.5f, 0.0f },
{ 0.0f, 0.1179f, -1.118f },
{ 0.5f, 0.1179f, -1.118f },
{ 1.0f, 0.1667f, -0.5f },
{ 1.0f, 0.1667f, 0.0f },
{ 0.0f, -0.1179f, -0.8821f },
{ 0.5f, -0.1179f, -0.8821f },
{ 1.0f, -0.1667f, -0.5f },
{ 1.0f, -0.1667f, 0.0f },
{ 0.0f, -0.3536f, -0.6464f },
{ 0.5f, -0.3536f, -0.6464f },
{ 1.0f, -0.5f, -0.5f },
{ 1.0f, -0.5f, 0.0f },
};
}
#pragma endregion
Sample::Sample() noexcept(false)
: m_subdivs(c_defaultSubdivs)
, m_drawWires(c_defaultWireframeRendering)
, m_partitionMode(PartitionMode::PartitionInteger)
, m_showHelp(false)
, m_ctrlConnected(false)
{
// Use gamma-correct rendering. Hardware tessellation requires Feature Level 11.0 or later.
m_deviceResources = std::make_unique<DX::DeviceResources>(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB,
DXGI_FORMAT_D32_FLOAT, 2, D3D_FEATURE_LEVEL_11_0);
m_deviceResources->RegisterDeviceNotify(this);
m_help = std::make_unique<ATG::Help>(c_sampleTitle, c_sampleDescription, c_helpButtons, _countof(c_helpButtons), true);
}
// Initializes the Direct3D resources required to run.
void Sample::Initialize(HWND window, int width, int height)
{
m_gamePad = std::make_unique<GamePad>();
m_keyboard = std::make_unique<Keyboard>();
m_deviceResources->SetWindow(window, width, height);
m_deviceResources->CreateDeviceResources();
CreateDeviceDependentResources();
m_deviceResources->CreateWindowSizeDependentResources();
CreateWindowSizeDependentResources();
}
#pragma region Frame Update
// Executes basic render loop.
void Sample::Tick()
{
m_timer.Tick([&]()
{
Update(m_timer);
});
Render();
}
// Updates the world.
void Sample::Update(DX::StepTimer const&)
{
auto pad = m_gamePad->GetState(0);
if (pad.IsConnected())
{
m_ctrlConnected = true;
m_gamePadButtons.Update(pad);
}
else
{
m_ctrlConnected = false;
m_gamePadButtons.Reset();
}
auto kb = m_keyboard->GetState();
m_keyboardButtons.Update(kb);
if ((!m_showHelp && m_keyboardButtons.IsKeyPressed(Keyboard::Escape)) || pad.IsViewPressed())
{
ExitSample();
}
if (m_keyboardButtons.IsKeyPressed(Keyboard::F1) || m_gamePadButtons.menu == GamePad::ButtonStateTracker::PRESSED)
{
m_showHelp = !m_showHelp;
}
else if (m_showHelp && (m_keyboardButtons.IsKeyPressed(Keyboard::Escape) || m_gamePadButtons.b == GamePad::ButtonStateTracker::PRESSED))
{
m_showHelp = false;
}
else if (m_keyboardButtons.IsKeyPressed(Keyboard::W) || m_gamePadButtons.y == GamePad::ButtonStateTracker::PRESSED)
{
m_drawWires = !m_drawWires;
}
else if (m_keyboardButtons.IsKeyPressed(Keyboard::D1) || m_keyboardButtons.IsKeyPressed(Keyboard::NumPad1) ||
m_gamePadButtons.x == GamePad::ButtonStateTracker::PRESSED)
{
m_partitionMode = PartitionMode::PartitionInteger;
}
else if (m_keyboardButtons.IsKeyPressed(Keyboard::D2) || m_keyboardButtons.IsKeyPressed(Keyboard::NumPad2) ||
m_gamePadButtons.a == GamePad::ButtonStateTracker::PRESSED)
{
m_partitionMode = PartitionMode::PartitionFractionalEven;
}
else if (m_keyboardButtons.IsKeyPressed(Keyboard::D3) || m_keyboardButtons.IsKeyPressed(Keyboard::NumPad3) ||
(!m_showHelp && m_gamePadButtons.b == GamePad::ButtonStateTracker::PRESSED))
{
m_partitionMode = PartitionMode::PartitionFractionalOdd;
}
if (kb.Down || pad.IsLeftTriggerPressed())
{
m_subdivs = std::max(m_subdivs - 0.1f, c_minDivs);
}
if (kb.Up || pad.IsRightTriggerPressed())
{
m_subdivs = std::min(m_subdivs + 0.1f, c_maxDivs);
}
float rotationAxisY = 0.0f;
if (pad.thumbSticks.leftX != 0.0f)
{
rotationAxisY = -pad.thumbSticks.leftX * c_rotationAnglePerStep;
}
else if (kb.Left)
{
rotationAxisY = c_rotationAnglePerStep;
}
else if (kb.Right)
{
rotationAxisY = -c_rotationAnglePerStep;
}
if (rotationAxisY != 0.0f)
{
XMVECTOR eye = XMLoadFloat3(&m_cameraEye);
eye = XMVector3Transform(eye, XMMatrixRotationY(rotationAxisY));
XMMATRIX view = XMMatrixLookAtLH(eye, c_cameraAt, c_cameraUp);
XMStoreFloat4x4(&m_viewMatrix, view);
XMStoreFloat3(&m_cameraEye, eye);
}
}
#pragma endregion
#pragma region Frame Render
// Draws the scene.
void Sample::Render()
{
// Don't try to render anything before the first Update.
if (m_timer.GetFrameCount() == 0)
{
return;
}
Clear();
m_deviceResources->PIXBeginEvent(L"Render");
auto context = m_deviceResources->GetD3DDeviceContext();
if (m_showHelp)
{
//Clear hull and domain shaders so help can render.
context->HSSetShader(nullptr, nullptr, 0);
context->DSSetShader(nullptr, nullptr, 0);
m_help->Render();
}
else
{
//Reset state that may have been modified by showing help.
context->OMSetBlendState(m_states->Opaque(), Colors::Black, 0xFFFFFFFF);
context->OMSetDepthStencilState(m_states->DepthDefault(), 0);
XMMATRIX view = XMLoadFloat4x4(&m_viewMatrix);
XMMATRIX projection = XMLoadFloat4x4(&m_projectionMatrix);
XMMATRIX viewProjectionMatrix = XMMatrixMultiply(view, projection);
// Update per-frame variables.
auto d3dBuffer = m_cbPerFrame.Get();
{
MapGuard mappedResource(context, d3dBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0);
ConstantBuffer* data = reinterpret_cast<ConstantBuffer*>(mappedResource.get());
XMStoreFloat4x4(&data->viewProjectionMatrix, viewProjectionMatrix);
data->cameraWorldPos = m_cameraEye;
data->tessellationFactor = (float)m_subdivs;
}
// Render the meshes.
// Bind all of the CBs.
context->VSSetConstantBuffers(0, 1, &d3dBuffer);
context->HSSetConstantBuffers(0, 1, &d3dBuffer);
context->DSSetConstantBuffers(0, 1, &d3dBuffer);
context->PSSetConstantBuffers(0, 1, &d3dBuffer);
// Set the shaders.
context->VSSetShader(m_vertexShader.Get(), nullptr, 0);
// For this sample, choose either the "integer", "fractional_even",
// or "fractional_odd" hull shader.
if (m_partitionMode == PartitionMode::PartitionInteger)
{
context->HSSetShader(m_hullShaderInteger.Get(), nullptr, 0);
}
else if (m_partitionMode == PartitionMode::PartitionFractionalEven)
{
context->HSSetShader(m_hullShaderFracEven.Get(), nullptr, 0);
}
else if (m_partitionMode == PartitionMode::PartitionFractionalOdd)
{
context->HSSetShader(m_hullShaderFracOdd.Get(), nullptr, 0);
}
context->DSSetShader(m_domainShader.Get(), nullptr, 0);
context->GSSetShader(nullptr, nullptr, 0);
// Optionally draw the wireframe.
if (m_drawWires)
{
context->PSSetShader(m_solidColorPS.Get(), nullptr, 0);
context->RSSetState(m_states->Wireframe());
}
else
{
context->PSSetShader(m_pixelShader.Get(), nullptr, 0);
context->RSSetState(m_states->CullNone());
}
// Set the input assembler.
// This sample uses patches with 16 control points each.
// Although the Mobius strip only needs to use a vertex buffer,
// you can use an index buffer as well by calling IASetIndexBuffer().
context->IASetInputLayout(m_inputLayout.Get());
UINT stride = sizeof(XMFLOAT3);
UINT offset = 0;
context->IASetVertexBuffers(0, 1, m_controlPointVB.GetAddressOf(), &stride, &offset);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_16_CONTROL_POINT_PATCHLIST);
// Draw the mesh
context->Draw(_countof(c_mobiusStrip), 0);
// Draw the UI
context->HSSetShader(nullptr, nullptr, 0);
context->DSSetShader(nullptr, nullptr, 0);
auto size = m_deviceResources->GetOutputSize();
auto safe = SimpleMath::Viewport::ComputeTitleSafeArea(size.right, size.bottom);
m_batch->Begin();
wchar_t str[64] = {};
swprintf_s(str, L"Subdivisions: %.2f Partition Mode: %ls", m_subdivs,
m_partitionMode == PartitionMode::PartitionInteger ? L"Integer" :
(m_partitionMode == PartitionMode::PartitionFractionalEven ? L"Fractional Even" : L"Fractional Odd"));
m_smallFont->DrawString(m_batch.get(), str, XMFLOAT2(float(safe.left), float(safe.top)), ATG::Colors::LightGrey);
const wchar_t* legend = m_ctrlConnected ?
L"[LThumb] Rotate [RT][LT] Increase/decrease subdivisions\n[A][B][X] Change partition mode [Y] Toggle wireframe [View] Exit [Menu] Help"
: L"Left/Right - Rotate Up/Down - Increase/decrease subdivisions\n1/2/3 - Change partition mode W - Toggle wireframe Esc - Exit F1 - Help";
DX::DrawControllerString(m_batch.get(), m_smallFont.get(), m_ctrlFont.get(),
legend,
XMFLOAT2(float(safe.left), float(safe.bottom) - 2 * m_smallFont->GetLineSpacing()),
ATG::Colors::LightGrey);
m_batch->End();
}
m_deviceResources->PIXEndEvent();
// Show the new frame.
m_deviceResources->Present();
}
// Helper method to clear the back buffers.
void Sample::Clear()
{
m_deviceResources->PIXBeginEvent(L"Clear");
auto context = m_deviceResources->GetD3DDeviceContext();
// Clear the views.
auto renderTarget = m_deviceResources->GetRenderTargetView();
auto depthStencil = m_deviceResources->GetDepthStencilView();
context->ClearRenderTargetView(renderTarget, ATG::ColorsLinear::Background);
context->ClearDepthStencilView(depthStencil, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
context->OMSetRenderTargets(1, &renderTarget, depthStencil);
// Set the viewport.
auto viewport = m_deviceResources->GetScreenViewport();
context->RSSetViewports(1, &viewport);
m_deviceResources->PIXEndEvent();
}
#pragma endregion
#pragma region Message Handlers
// Message handlers
void Sample::OnActivated()
{
}
void Sample::OnDeactivated()
{
}
void Sample::OnSuspending()
{
}
void Sample::OnResuming()
{
m_timer.ResetElapsedTime();
m_gamePadButtons.Reset();
m_keyboardButtons.Reset();
}
void Sample::OnWindowMoved()
{
}
void Sample::OnWindowSizeChanged(int width, int height)
{
if (!m_deviceResources->WindowSizeChanged(width, height))
return;
CreateWindowSizeDependentResources();
}
// Properties
void Sample::GetDefaultSize(int& width, int& height) const
{
width = 1280;
height = 720;
}
#pragma endregion
#pragma region Direct3D Resources
// These are the resources that depend on the device.
void Sample::CreateDeviceDependentResources()
{
auto device = m_deviceResources->GetD3DDevice();
auto context = m_deviceResources->GetD3DDeviceContext();
m_states = std::make_unique<CommonStates>(device);
CreateShaders();
// Initialize the world and view matrices.
XMMATRIX world = XMMatrixIdentity();
XMMATRIX view = XMMatrixLookAtLH(c_cameraEye, c_cameraAt, c_cameraUp);
XMStoreFloat4x4(&m_worldMatrix, world);
XMStoreFloat4x4(&m_viewMatrix, view);
XMStoreFloat3(&m_cameraEye, c_cameraEye);
m_batch = std::make_unique<SpriteBatch>(context);
wchar_t strFilePath[MAX_PATH] = {};
DX::FindMediaFile(strFilePath, MAX_PATH, L"SegoeUI_18.spritefont");
m_smallFont = std::make_unique<SpriteFont>(device, strFilePath);
DX::FindMediaFile(strFilePath, MAX_PATH, L"XboxOneControllerLegendSmall.spritefont");
m_ctrlFont = std::make_unique<SpriteFont>(device, strFilePath);
m_help->RestoreDevice(context);
}
// Creates and initializes shaders and their data.
void Sample::CreateShaders()
{
auto device = m_deviceResources->GetD3DDevice();
// Load and create shaders.
auto vertexShaderBlob = DX::ReadData(L"BezierVS.cso");
DX::ThrowIfFailed(
device->CreateVertexShader(vertexShaderBlob.data(), vertexShaderBlob.size(),
nullptr, m_vertexShader.ReleaseAndGetAddressOf()));
auto hullShaderIntegerBlob = DX::ReadData(L"BezierHS_int.cso");
DX::ThrowIfFailed(
device->CreateHullShader(hullShaderIntegerBlob.data(), hullShaderIntegerBlob.size(),
nullptr, m_hullShaderInteger.ReleaseAndGetAddressOf()));
auto hullShaderFracEvenBlob = DX::ReadData(L"BezierHS_fracEven.cso");
DX::ThrowIfFailed(
device->CreateHullShader(hullShaderFracEvenBlob.data(), hullShaderFracEvenBlob.size(),
nullptr, m_hullShaderFracEven.ReleaseAndGetAddressOf()));
auto hullShaderFracOddBlob = DX::ReadData(L"BezierHS_fracOdd.cso");
DX::ThrowIfFailed(
device->CreateHullShader(hullShaderFracOddBlob.data(), hullShaderFracOddBlob.size(),
nullptr, m_hullShaderFracOdd.ReleaseAndGetAddressOf()));
auto domainShaderBlob = DX::ReadData(L"BezierDS.cso");
DX::ThrowIfFailed(
device->CreateDomainShader(domainShaderBlob.data(), domainShaderBlob.size(),
nullptr, m_domainShader.ReleaseAndGetAddressOf()));
auto pixelShaderBlob = DX::ReadData(L"BezierPS.cso");
DX::ThrowIfFailed(
device->CreatePixelShader(pixelShaderBlob.data(), pixelShaderBlob.size(),
nullptr, m_pixelShader.ReleaseAndGetAddressOf()));
auto solidColorPSBlob = DX::ReadData(L"SolidColorPS.cso");
DX::ThrowIfFailed(
device->CreatePixelShader(solidColorPSBlob.data(), solidColorPSBlob.size(),
nullptr, m_solidColorPS.ReleaseAndGetAddressOf()));
// Create our vertex input layout.
static const D3D11_INPUT_ELEMENT_DESC c_inputElementDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
DX::ThrowIfFailed(
device->CreateInputLayout(c_inputElementDesc, _countof(c_inputElementDesc),
vertexShaderBlob.data(), vertexShaderBlob.size(),
m_inputLayout.ReleaseAndGetAddressOf()));
// Create constant buffers.
D3D11_BUFFER_DESC bufferDesc;
bufferDesc.Usage = D3D11_USAGE_DYNAMIC;
bufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bufferDesc.MiscFlags = 0;
bufferDesc.ByteWidth = sizeof(ConstantBuffer);
DX::ThrowIfFailed(device->CreateBuffer(&bufferDesc, nullptr, m_cbPerFrame.ReleaseAndGetAddressOf()));
// Create vertex buffer.
D3D11_BUFFER_DESC vbDesc = {};
vbDesc.ByteWidth = sizeof(XMFLOAT3) * ARRAYSIZE(c_mobiusStrip);
vbDesc.Usage = D3D11_USAGE_DEFAULT;
vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA vbInitData = {};
vbInitData.pSysMem = c_mobiusStrip;
DX::ThrowIfFailed(device->CreateBuffer(&vbDesc, &vbInitData, m_controlPointVB.ReleaseAndGetAddressOf()));
}
// Allocate all memory resources that change on a window SizeChanged event.
void Sample::CreateWindowSizeDependentResources()
{
auto size = m_deviceResources->GetOutputSize();
XMMATRIX projection = XMMatrixPerspectiveFovLH(XM_PIDIV4, float(size.right) / float(size.bottom), 0.01f, 100.0f);
XMStoreFloat4x4(&m_projectionMatrix, projection);
m_batch->SetViewport(m_deviceResources->GetScreenViewport());
m_help->SetWindow(size);
}
void Sample::OnDeviceLost()
{
m_states.reset();
m_inputLayout.Reset();
m_vertexShader.Reset();
m_hullShaderInteger.Reset();
m_hullShaderFracEven.Reset();
m_hullShaderFracOdd.Reset();
m_domainShader.Reset();
m_pixelShader.Reset();
m_solidColorPS.Reset();
m_controlPointVB.Reset();
m_cbPerFrame.Reset();
m_rasterizerStateSolid.Reset();
m_rasterizerStateWireframe.Reset();
m_batch.reset();
m_smallFont.reset();
m_ctrlFont.reset();
m_help->ReleaseDevice();
}
void Sample::OnDeviceRestored()
{
CreateDeviceDependentResources();
CreateWindowSizeDependentResources();
}
#pragma endregion