input.cpp

This example demonstrates the use of callbacks for handling user input. It renders two objects: A rotating cube and a billboard which shows a 2D mouse cursor.

The billboard mouse cursor replaces the native mouse cursor whenever the mouse position is over the visible part of the target window.

The cube can be rotated by moving the mouse when the left mouse button is pressed. For this purpose, the mouse_motion callback is used, which reports raw, high precision relative mouse movement to the application.

Here is the vertex shader for rendering the cube (cube.vsh)

//          Copyright Florian Winter 2010 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)


// Per-scene constants
cbuffer PerScene {
  // Model-view-projection matric
  float4x4 model_view_projection_matrix;
}

struct Output {
  float4 position : SV_POSITION;
  float4 world_position : WORLDPOS;
};

// Main shader function
Output main(float4 position : POSITION) {
  // Transform position using model-view-projection matric
  Output output;
  output.position = mul(position, model_view_projection_matrix);
  output.world_position = position;
  return output;
}

Here is the pixel shader for rendering the cube (cube.psh)

//          Copyright Florian Winter 2010 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)


float4 main(float4 position : SV_POSITION, float4 world_position : WORLDPOS) : SV_Target {
  return world_position;
}

Here is the vertex shader for rendering the billboard (billboard.vsh)

//          Copyright Florian Winter 2010 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)


// Per-scene constants
cbuffer PerScene {
  // Model-view-projection matric
  float4x4 model_view_projection_matrix;
}

struct Output {
  float4 position : SV_POSITION;
  float4 model_position : MODELPOS;
};

// Main shader function
Output main(float4 position : POSITION) {
  // Transform position using model-view-projection matric
  Output output;
  output.position = mul(position, model_view_projection_matrix);
  output.model_position = position / 16;
  return output;
}

Here is the pixel shader for rendering the billboard (billboard.psh)

//          Copyright Florian Winter 2010 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)


// This shader draws a mouse cursor as a simple transparent star pattern.
float4 main(float4 position : SV_POSITION, float4 model_position : MODELPOS) : SV_Target {
  // Get polar coordinates
  float angle = atan2(model_position.y, model_position.x);
  float distance = length(model_position);

  // Compute color
  float factor = 0.5 + 0.25 * cos(angle * 5);
  return clamp(1 - distance, 0, 1) * factor;
}

Here is the source code:

//          Copyright Florian Winter 2010 - 2010.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <tk11/tk11.hpp>

#include <boost/bind.hpp>

#include <D3DX10Math.h>

using namespace tk11;


// 2D mouse cursor.
class Billboard {
public:
  // Object initialization.
  // Here we create resources, such as textures, buffers and shaders.
  void init(ID3D11Device_ptr device) {
    // Load shaders
    vertex_shader_code = load_compiled_shader("billboard.Vso");
    pixel_shader_code = load_compiled_shader("billboard.Pso");

    // Create vertex buffer
    Vertex vertices[4];
    float w = 16;
    float h = 16;
    vertices[0].position = D3DXVECTOR2(-w, h);
    vertices[1].position = D3DXVECTOR2(-w, -h);
    vertices[2].position = D3DXVECTOR2(w, h);
    vertices[3].position = D3DXVECTOR2(w, -h);
    vertex_buffer = create_vertex_buffer(device, vertices, 4);

    // Set up input element descriptions
    D3D11_INPUT_ELEMENT_DESC elements[1] = {
      make_vector_input_element<float, 2>("POSITION")
    };

    // Create input layout
    input_layout = create_input_layout(device, elements, 1, vertex_shader_code);

    // Create vertex shader
    vertex_shader = create_vertex_shader(device, vertex_shader_code);

    // Create vertex shader constant buffer
    vertex_shader_cbuffer = create_typed_constant_buffer<VS_Constants>(device);

    // Create pixel shader
    pixel_shader = create_pixel_shader(device, pixel_shader_code);

    // Fill in blend state description
    D3D11_BLEND_DESC desc;
    desc.AlphaToCoverageEnable = FALSE;
    desc.IndependentBlendEnable = FALSE;
    desc.RenderTarget[0].BlendEnable = TRUE;
    desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
    desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
    desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
    desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
    desc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
    desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

    // Create blend state
    blend_state = create_blend_state(device, desc);
  }

  // Mouse pointer movement
  void mouse_move(int x, int y) {
    // Setup model matrix
    D3DXMatrixTranslation(&model_view_matrix, FLOAT(x), FLOAT(y), 0);
  }

  // Rendering.
  // Here we render the object to the main render target (the back buffer)
  void render(ID3D11DeviceContext_ptr context, const D3DXMATRIX& projection_matrix) {
    // Compute model-view-projection matrix
    // by coposing model-view matrix and projection matrix
    VS_Constants vs_constants;
    D3DXMatrixMultiply(&vs_constants.model_view_projection_matrix, &model_view_matrix, &projection_matrix);

    // A D3DXMATRIX must be transposed before it can be used by a shader
    D3DXMatrixTranspose(&vs_constants.model_view_projection_matrix, &vs_constants.model_view_projection_matrix);
    
    // Update vertex shader constant buffer
    typed_update_constant_buffer(context, vertex_shader_cbuffer, vs_constants);

    // Bind shaders
    context->VSSetShader(vertex_shader.get(), 0, 0);
    context->PSSetShader(pixel_shader.get(), 0, 0);

    // Bind constant buffers
    ID3D11Buffer* vs_cbuffers[1] = { vertex_shader_cbuffer.get() };
    context->VSSetConstantBuffers(0, 1, vs_cbuffers);

    // Bind vertex buffer
    ID3D11Buffer* vb_buffers[1] = { vertex_buffer.get() };
    UINT vb_strides[1] = { sizeof(Vertex) };
    UINT vb_offsets[1] = { 0 };
    context->IASetVertexBuffers(0, 1, vb_buffers, vb_strides, vb_offsets);

    // Bind input layout
    context->IASetInputLayout(input_layout.get());

    // Set primitive topology
    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

    // Set blend state
    FLOAT blend_factors[4] = { 0, 0, 0, 0 };
    context->OMSetBlendState(blend_state.get(), blend_factors, 0xffffffff);

    // Draw two triangle strips
    context->Draw(4, 0);
  }

private:
  // Vertex structure
  struct Vertex {
    // Position
    D3DXVECTOR2 position;
  };

  // Constants of the vertex shader
  struct VS_Constants {
    // Transformation matrix
    D3DXMATRIX model_view_projection_matrix;
  };

  // Model matrix
  D3DXMATRIX model_view_matrix;

  // Vertex shader code
  ID3D10Blob_ptr vertex_shader_code;

  // Pixel shader code
  ID3D10Blob_ptr pixel_shader_code;

  // Vertex buffer
  ID3D11Buffer_ptr vertex_buffer;

  // Input layout
  ID3D11InputLayout_ptr input_layout;

  // Vertex shader
  ID3D11VertexShader_ptr vertex_shader;

  // Vertex shader constant buffer
  ID3D11Buffer_ptr vertex_shader_cbuffer;

  // Pixel shader
  ID3D11PixelShader_ptr pixel_shader;

  // Blend state
  ID3D11BlendState_ptr blend_state;
};


// Rotating cube
class Cube {
public:
  Cube()
    : angle1(0)
    , angle2(0)
    , torque1(0)
    , torque2(0)
    , view_distance(5)
    , last_time(seconds(0))
  {}

  // Object initialization.
  // Here we create resources, such as textures, buffers and shaders.
  void init(ID3D11Device_ptr device) {
    // Load shaders
    vertex_shader_code = load_compiled_shader("cube.Vso");
    pixel_shader_code = load_compiled_shader("cube.Pso");

    // Create vertex buffer
    Vertex vertices[8];
    vertices[0].position = D3DXVECTOR3(-1, -1, -1);
    vertices[1].position = D3DXVECTOR3(1, -1, -1);
    vertices[2].position = D3DXVECTOR3(-1, 1, -1);
    vertices[3].position = D3DXVECTOR3(1, 1, -1);
    vertices[4].position = D3DXVECTOR3(-1, -1, 1);
    vertices[5].position = D3DXVECTOR3(1, -1, 1);
    vertices[6].position = D3DXVECTOR3(-1, 1, 1);
    vertices[7].position = D3DXVECTOR3(1, 1, 1);
    vertex_buffer = create_vertex_buffer(device, vertices, 8);

    // Create index buffer
    int indices[16] = {
      4, 6, 0, 2, 1, 3, 5, 7,
      3, 2, 7, 6, 5, 4, 1, 0
    };
    index_buffer = create_index_buffer(device, indices, 16);

    // Set up input element descriptions
    D3D11_INPUT_ELEMENT_DESC elements[1] = {
      make_vector_input_element<float, 3>("POSITION")
    };

    // Create input layout
    input_layout = create_input_layout(device, elements, 1, vertex_shader_code);

    // Create vertex shader
    vertex_shader = create_vertex_shader(device, vertex_shader_code);

    // Create vertex shader constant buffer
    vertex_shader_cbuffer = create_typed_constant_buffer<VS_Constants>(device);

    // Create pixel shader
    pixel_shader = create_pixel_shader(device, pixel_shader_code);
  }

  // Object update.
  // Here we update all variables that change over time.
  void update(const Duration& elapsed_time) {
    // Get relative elapsed time
    Duration delta_time(elapsed_time - last_time);
    last_time = elapsed_time;

    // Get relative elapsed time in seconds
    float t = time_div_float<float>(delta_time, seconds(1));

    // Update rotation
    angle1 += torque1;
    angle2 += torque2;
    torque1 *= powf(0.3f, t);
    torque2 *= powf(0.3f, t);

    // Normalize angles to keep them numerically stable
    const float pi = 3.1415926535f;
    angle1 = fmodf(angle1, 2 * pi);
    angle2 = fmodf(angle2, 2 * pi);

    // Update rotation matrix
    update_matrix();
  }
  
  // Relative high-precision mouse motion.
  void mouse_motion(int delta_x, int delta_y) {
    const float pi = 3.1415926535f;

    // Update angles based on mouse motion
    torque1 += float(delta_x) * 2 * pi * 0.0001f;
    torque2 += float(delta_y) * 2 * pi * 0.0001f;
  }

  // Mouse wheel has been turned
  void mouse_wheel(int delta, int x, int y) {
    // Adjust view distance
    view_distance *= powf(1.1f, float(delta));
  }

  // Rendering.
  // Here we render the object to the main render target (the back buffer)
  void render(ID3D11DeviceContext_ptr context, const D3DXMATRIX& projection_matrix) {
    // Compute model-view-projection matrix
    // by coposing model-view matrix and projection matrix
    VS_Constants vs_constants;
    D3DXMatrixMultiply(&vs_constants.model_view_projection_matrix, &model_view_matrix, &projection_matrix);

    // A D3DXMATRIX must be transposed before it can be used by a shader
    D3DXMatrixTranspose(&vs_constants.model_view_projection_matrix, &vs_constants.model_view_projection_matrix);
    
    // Update vertex shader constant buffer
    typed_update_constant_buffer(context, vertex_shader_cbuffer, vs_constants);

    // Bind shaders
    context->VSSetShader(vertex_shader.get(), 0, 0);
    context->PSSetShader(pixel_shader.get(), 0, 0);

    // Bind constant buffers
    ID3D11Buffer* vs_cbuffers[1] = { vertex_shader_cbuffer.get() };
    context->VSSetConstantBuffers(0, 1, vs_cbuffers);

    // Bind vertex buffer
    ID3D11Buffer* vb_buffers[1] = { vertex_buffer.get() };
    UINT vb_strides[1] = { sizeof(Vertex) };
    UINT vb_offsets[1] = { 0 };
    context->IASetVertexBuffers(0, 1, vb_buffers, vb_strides, vb_offsets);

    // Bind index buffer
    context->IASetIndexBuffer(index_buffer.get(), DXGI_FORMAT_R32_UINT, 0);

    // Bind input layout
    context->IASetInputLayout(input_layout.get());

    // Set primitive topology
    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

    // Draw two triangle strips
    context->DrawIndexed(8, 0, 0);
    context->DrawIndexed(8, 8, 0);
  }

private:
  // A vertex of the cube
  struct Vertex {
    // Position
    D3DXVECTOR3 position;
  };

  // Constants of the vertex shader
  struct VS_Constants {
    // Transformation matrix
    D3DXMATRIX model_view_projection_matrix;
  };

  // Model-view matrix
  D3DXMATRIX model_view_matrix;

  // Vertex shader code
  ID3D10Blob_ptr vertex_shader_code;

  // Pixel shader code
  ID3D10Blob_ptr pixel_shader_code;

  // Vertex buffer
  ID3D11Buffer_ptr vertex_buffer;

  // Index buffer
  ID3D11Buffer_ptr index_buffer;

  // Input layout
  ID3D11InputLayout_ptr input_layout;

  // Vertex shader
  ID3D11VertexShader_ptr vertex_shader;

  // Vertex shader constant buffer
  ID3D11Buffer_ptr vertex_shader_cbuffer;

  // Pixel shader
  ID3D11PixelShader_ptr pixel_shader;

  // Orientation angles of cube
  float angle1;
  float angle2;

  // Momentum
  float torque1;
  float torque2;

  // Distance of the viewer to the object
  float view_distance;

  // Last update time
  Duration last_time;

  // Update the model-view matrix based on current parameters
  void update_matrix() {
    // Setup camera matrix
    D3DXMATRIX view_matrix;
    D3DXVECTOR3 eye(0, 0, view_distance);
    D3DXVECTOR3 at(0, 0, 0);
    D3DXVECTOR3 up(0, 1, 0);
    D3DXMatrixLookAtLH(&view_matrix, &eye, &at, &up);

    // Setup model matrix
    D3DXMATRIX model_matrix;
    D3DXMATRIX rot1;
    D3DXMATRIX rot2;
    D3DXVECTOR3 axis1(1,0,0);
    D3DXVECTOR3 axis2(0,1,0);
    D3DXMatrixRotationAxis(&rot1, &axis1, angle1);
    D3DXMatrixRotationAxis(&rot2, &axis2, angle2);
    D3DXMatrixMultiply(&model_matrix, &rot1, &rot2);

    // Combine model and view matrix
    D3DXMatrixMultiply(&model_view_matrix, &model_matrix, &view_matrix);
  }
};


// Application class
// Holds all resources owned by the application and makes them accessible by
// callbacks.
class Application {
public:
  // Initialize and run application
  // Creates the framework and the window, then starts the main loop.
  Application()
    : window(framework, get_windows_parameters())
    , cursor_in_window(false)
    , left_button_down(false)
  {
    framework.run();
  }

private:
  // Perspective projection matrix.
  // This projection matrix is used for drawing 3D objects.
  D3DXMATRIX perspective_matrix;

  // Orthogonal projection matrix
  // This projection matrix is used for drawing HUD objects.
  D3DXMATRIX ortho_matrix;

  // Rotating cube object
  Cube cube;

  // Billboard object
  Billboard billboard;

  // The Tk11 framework
  Framework framework;

  // The main rendering window
  Window window;

  // Mouse cursor is inside window
  bool cursor_in_window;

  // Left mouse button is down
  bool left_button_down;

  // Resize callback.
  // Here we update all variables which depend on the size of the target window,
  // such as projection matrices and viewport parameters.
  void resize(unsigned int x_resolution, unsigned int y_resolution,
      bool fullscreen, ID3D11Device_ptr device, IDXGISwapChain_ptr swap_chain,
      ID3D11Texture2D_ptr back_buffer)
  {
    // Setup projection matrix
    float aspect = float(x_resolution) / float(y_resolution);
    float fovy = 3.1415926535f / 3;
    float znear = 2;
    float zfar = 100;
    D3DXMatrixPerspectiveFovLH(&perspective_matrix, fovy, aspect, znear, zfar);

    // Prepare orthogonal projection matrix
    D3DXMatrixOrthoOffCenterLH(&ortho_matrix, 0, FLOAT(x_resolution), FLOAT(y_resolution), 0, 0, 1);
  }

  // Scene initialization callback.
  // Here we create resources, such as textures, buffers and shaders.
  void init(ID3D11Device_ptr device) {
    // Initialize objects
    cube.init(device);
    billboard.init(device);
  }

  // Scene update callback.
  // Here we update all variables that change over time.
  void update(const Duration& elapsed_time) {
    // Update objects
    cube.update(elapsed_time);
  }

  // Scene rendering callback.
  // Here we render the scene to the main render target (the back buffer)
  void render(ID3D11DeviceContext_ptr context, ID3D11RenderTargetView_ptr render_target) {
    // Clear render target view
    float clear_color_rgba[] = { 0, 0, 0, 0 };
    context->ClearRenderTargetView(render_target.get(), clear_color_rgba);

    // Render 3D objects
    cube.render(context, perspective_matrix);

    // Render the cursor if it is inside the window,
    // and the left button is not down.
    if(cursor_in_window && !left_button_down) {
      billboard.render(context, ortho_matrix);
    }
  }

  // Mouse pointer movement callback.
  // This callback is invoked when the mouse pointer has been moved over the
  // target window.
  void mouse_move(int x, int y) {
    // Update the position of the billboard which shows the mouse cursor
    billboard.mouse_move(x, y);
  }

  // Callback: Mouse pointer has entered the window.
  void mouse_enter() {
    // Rendered cursor is now visible
    cursor_in_window = true;
  }

  // Callback: Mouse pointer has left the window.
  void mouse_leave() {
    // Rendered cursor is now invisible
    cursor_in_window = false;
  }

  // Callback: A mouse button has been pressed.
  void mouse_down(Mouse_Button button, int x, int y) {
    std::cout << "down button=" << button << ", x=" << x << ", y=" << y << std::endl;

    // Update left button state.
    // If the left burron is down, raw mouse motion rotates the cube.
    if(button == left_button) {
      left_button_down = true;
    }
  }

  // Callback: A mouse button has been released.
  void mouse_up(Mouse_Button button, int x, int y) {
    std::cout << "up button=" << button << ", x=" << x << ", y=" << y << std::endl;

    // Update left button state.
    // If the left burron is down, raw mouse motion rotates the cube.
    if(button == left_button) {
      left_button_down = false;
    }
  }

  // Callback: Mouse wheel has been turned
  void mouse_wheel(int delta, int x, int y) {
    // Forward event to the cube object.
    // The mouse wheel controls the view distance.
    cube.mouse_wheel(delta, x, y);
  }

  // Callback: Relative high-precision mouse motion.
  void mouse_motion(int delta_x, int delta_y) {
    // If left button is down, forward the event to the cube to rotate it.
    if(left_button_down) {
      cube.mouse_motion(delta_x, delta_y);
    }
  }

  // Callback: A key has been pressed
  void key_down(unsigned int key) {
    std::cout << "DOWN: " << key << std::endl;

    // Handle some keys
    if(key == VK_ESCAPE) {
      // Quit application
      framework.quit();
    }
  }

  // Callback: A key has been released
  void key_up(unsigned int key) {
    std::cout << "UP: " << key << std::endl;
  }

  // Callback: A key has been repeated
  void key_repeat(unsigned int key) {
    std::cout << "REPEAT: " << key << std::endl;
  }

  // Callback: A character has been tyed
  void char_input(unsigned int char_) {
    std::wcout << static_cast<wchar_t>(char_) << std::endl;
  }

  // Get window parameters
  // Returns a Window_Parameters structure which defines all parameters for
  // creating the Direct3D window, including callbacks.
  Window_Parameters get_windows_parameters() {
    using boost::bind;

    // Framework parameters
    Window_Parameters parameters;

    // Set windowed mode with 800x600 resolution
    parameters.x_resolution = 800;
    parameters.y_resolution = 600;
    parameters.fullscreen = false;

    // Hide the Windows mouse cursor when it is over the window.
    // We want to render our own cursor.
    parameters.cursor_mode = hide_cursor;

    // Window and rendering callbacks
    parameters.resize_callback =
      bind(&Application::resize, this, _1, _2, _3, _4, _5, _6);
    parameters.init_callback = bind(&Application::init, this, _1);
    parameters.update_callback = bind(&Application::update, this, _1);
    parameters.render_callback = bind(&Application::render, this, _1, _2);

    // Mouse calbacks
    parameters.mouse_move_callback = bind(&Application::mouse_move, this, _1, _2);
    parameters.mouse_enter_callback = bind(&Application::mouse_enter, this);
    parameters.mouse_leave_callback = bind(&Application::mouse_leave, this);
    parameters.mouse_down_callback = bind(&Application::mouse_down, this, _1, _2, _3);
    parameters.mouse_up_callback = bind(&Application::mouse_up, this, _1, _2, _3);
    parameters.mouse_wheel_callback = bind(&Application::mouse_wheel, this, _1, _2, _3);
    parameters.mouse_motion_callback = bind(&Application::mouse_motion, this, _1, _2);

    // Keyboard callbacks
    parameters.key_down_callback = bind(&Application::key_down, this, _1);
    parameters.key_up_callback = bind(&Application::key_up, this, _1);
    parameters.key_repeat_callback = bind(&Application::key_repeat, this, _1);
    parameters.char_input_callback = bind(&Application::char_input, this, _1);

    return parameters;
  }
};


int main() {
  try {
    // Crate and run application
    Application app;

    return 0;
  }
  catch(const boost::exception& e) {
    // Show an error dialog with diagnostic information about the exception
    // which occurred.
    // (Only for debugging. In a real application, you may want to translate
    // exceptions into more readable error messages).
    show_error_dialog(diagnostic_information(e));
    return 1;
  }
  catch(const std::exception& e) {
    // Show an error dialog with the name of the exception.
    // (Only for debugging. In a real application, you may want to translate
    // exceptions into more readable error messages).
    show_error_dialog(e.what());
    return 1;
  }
  catch(...) {
    // Show an error dialog.
    // (Only for debugging. In a real application, you may want to translate
    // exceptions into more readable error messages).
    show_error_dialog("Unknown exception");
    return 1;
  }
}
Tk11 Direct3D 11 Toolkit version 0.2 (SourceForge)
Copyright Florian Winter 2010 - 2010. Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Generated on Sun Apr 11 20:22:57 2010 for Tk11 by  doxygen 1.6.3