diff options
Diffstat (limited to 'support/hololens/ServoApp/Content/SpinningCubeRenderer.cpp')
-rw-r--r-- | support/hololens/ServoApp/Content/SpinningCubeRenderer.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/support/hololens/ServoApp/Content/SpinningCubeRenderer.cpp b/support/hololens/ServoApp/Content/SpinningCubeRenderer.cpp new file mode 100644 index 00000000000..c76119bccf7 --- /dev/null +++ b/support/hololens/ServoApp/Content/SpinningCubeRenderer.cpp @@ -0,0 +1,266 @@ +#include "pch.h" +#include "SpinningCubeRenderer.h" +#include "Common/DirectXHelper.h" + +using namespace Immersive; +using namespace DirectX; +using namespace winrt::Windows::Foundation::Numerics; +using namespace winrt::Windows::UI::Input::Spatial; + +// Loads vertex and pixel shaders from files and instantiates the cube geometry. +SpinningCubeRenderer::SpinningCubeRenderer( + std::shared_ptr<DX::DeviceResources> const &deviceResources) + : m_deviceResources(deviceResources) { + CreateDeviceDependentResources(); +} + +// This function uses a SpatialPointerPose to position the world-locked hologram +// two meters in front of the user's heading. +void SpinningCubeRenderer::PositionHologram( + SpatialPointerPose const &pointerPose) { + if (pointerPose != nullptr) { + // Get the gaze direction relative to the given coordinate system. + const float3 headPosition = pointerPose.Head().Position(); + const float3 headDirection = pointerPose.Head().ForwardDirection(); + + // The hologram is positioned two meters along the user's gaze direction. + constexpr float distanceFromUser = 2.0f; // meters + const float3 gazeAtTwoMeters = + headPosition + (distanceFromUser * headDirection); + + // This will be used as the translation component of the hologram's + // model transform. + SetPosition(gazeAtTwoMeters); + } +} + +// Called once per frame. Rotates the cube, and calculates and sets the model +// matrix relative to the position transform indicated by +// hologramPositionTransform. +void SpinningCubeRenderer::Update(DX::StepTimer const &timer) { + // Rotate the cube. + // Convert degrees to radians, then convert seconds to rotation angle. + const float radiansPerSecond = XMConvertToRadians(m_degreesPerSecond); + const double totalRotation = timer.GetTotalSeconds() * radiansPerSecond; + const float radians = static_cast<float>(fmod(totalRotation, XM_2PI)); + const XMMATRIX modelRotation = XMMatrixRotationY(-radians); + + // Position the cube. + const XMMATRIX modelTranslation = + XMMatrixTranslationFromVector(XMLoadFloat3(&m_position)); + + // Multiply to get the transform matrix. + // Note that this transform does not enforce a particular coordinate system. + // The calling class is responsible for rendering this content in a consistent + // manner. + const XMMATRIX modelTransform = + XMMatrixMultiply(modelRotation, modelTranslation); + + // The view and projection matrices are provided by the system; they are + // associated with holographic cameras, and updated on a per-camera basis. + // Here, we provide the model transform for the sample hologram. The model + // transform matrix is transposed to prepare it for the shader. + XMStoreFloat4x4(&m_modelConstantBufferData.model, + XMMatrixTranspose(modelTransform)); + + // Loading is asynchronous. Resources must be created before they can be + // updated. + if (!m_loadingComplete) { + return; + } + + // Use the D3D device context to update Direct3D device-based resources. + const auto context = m_deviceResources->GetD3DDeviceContext(); + + // Update the model transform buffer for the hologram. + context->UpdateSubresource(m_modelConstantBuffer.Get(), 0, nullptr, + &m_modelConstantBufferData, 0, 0); +} + +// Renders one frame using the vertex and pixel shaders. +// On devices that do not support the D3D11_FEATURE_D3D11_OPTIONS3:: +// VPAndRTArrayIndexFromAnyShaderFeedingRasterizer optional feature, +// a pass-through geometry shader is also used to set the render +// target array index. +void SpinningCubeRenderer::Render() { + // Loading is asynchronous. Resources must be created before drawing can + // occur. + if (!m_loadingComplete) { + return; + } + + const auto context = m_deviceResources->GetD3DDeviceContext(); + + // Each vertex is one instance of the VertexPositionColor struct. + const UINT stride = sizeof(VertexPositionColor); + const UINT offset = 0; + context->IASetVertexBuffers(0, 1, m_vertexBuffer.GetAddressOf(), &stride, + &offset); + context->IASetIndexBuffer(m_indexBuffer.Get(), + DXGI_FORMAT_R16_UINT, // Each index is one 16-bit + // unsigned integer (short). + 0); + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + context->IASetInputLayout(m_inputLayout.Get()); + + // Attach the vertex shader. + context->VSSetShader(m_vertexShader.Get(), nullptr, 0); + // Apply the model constant buffer to the vertex shader. + context->VSSetConstantBuffers(0, 1, m_modelConstantBuffer.GetAddressOf()); + + if (!m_usingVprtShaders) { + // On devices that do not support the D3D11_FEATURE_D3D11_OPTIONS3:: + // VPAndRTArrayIndexFromAnyShaderFeedingRasterizer optional feature, + // a pass-through geometry shader is used to set the render target + // array index. + context->GSSetShader(m_geometryShader.Get(), nullptr, 0); + } + + // Attach the pixel shader. + context->PSSetShader(m_pixelShader.Get(), nullptr, 0); + + // Draw the objects. + context->DrawIndexedInstanced(m_indexCount, // Index count per instance. + 2, // Instance count. + 0, // Start index location. + 0, // Base vertex location. + 0 // Start instance location. + ); +} + +std::future<void> SpinningCubeRenderer::CreateDeviceDependentResources() { + m_usingVprtShaders = m_deviceResources->GetDeviceSupportsVprt(); + + // On devices that do support the D3D11_FEATURE_D3D11_OPTIONS3:: + // VPAndRTArrayIndexFromAnyShaderFeedingRasterizer optional feature + // we can avoid using a pass-through geometry shader to set the render + // target array index, thus avoiding any overhead that would be + // incurred by setting the geometry shader stage. + std::wstring vertexShaderFileName = m_usingVprtShaders + ? L"ms-appx:///VprtVertexShader.cso" + : L"ms-appx:///VertexShader.cso"; + + // Shaders will be loaded asynchronously. + + // After the vertex shader file is loaded, create the shader and input layout. + std::vector<byte> vertexShaderFileData = + co_await DX::ReadDataAsync(vertexShaderFileName); + winrt::check_hresult(m_deviceResources->GetD3DDevice()->CreateVertexShader( + vertexShaderFileData.data(), vertexShaderFileData.size(), nullptr, + &m_vertexShader)); + + constexpr std::array<D3D11_INPUT_ELEMENT_DESC, 2> vertexDesc = {{ + {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, + D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, + D3D11_INPUT_PER_VERTEX_DATA, 0}, + }}; + + winrt::check_hresult(m_deviceResources->GetD3DDevice()->CreateInputLayout( + vertexDesc.data(), static_cast<UINT>(vertexDesc.size()), + vertexShaderFileData.data(), + static_cast<UINT>(vertexShaderFileData.size()), &m_inputLayout)); + + // After the pixel shader file is loaded, create the shader and constant + // buffer. + std::vector<byte> pixelShaderFileData = + co_await DX::ReadDataAsync(L"ms-appx:///PixelShader.cso"); + winrt::check_hresult(m_deviceResources->GetD3DDevice()->CreatePixelShader( + pixelShaderFileData.data(), pixelShaderFileData.size(), nullptr, + &m_pixelShader)); + + const CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ModelConstantBuffer), + D3D11_BIND_CONSTANT_BUFFER); + winrt::check_hresult(m_deviceResources->GetD3DDevice()->CreateBuffer( + &constantBufferDesc, nullptr, &m_modelConstantBuffer)); + + if (!m_usingVprtShaders) { + // Load the pass-through geometry shader. + std::vector<byte> geometryShaderFileData = + co_await DX::ReadDataAsync(L"ms-appx:///GeometryShader.cso"); + + // After the pass-through geometry shader file is loaded, create the shader. + winrt::check_hresult( + m_deviceResources->GetD3DDevice()->CreateGeometryShader( + geometryShaderFileData.data(), geometryShaderFileData.size(), + nullptr, &m_geometryShader)); + } + + // Load mesh vertices. Each vertex has a position and a color. + // Note that the cube size has changed from the default DirectX app + // template. Windows Holographic is scaled in meters, so to draw the + // cube at a comfortable size we made the cube width 0.2 m (20 cm). + static const std::array<VertexPositionColor, 8> cubeVertices = {{ + {XMFLOAT3(-0.1f, -0.1f, -0.1f), XMFLOAT3(0.0f, 0.0f, 0.0f)}, + {XMFLOAT3(-0.1f, -0.1f, 0.1f), XMFLOAT3(0.0f, 0.0f, 1.0f)}, + {XMFLOAT3(-0.1f, 0.1f, -0.1f), XMFLOAT3(0.0f, 1.0f, 0.0f)}, + {XMFLOAT3(-0.1f, 0.1f, 0.1f), XMFLOAT3(0.0f, 1.0f, 1.0f)}, + {XMFLOAT3(0.1f, -0.1f, -0.1f), XMFLOAT3(1.0f, 0.0f, 0.0f)}, + {XMFLOAT3(0.1f, -0.1f, 0.1f), XMFLOAT3(1.0f, 0.0f, 1.0f)}, + {XMFLOAT3(0.1f, 0.1f, -0.1f), XMFLOAT3(1.0f, 1.0f, 0.0f)}, + {XMFLOAT3(0.1f, 0.1f, 0.1f), XMFLOAT3(1.0f, 1.0f, 1.0f)}, + }}; + + D3D11_SUBRESOURCE_DATA vertexBufferData = {0}; + vertexBufferData.pSysMem = cubeVertices.data(); + vertexBufferData.SysMemPitch = 0; + vertexBufferData.SysMemSlicePitch = 0; + const CD3D11_BUFFER_DESC vertexBufferDesc( + sizeof(VertexPositionColor) * static_cast<UINT>(cubeVertices.size()), + D3D11_BIND_VERTEX_BUFFER); + winrt::check_hresult(m_deviceResources->GetD3DDevice()->CreateBuffer( + &vertexBufferDesc, &vertexBufferData, &m_vertexBuffer)); + + // Load mesh indices. Each trio of indices represents + // a triangle to be rendered on the screen. + // For example: 2,1,0 means that the vertices with indexes + // 2, 1, and 0 from the vertex buffer compose the + // first triangle of this mesh. + // Note that the winding order is clockwise by default. + constexpr std::array<unsigned short, 36> cubeIndices = {{ + 2, 1, 0, // -x + 2, 3, 1, + + 6, 4, 5, // +x + 6, 5, 7, + + 0, 1, 5, // -y + 0, 5, 4, + + 2, 6, 7, // +y + 2, 7, 3, + + 0, 4, 6, // -z + 0, 6, 2, + + 1, 3, 7, // +z + 1, 7, 5, + }}; + + m_indexCount = static_cast<unsigned int>(cubeIndices.size()); + + D3D11_SUBRESOURCE_DATA indexBufferData = {0}; + indexBufferData.pSysMem = cubeIndices.data(); + indexBufferData.SysMemPitch = 0; + indexBufferData.SysMemSlicePitch = 0; + CD3D11_BUFFER_DESC indexBufferDesc(sizeof(unsigned short) * + static_cast<UINT>(cubeIndices.size()), + D3D11_BIND_INDEX_BUFFER); + winrt::check_hresult(m_deviceResources->GetD3DDevice()->CreateBuffer( + &indexBufferDesc, &indexBufferData, &m_indexBuffer)); + + // Once the cube is loaded, the object is ready to be rendered. + m_loadingComplete = true; +}; + +void SpinningCubeRenderer::ReleaseDeviceDependentResources() { + m_loadingComplete = false; + m_usingVprtShaders = false; + m_vertexShader.Reset(); + m_inputLayout.Reset(); + m_pixelShader.Reset(); + m_geometryShader.Reset(); + m_modelConstantBuffer.Reset(); + m_vertexBuffer.Reset(); + m_indexBuffer.Reset(); +} |