aboutsummaryrefslogtreecommitdiffstats
path: root/support/hololens/ServoApp/Content/SpinningCubeRenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'support/hololens/ServoApp/Content/SpinningCubeRenderer.cpp')
-rw-r--r--support/hololens/ServoApp/Content/SpinningCubeRenderer.cpp266
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();
+}