template project, first version

This commit is contained in:
rachelchu
2017-10-11 15:01:05 +02:00
commit 8e902224cf
794 changed files with 326190 additions and 0 deletions

View File

@@ -0,0 +1,985 @@
// ---------------------------------------------------------------------------
//
// @file TwSimpleDX11.cpp
//
// @brief Example that uses AntTweakBar with DirectX11.
//
// It draws a Menger sponge, aka Sierpinski cube:
// http://en.wikipedia.org/wiki/Menger_sponge .
//
// Cubes shading is augmented with some simple ambient occlusion
// applied by subdividing each cube face into a 3x3 grid.
// AntTweakBar is used to add some interacitve controls.
//
// Note that most of the code here is related to DirectX and
// Menger sponge generation. AntTweakBar calls are localized
// in the WinMain function.
//
// AntTweakBar: http://anttweakbar.sourceforge.net/doc
// DirectX: http://msdn.microsoft.com/directx
//
// @author Philippe Decaudin
//
// ---------------------------------------------------------------------------
#include <AntTweakBar.h>
#include <cmath>
#include <vector>
#include <cassert>
#include "d3d10vs2003.h" // workaround to include D3D10.h and D3D11.h with VS2003
#include <d3d11.h>
// Shaders:
// Vertex and pixel shaders are defined in TwSimpleDX11.hlsl and are compiled
// in a pre-build step using the fxc.exe compiler (from the DirectX SDK Aug'09 or later).
// Pre-build commands are:
// fxc /T vs_4_0_level_9_1 /E MainVS /Fh $(IntDir)\TwSimpleDX11_VS.h TwSimpleDX11.hlsl
// fxc /T ps_4_0_level_9_1 /E MainPS /Fh $(IntDir)\TwSimpleDX11_PS.h TwSimpleDX11.hlsl
//
#include "TwSimpleDX11_VS.h"
#include "TwSimpleDX11_PS.h"
// D3D objects
ID3D11Device * g_D3DDev = NULL;
ID3D11DeviceContext * g_D3DDevCtx = NULL;
IDXGISwapChain * g_SwapChain = NULL;
DXGI_SWAP_CHAIN_DESC g_SwapChainDesc;
ID3D11RenderTargetView *g_RenderTargetView = NULL;
ID3D11DepthStencilView *g_DepthStencilView = NULL;
D3D11_TEXTURE2D_DESC g_DepthStencilDesc;
ID3D11VertexShader * g_VertexShader = NULL;
ID3D11PixelShader * g_PixelShader = NULL;
ID3D11InputLayout * g_InputLayout = NULL;
ID3D11Buffer * g_VertexBuffer = NULL;
ID3D11Buffer * g_IndexBuffer = NULL;
ID3D11BlendState * g_BlendState = NULL;
ID3D11DepthStencilState*g_DepthStencilState = NULL;
ID3D11RasterizerState * g_RasterState = NULL;
ID3D11Buffer * g_ConstantBuffer = NULL;
// Geometry data structures and objects
struct Vector3
{
float v[3];
static Vector3 ZERO;
};
Vector3 Vector3::ZERO = { 0, 0, 0 };
struct Matrix4x4
{
float m[4][4];
static Matrix4x4 IDENTITY;
};
Matrix4x4 Matrix4x4::IDENTITY = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };
struct Quaternion
{
float q[4];
static Quaternion IDENTITY;
};
Quaternion Quaternion::IDENTITY = { 0, 0, 0, 1 };
const float FLOAT_PI = 3.14159265f;
struct Vertex
{
Vector3 Position;
Vector3 Normal;
unsigned int AmbientColor;
};
struct ShaderConstants
{
Matrix4x4 WorldViewProj;
Matrix4x4 WorldNorm;
Vector3 LightDir;
float LightCoeff;
};
// Each cube face is split into a 3x3 grid
const int CUBE_FACE_VERTEX_COUNT = 4 * 4; // 16 vertices per face
const int CUBE_FACE_TRIANGLE_COUNT = 2 * 3 * 3; // 18 triangles to be drawn for each face
// Faces color of the sponge wrt to recursion level
const unsigned int COLORS[] = { 0xffffffff, 0xff007fff, 0xff7fff00, 0xffff007f, 0xff0000ff, 0xff00ff00, 0xffff0000 };
// Scene globals
Quaternion g_SpongeRotation; // model rotation, set by InitScene
int g_SpongeLevel = 2; // number of recursions
bool g_SpongeAO = true; // apply ambient occlusion
unsigned int g_SpongeIndicesCount = 0; // set by BuildSponge
Vector3 g_LightDir = {-0.5f, -0.2f, 1}; // light direction vector
float g_CamDistance = 0.7f; // camera distance
float g_BackgroundColor[] = {0, 0, 0.5f, 1}; // background color
bool g_Animate = true; // enable animation
float g_AnimationSpeed = 0.2f; // animation speed
// Some math operators and functions.
// They are defined here to avoid linking with D3DX.
Vector3 operator+(const Vector3& a, const Vector3& b)
{
Vector3 out;
out.v[0] = a.v[0] + b.v[0];
out.v[1] = a.v[1] + b.v[1];
out.v[2] = a.v[2] + b.v[2];
return out;
}
Vector3 operator*(float s, const Vector3& a)
{
Vector3 out;
out.v[0] = s * a.v[0];
out.v[1] = s * a.v[1];
out.v[2] = s * a.v[2];
return out;
}
float Length(const Vector3& a)
{
return sqrt(a.v[0]*a.v[0] + a.v[1]*a.v[1] + a.v[2]*a.v[2]);
}
Matrix4x4 Projection(float fov, float aspectRatio, float zNear, float zFar) // Left-handed projection
{
Matrix4x4 out;
float yScale = 1.0f / tan(fov / 2.0f);
float xScale = yScale / aspectRatio;
out.m[0][0] = xScale;
out.m[0][1] = out.m[0][2] = out.m[0][3] = 0;
out.m[1][1] = yScale;
out.m[1][0] = out.m[1][2] = out.m[1][3] = 0;
out.m[2][0] = out.m[2][1] = 0;
out.m[2][2] = zFar / (zFar - zNear);
out.m[2][3] = 1;
out.m[3][0] = out.m[3][1] = out.m[3][3] = 0;
out.m[3][2] = - zNear * zFar / (zFar - zNear);
return out;
}
Matrix4x4 Translation(const Vector3& t)
{
Matrix4x4 out(Matrix4x4::IDENTITY);
out.m[3][0] = t.v[0];
out.m[3][1] = t.v[1];
out.m[3][2] = t.v[2];
return out;
}
Matrix4x4 Scale(float s)
{
Matrix4x4 out(Matrix4x4::IDENTITY);
out.m[0][0] = out.m[1][1] = out.m[2][2] = s;
return out;
}
Matrix4x4 operator*(const Matrix4x4& a, const Matrix4x4& b)
{
Matrix4x4 out(Matrix4x4::IDENTITY);
int i, j;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
out.m[i][j] = a.m[i][0]*b.m[0][j] + a.m[i][1]*b.m[1][j] + a.m[i][2]*b.m[2][j] + a.m[i][3]*b.m[3][j];
return out;
}
Vector3 operator*(const Vector3& p, const Matrix4x4& a)
{
Vector3 out;
float rw = 1.f / (p.v[0]*a.m[0][3] + p.v[1]*a.m[1][3] + p.v[2]*a.m[2][3] + a.m[3][3]);
out.v[0] = rw * (p.v[0]*a.m[0][0] + p.v[1]*a.m[1][0] + p.v[2]*a.m[2][0] + a.m[3][0]);
out.v[1] = rw * (p.v[0]*a.m[0][1] + p.v[1]*a.m[1][1] + p.v[2]*a.m[2][1] + a.m[3][1]);
out.v[2] = rw * (p.v[0]*a.m[0][2] + p.v[1]*a.m[1][2] + p.v[2]*a.m[2][2] + a.m[3][2]);
return out;
}
Quaternion RotationFromAxisAngle(const Vector3& axis, float angle)
{
Quaternion out;
float norm = Length(axis);
float sina2 = sin(0.5f * angle);
out.q[0] = sina2 * axis.v[0] / norm;
out.q[1] = sina2 * axis.v[1] / norm;
out.q[2] = sina2 * axis.v[2] / norm;
out.q[3] = cos(0.5f * angle);
return out;
}
void AxisAngleFromRotation(Vector3& outAxis, float& outAngle, const Quaternion& quat)
{
float sina2 = sqrt(quat.q[0]*quat.q[0] + quat.q[1]*quat.q[1] + quat.q[2]*quat.q[2]);
outAngle = 2.0f * atan2(sina2, quat.q[3]);
float r = (sina2 > 0) ? (1.0f / sina2) : 0;
outAxis.v[0] = r * quat.q[0];
outAxis.v[1] = r * quat.q[1];
outAxis.v[2] = r * quat.q[2];
}
Matrix4x4 QuaternionToMatrix(const Quaternion& quat)
{
Matrix4x4 out;
float yy2 = 2.0f * quat.q[1] * quat.q[1];
float xy2 = 2.0f * quat.q[0] * quat.q[1];
float xz2 = 2.0f * quat.q[0] * quat.q[2];
float yz2 = 2.0f * quat.q[1] * quat.q[2];
float zz2 = 2.0f * quat.q[2] * quat.q[2];
float wz2 = 2.0f * quat.q[3] * quat.q[2];
float wy2 = 2.0f * quat.q[3] * quat.q[1];
float wx2 = 2.0f * quat.q[3] * quat.q[0];
float xx2 = 2.0f * quat.q[0] * quat.q[0];
out.m[0][0] = - yy2 - zz2 + 1.0f;
out.m[0][1] = xy2 + wz2;
out.m[0][2] = xz2 - wy2;
out.m[0][3] = 0;
out.m[1][0] = xy2 - wz2;
out.m[1][1] = - xx2 - zz2 + 1.0f;
out.m[1][2] = yz2 + wx2;
out.m[1][3] = 0;
out.m[2][0] = xz2 + wy2;
out.m[2][1] = yz2 - wx2;
out.m[2][2] = - xx2 - yy2 + 1.0f;
out.m[2][3] = 0;
out.m[3][0] = out.m[3][1] = out.m[3][2] = 0;
out.m[3][3] = 1;
return out;
}
// Forward declarations
HRESULT InitDevice(HWND wnd);
HRESULT InitScene();
void Cleanup();
LRESULT CALLBACK MessageProc(HWND, UINT, WPARAM, LPARAM);
void Anim();
void Render();
HRESULT BuildSponge(int levelMax, bool aoEnabled);
// Callback function called by AntTweakBar to set the sponge recursion level
void TW_CALL SetSpongeLevelCB(const void *value, void * /*clientData*/)
{
g_SpongeLevel = *static_cast<const int *>(value);
BuildSponge(g_SpongeLevel, g_SpongeAO);
}
// Callback function called by AntTweakBar to get the sponge recursion level
void TW_CALL GetSpongeLevelCB(void *value, void * /*clientData*/)
{
*static_cast<int *>(value) = g_SpongeLevel;
}
// Callback function called by AntTweakBar to enable/disable ambient occlusion
void TW_CALL SetSpongeAOCB(const void *value, void * /*clientData*/)
{
g_SpongeAO = *static_cast<const bool *>(value);
BuildSponge(g_SpongeLevel, g_SpongeAO);
}
// Callback function called by AntTweakBar to get ambient occlusion state
void TW_CALL GetSpongeAOCB(void *value, void * /*clientData*/)
{
*static_cast<bool *>(value) = g_SpongeAO;
}
// Main
int WINAPI WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int cmdShow)
{
// Register our window class
WNDCLASSEX wcex = { sizeof(WNDCLASSEX), CS_HREDRAW|CS_VREDRAW, MessageProc,
0L, 0L, instance, NULL, NULL, NULL, NULL, L"TwDX11", NULL };
RegisterClassEx(&wcex);
// Create a window
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
HWND wnd = CreateWindow(L"TwDX11", L"AntTweakBar simple example using DirectX11",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
rc.right-rc.left, rc.bottom-rc.top, NULL, NULL, instance, NULL);
if (!wnd)
{
MessageBox(NULL, L"Cannot create window", L"Error", MB_OK|MB_ICONERROR);
return 0;
}
ShowWindow(wnd, cmdShow);
UpdateWindow(wnd);
// Initialize D3D11
if (FAILED(InitDevice(wnd)))
{
MessageBox(wnd, L"Cannot create D3D11 device", L"Error", MB_OK|MB_ICONERROR);
Cleanup();
return 0;
}
// Initialize the 3D scene
if (FAILED(InitScene()))
{
MessageBox(wnd, L"Scene initialization failed.", L"Error", MB_OK|MB_ICONERROR);
Cleanup();
return 0;
}
// Initialize AntTweakBar
if (!TwInit(TW_DIRECT3D11, g_D3DDev))
{
MessageBoxA(wnd, TwGetLastError(), "AntTweakBar initialization failed", MB_OK|MB_ICONERROR);
Cleanup();
return 0;
}
// Create a tweak bar
TwBar *bar = TwNewBar("TweakBar");
TwDefine(" GLOBAL help='This example shows how to integrate AntTweakBar into a DirectX11 application.' "); // Message added to the help bar.
int barSize[2] = {224, 320};
TwSetParam(bar, NULL, "size", TW_PARAM_INT32, 2, barSize);
// Add variables to the tweak bar
TwAddVarCB(bar, "Level", TW_TYPE_INT32, SetSpongeLevelCB, GetSpongeLevelCB, NULL, "min=0 max=3 group=Sponge keyincr=l keydecr=L");
TwAddVarCB(bar, "Ambient Occlusion", TW_TYPE_BOOLCPP, SetSpongeAOCB, GetSpongeAOCB, NULL, "group=Sponge key=o");
TwAddVarRW(bar, "Rotation", TW_TYPE_QUAT4F, &g_SpongeRotation, "opened=true axisz=-z group=Sponge");
TwAddVarRW(bar, "Animation", TW_TYPE_BOOLCPP, &g_Animate, "group=Sponge key=a");
TwAddVarRW(bar, "Animation speed", TW_TYPE_FLOAT, &g_AnimationSpeed, "min=-10 max=10 step=0.1 group=Sponge keyincr=+ keydecr=-");
TwAddVarRW(bar, "Light direction", TW_TYPE_DIR3F, &g_LightDir, "opened=true axisz=-z showval=false");
TwAddVarRW(bar, "Camera distance", TW_TYPE_FLOAT, &g_CamDistance, "min=0 max=4 step=0.01 keyincr=PGUP keydecr=PGDOWN");
TwAddVarRW(bar, "Background", TW_TYPE_COLOR4F, &g_BackgroundColor, "colormode=hls");
// Main message loop
MSG msg = {0};
while (WM_QUIT != msg.message)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
Anim();
Render();
}
}
TwTerminate();
Cleanup();
return (int)msg.wParam;
}
// Create Direct3D device and swap chain
HRESULT InitDevice(HWND wnd)
{
HRESULT hr = S_OK;
// Get window size
RECT rc;
GetClientRect(wnd, &rc);
UINT width = rc.right - rc.left;
UINT height = rc.bottom - rc.top;
// Create D3D11 device and swap chain
UINT createDeviceFlags = 0;
#ifdef _DEBUG
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
ZeroMemory(&g_SwapChainDesc, sizeof(g_SwapChainDesc));
g_SwapChainDesc.BufferCount = 1;
g_SwapChainDesc.BufferDesc.Width = width;
g_SwapChainDesc.BufferDesc.Height = height;
g_SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
g_SwapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
g_SwapChainDesc.BufferDesc.RefreshRate.Denominator = 0;
g_SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
g_SwapChainDesc.OutputWindow = wnd;
g_SwapChainDesc.SampleDesc.Count = 4;
g_SwapChainDesc.SampleDesc.Quality = 0;
g_SwapChainDesc.Windowed = TRUE;
g_SwapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
// Try to create a hardware accelerated device with multisample antialiasing first
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags,
NULL, 0, D3D11_SDK_VERSION, &g_SwapChainDesc, &g_SwapChain,
&g_D3DDev, NULL, &g_D3DDevCtx);
if (FAILED(hr))
{
// If failed, try without antialiasing
g_SwapChainDesc.SampleDesc.Count = 1;
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, createDeviceFlags,
NULL, 0, D3D11_SDK_VERSION, &g_SwapChainDesc, &g_SwapChain,
&g_D3DDev, NULL, &g_D3DDevCtx);
if (FAILED(hr))
{
// If failed, try to create a reference device
hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_REFERENCE, NULL, createDeviceFlags,
NULL, 0, D3D11_SDK_VERSION, &g_SwapChainDesc, &g_SwapChain,
&g_D3DDev, NULL, &g_D3DDevCtx);
if (SUCCEEDED(hr))
MessageBox(wnd, L"No DX11 hardware acceleration found.\nSwitching to REFERENCE driver (very slow).",
L"Warning", MB_OK|MB_ICONWARNING);
else
return hr;
}
}
// Create a render target and depth-stencil view
ID3D11Texture2D *backBuffer = NULL, *dsBuffer = NULL;
hr = g_SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBuffer);
if (FAILED(hr))
return hr;
hr = g_D3DDev->CreateRenderTargetView(backBuffer, NULL, &g_RenderTargetView);
backBuffer->Release();
if (FAILED(hr))
return hr;
g_DepthStencilDesc.Width = width;
g_DepthStencilDesc.Height = height;
g_DepthStencilDesc.MipLevels = 1;
g_DepthStencilDesc.ArraySize = 1;
g_DepthStencilDesc.Format = DXGI_FORMAT_D16_UNORM;
g_DepthStencilDesc.SampleDesc = g_SwapChainDesc.SampleDesc;
g_DepthStencilDesc.Usage = D3D11_USAGE_DEFAULT;
g_DepthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
g_DepthStencilDesc.CPUAccessFlags = 0;
g_DepthStencilDesc.MiscFlags = 0;
hr = g_D3DDev->CreateTexture2D(&g_DepthStencilDesc, NULL, &dsBuffer);
if (FAILED(hr))
return hr;
hr = g_D3DDev->CreateDepthStencilView(dsBuffer, NULL, &g_DepthStencilView);
dsBuffer->Release();
if (FAILED(hr))
return hr;
g_D3DDevCtx->OMSetRenderTargets(1, &g_RenderTargetView, g_DepthStencilView);
// Setup the viewport
D3D11_VIEWPORT vp;
vp.Width = (float)width;
vp.Height = (float)height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_D3DDevCtx->RSSetViewports(1, &vp);
return S_OK;
}
// Initialize the 3D objects & shaders
HRESULT InitScene()
{
HRESULT hr;
// Define the input layout
D3D11_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Position), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Normal), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(Vertex, AmbientColor), D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
hr = g_D3DDev->CreateInputLayout(layout, sizeof(layout)/sizeof(layout[0]), g_MainVS, sizeof(g_MainVS), &g_InputLayout);
if (FAILED(hr))
return hr;
// Set the input layout
g_D3DDevCtx->IASetInputLayout(g_InputLayout);
// Create shaders
hr = g_D3DDev->CreateVertexShader(g_MainVS, sizeof(g_MainVS), NULL, &g_VertexShader);
if (FAILED(hr))
return hr;
hr = g_D3DDev->CreatePixelShader(g_MainPS, sizeof(g_MainPS), NULL, &g_PixelShader);
if (FAILED(hr))
return hr;
// Set shaders
g_D3DDevCtx->VSSetShader(g_VertexShader, NULL, 0);
g_D3DDevCtx->PSSetShader(g_PixelShader, NULL, 0);
// Create vertex and index buffers
hr = BuildSponge(g_SpongeLevel, g_SpongeAO);
if (FAILED(hr))
return hr;
// Create constant buffer
D3D11_BUFFER_DESC bd;
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(ShaderConstants);
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
bd.MiscFlags = 0;
bd.StructureByteStride = 0;
hr = g_D3DDev->CreateBuffer(&bd, NULL, &g_ConstantBuffer);
if (FAILED(hr))
return hr;
// Blend state
D3D11_BLEND_DESC bsd;
bsd.AlphaToCoverageEnable = FALSE;
bsd.IndependentBlendEnable = FALSE;
for (int i = 0; i < 8; i++)
{
bsd.RenderTarget[i].BlendEnable = TRUE;
bsd.RenderTarget[i].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
bsd.RenderTarget[i].SrcBlend = D3D11_BLEND_SRC_ALPHA;
bsd.RenderTarget[i].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bsd.RenderTarget[i].BlendOp = D3D11_BLEND_OP_ADD;
bsd.RenderTarget[i].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
bsd.RenderTarget[i].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
bsd.RenderTarget[i].BlendOpAlpha = D3D11_BLEND_OP_ADD;
}
g_D3DDev->CreateBlendState(&bsd, &g_BlendState);
float blendFactors[4] = { 1, 1, 1, 1 };
g_D3DDevCtx->OMSetBlendState(g_BlendState, blendFactors, 0xffffffff);
// Depth-stencil state
D3D11_DEPTH_STENCILOP_DESC od;
od.StencilFunc = D3D11_COMPARISON_ALWAYS;
od.StencilFailOp = D3D11_STENCIL_OP_KEEP;
od.StencilPassOp = D3D11_STENCIL_OP_KEEP;
od.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
D3D11_DEPTH_STENCIL_DESC dsd;
dsd.DepthEnable = TRUE;
dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
dsd.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
dsd.StencilEnable = FALSE;
dsd.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
dsd.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
dsd.FrontFace = od;
dsd.BackFace = od;
g_D3DDev->CreateDepthStencilState(&dsd, &g_DepthStencilState);
g_D3DDevCtx->OMSetDepthStencilState(g_DepthStencilState, 0);
// Rasterizer state
D3D11_RASTERIZER_DESC rs;
ZeroMemory(&rs, sizeof(rs));
rs.FillMode = D3D11_FILL_SOLID;
rs.CullMode = D3D11_CULL_NONE;
rs.MultisampleEnable = (g_SwapChainDesc.SampleDesc.Count > 0);
g_D3DDev->CreateRasterizerState(&rs, &g_RasterState);
g_D3DDevCtx->RSSetState(g_RasterState);
// Init model rotation
Vector3 axis = {-1, 1, 0};
g_SpongeRotation = RotationFromAxisAngle(axis, FLOAT_PI/4);
return S_OK;
}
// Clean up D3D objects
void Cleanup()
{
#define RELEASE_CHECK(p) if (p) { ULONG rc = (p)->Release(); assert(rc == 0); (void)rc; (p) = NULL; }
if (g_D3DDevCtx)
g_D3DDevCtx->ClearState();
RELEASE_CHECK(g_BlendState);
RELEASE_CHECK(g_DepthStencilState);
RELEASE_CHECK(g_RasterState);
RELEASE_CHECK(g_VertexBuffer);
RELEASE_CHECK(g_IndexBuffer);
RELEASE_CHECK(g_ConstantBuffer);
RELEASE_CHECK(g_InputLayout);
RELEASE_CHECK(g_VertexShader);
RELEASE_CHECK(g_PixelShader);
RELEASE_CHECK(g_RenderTargetView);
RELEASE_CHECK(g_DepthStencilView);
if (g_SwapChainDesc.Windowed)
RELEASE_CHECK(g_SwapChain);
RELEASE_CHECK(g_D3DDevCtx);
RELEASE_CHECK(g_D3DDev);
}
// Called every time the application receives a message
LRESULT CALLBACK MessageProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// Send event message to AntTweakBar
if (TwEventWin(wnd, message, wParam, lParam))
return 0; // Event has been handled by AntTweakBar
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
BeginPaint(wnd, &ps);
EndPaint(wnd, &ps);
return 0;
}
case WM_SIZE: // Window size has been changed
if (g_D3DDev) // Resize D3D render target
{
// Release render target and depth-stencil view
ID3D11RenderTargetView *nullRTV = NULL;
g_D3DDevCtx->OMSetRenderTargets(1, &nullRTV, NULL);
if (g_RenderTargetView)
{
g_RenderTargetView->Release();
g_RenderTargetView = NULL;
}
if (g_DepthStencilView)
{
g_DepthStencilView->Release();
g_DepthStencilView = NULL;
}
if (g_SwapChain)
{
// Resize swap chain
g_SwapChainDesc.BufferDesc.Width = LOWORD(lParam);
g_SwapChainDesc.BufferDesc.Height = HIWORD(lParam);
g_SwapChain->ResizeBuffers(g_SwapChainDesc.BufferCount, g_SwapChainDesc.BufferDesc.Width,
g_SwapChainDesc.BufferDesc.Height, g_SwapChainDesc.BufferDesc.Format,
g_SwapChainDesc.Flags);
// Re-create a render target and depth-stencil view
ID3D11Texture2D *backBuffer = NULL, *dsBuffer = NULL;
g_SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBuffer);
g_D3DDev->CreateRenderTargetView(backBuffer, NULL, &g_RenderTargetView);
backBuffer->Release();
g_DepthStencilDesc.Width = g_SwapChainDesc.BufferDesc.Width;
g_DepthStencilDesc.Height = g_SwapChainDesc.BufferDesc.Height;
g_D3DDev->CreateTexture2D(&g_DepthStencilDesc, NULL, &dsBuffer);
g_D3DDev->CreateDepthStencilView(dsBuffer, NULL, &g_DepthStencilView);
dsBuffer->Release();
g_D3DDevCtx->OMSetRenderTargets(1, &g_RenderTargetView, g_DepthStencilView);
// Setup the viewport
D3D11_VIEWPORT vp;
vp.Width = (float)g_SwapChainDesc.BufferDesc.Width;
vp.Height = (float)g_SwapChainDesc.BufferDesc.Height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_D3DDevCtx->RSSetViewports(1, &vp);
}
// TwWindowSize has been called by TwEventWin, so it is not necessary to call it again here.
}
return 0;
case WM_CHAR:
if (wParam == VK_ESCAPE)
PostQuitMessage(0);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(wnd, message, wParam, lParam);
}
}
// Append vertices and indices of a cube to the index and vertex buffers.
// The cube has gradient ambient-occlusion defined per edge.
void AppendCubeToBuffers(std::vector<Vertex>& vertices, std::vector<unsigned int>& indices,
const Matrix4x4& xform, float aoRatio, const bool aoEdges[12],
const unsigned int faceColors[6])
{
unsigned int indicesOffset = (unsigned int)vertices.size();
// Fill vertex buffer
// Cube faces and edges numbering:
// __________ _____6____
// / /| /| /|
// / 4 / |<2 10 5 9 |
// /_________/ | /__|__2___/ 7
// | | 1| | |___4__|__|
// 3>| 0 | / 3 / 1 /
// | | / | 11 | 8
// |_________|/ |/____0___|/
// 5^
// Each face is split in a 3x3 grid, which gives 16 vertices per face and 3x3x2(=18) triangles per face.
// Ambient occlusion color is set for each of these vertices wrt aoEdges flags.
const float R = 0.5f; // unit cube radius
// the 4 corner coordinates for each of the 6 faces
const Vector3 A[6] = { {-R, -R, -R}, {+R, -R, -R}, {+R, -R, +R}, {-R, -R, +R}, {-R, +R, -R}, {-R, -R, -R} };
const Vector3 B[6] = { {+R, -R, -R}, {+R, -R, +R}, {-R, -R, +R}, {-R, -R, -R}, {+R, +R, -R}, {+R, -R, -R} };
const Vector3 C[6] = { {-R, +R, -R}, {+R, +R, -R}, {+R, +R, +R}, {-R, +R, +R}, {-R, +R, +R}, {-R, -R, +R} };
const Vector3 D[6] = { {+R, +R, -R}, {+R, +R, +R}, {-R, +R, +R}, {-R, +R, -R}, {+R, +R, +R}, {+R, -R, +R} };
// the 6 face normals
const Vector3 N[6] = { { 0, 0, -1}, {+1, 0, 0}, { 0, 0, +1}, {-1, 0, 0}, { 0, +1, 0}, { 0, -1, 0} };
// association between edge indices and the 6 faces
const int E[6][4] = { {0, 1, 2, 3}, {8, 7, 9, 1}, {4, 5, 6, 7}, {11, 3, 10, 5}, {2, 9, 6, 10}, {0, 8, 4, 11} };
int face, i, j;
float u, v;
bool ao;
Vertex vertex;
for (face = 0; face < 6; face++)
for (j = 0; j < 4; j++)
{
v = (j == 1) ? aoRatio : ((j == 2) ? 1.0f - aoRatio : j/3);
for (i = 0; i < 4; i++)
{
u = (i == 1) ? aoRatio : ((i == 2) ? 1.0f - aoRatio : i/3);
vertex.Position = (1.0f - v) * ((1.0f - u) * A[face] + u * B[face])
+ v * ((1.0f - u) * C[face] + u * D[face]);
vertex.Position = vertex.Position * xform;
vertex.Normal = N[face];
ao = (j == 0) && aoEdges[E[face][0]];
ao |= (i == 3) && aoEdges[E[face][1]];
ao |= (j == 3) && aoEdges[E[face][2]];
ao |= (i == 0) && aoEdges[E[face][3]];
#define DARKEN(r, s) ( (unsigned int)(float(r)*(s)) > 255 ? 255 : (unsigned int)(float(r)*(s)) )
#define DARKEN_COLOR(c, s) ( 0xff000000 | (DARKEN(((c)>>16)&0xff, s)<<16) | (DARKEN(((c)>>8)&0xff, s)<<8) | DARKEN((c)&0xff, s) )
vertex.AmbientColor = ao ? DARKEN_COLOR(faceColors[face], 0.75f) : faceColors[face];
vertices.push_back(vertex);
}
}
// Fill index buffer
// 3 indices per triangle, 2*3*3 triangles per faces, 6 faces.
// Vertex index numbering of each face:
// 12__13__14___15
// |'. | .'| .'|
// 8__'9'_10'__11
// | .'| .'| .'|
// 4'__5'__6'__7
// | .'| .'|'. |
// 0'__1'__2__'3
const unsigned short I[CUBE_FACE_TRIANGLE_COUNT][3] =
{
{0, 5, 4}, {0, 1, 5}, {1, 6, 5}, {1, 2, 6}, {3, 6, 2}, {3, 7, 6},
{4, 9, 8}, {4, 5, 9}, {5, 10, 9}, {5, 6, 10}, {6, 11, 10}, {6, 7, 11},
{8, 9, 12}, {9, 13, 12}, {9, 14, 13}, {9, 10, 14}, {10, 15, 14}, {10, 11, 15}
};
int tri;
for (face = 0; face < 6; face++)
for (tri = 0; tri < CUBE_FACE_TRIANGLE_COUNT; tri++)
for (i = 0; i < 3; i++)
indices.push_back(indicesOffset + I[tri][i] + 16*face); // 16 vertices per face
}
// Recursive function called to fill the vertex and index buffers with the cubes forming the Menger sponge.
void FillSpongeBuffers(int level, int levelMax, std::vector<Vertex>& vertices, std::vector<unsigned int>& indices,
const Vector3& center, bool aoEnabled, const bool aoEdges[12], const unsigned int faceColors[6])
{
float scale = pow(1.0f/3.0f, level);
if (level == levelMax)
{
float aoRatio = pow(3.0f, level) * 0.02f;
if (aoRatio > 0.4999f)
aoRatio = 0.4999f;
Matrix4x4 xform = Scale(scale) * Translation(center);
AppendCubeToBuffers(vertices, indices, xform, aoRatio, aoEdges, faceColors);
}
else
{
// Local function which applies AO in one direction
struct Local
{
static void ApplyAO(int i, int j, bool& e0, bool& e1, bool& e2, bool& e3)
{
if (i == -1 && j == 0) e0 = e1 = true;
if (i == +1 && j <= 0) e1 = false;
if (i == +1 && j >= 0) e0 = false;
if (i == +1 && j == 0) e2 = e3 = true;
if (i == -1 && j <= 0) e2 = false;
if (i == -1 && j >= 0) e3 = false;
if (j == -1 && i == 0) e1 = e2 = true;
if (j == +1 && i <= 0) e1 = false;
if (j == +1 && i >= 0) e2 = false;
if (j == +1 && i == 0) e0 = e3 = true;
if (j == -1 && i <= 0) e0 = false;
if (j == -1 && i >= 0) e3 = false;
}
};
bool aoEdgesCopy[12];
unsigned int faceColorsCopy[6];
int i, j, k, l;
for (i = -1; i <= 1; i++)
for (j = -1; j <= 1; j++)
for (k = -1; k <= 1; k++)
if ( !( (i == 0 && j == 0) || (i == 0 && k == 0) || (j == 0 && k == 0) ) )
{
float s = 1.0f/3.0f * scale;
Vector3 t = { center.v[0] + s * i, center.v[1] + s * j, center.v[2] + s * k };
for (l = 0; l < 12; l++)
aoEdgesCopy[l] = aoEdges[l];
if (aoEnabled)
{
Local::ApplyAO( i, j, aoEdgesCopy[8], aoEdgesCopy[9], aoEdgesCopy[10], aoEdgesCopy[11]); // z direction
Local::ApplyAO( i, k, aoEdgesCopy[1], aoEdgesCopy[7], aoEdgesCopy[5], aoEdgesCopy[3] ); // y direction
Local::ApplyAO(-k, j, aoEdgesCopy[0], aoEdgesCopy[2], aoEdgesCopy[6], aoEdgesCopy[4] ); // x direction
}
for (l = 0; l < 6; l++)
faceColorsCopy[l] = faceColors[l];
if (k == +1) faceColorsCopy[0] = COLORS[level+1];
if (i == -1) faceColorsCopy[1] = COLORS[level+1];
if (k == -1) faceColorsCopy[2] = COLORS[level+1];
if (i == +1) faceColorsCopy[3] = COLORS[level+1];
if (j == -1) faceColorsCopy[4] = COLORS[level+1];
if (j == +1) faceColorsCopy[5] = COLORS[level+1];
FillSpongeBuffers(level + 1, levelMax, vertices, indices, t, aoEnabled, aoEdgesCopy, faceColorsCopy);
}
}
}
// Build sponge vertex and index buffers
HRESULT BuildSponge(int levelMax, bool aoEnabled)
{
if (g_VertexBuffer)
g_VertexBuffer->Release();
if (g_IndexBuffer)
g_IndexBuffer->Release();
g_SpongeIndicesCount = 0;
// Fill vertex and index memory buffers
static std::vector<Vertex> vertices;
static std::vector<unsigned int> indices;
vertices.clear();
indices.clear();
bool aoEdges[12] = { false, false, false, false, false, false, false, false, false, false, false, false };
unsigned int faceColors[6] = { COLORS[0], COLORS[0], COLORS[0], COLORS[0], COLORS[0], COLORS[0] };
FillSpongeBuffers(0, levelMax, vertices, indices, Vector3::ZERO, aoEnabled, aoEdges, faceColors);
// Create vertex buffer
D3D11_BUFFER_DESC bd;
bd.Usage = D3D11_USAGE_IMMUTABLE;
bd.ByteWidth = (UINT)vertices.size() * sizeof(Vertex);
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
bd.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA initData;
initData.pSysMem = &vertices[0];
initData.SysMemPitch = 0;
initData.SysMemSlicePitch = 0;
HRESULT hr = g_D3DDev->CreateBuffer(&bd, &initData, &g_VertexBuffer);
if (FAILED(hr))
return hr;
// Create index buffer
bd.ByteWidth = (UINT)indices.size() * sizeof(unsigned int);
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
initData.pSysMem = &indices[0];
hr = g_D3DDev->CreateBuffer(&bd, &initData, &g_IndexBuffer);
if (FAILED(hr))
{
g_VertexBuffer->Release();
return hr;
}
g_SpongeIndicesCount = (unsigned int)indices.size();
return S_OK;
}
// Render the sponge
void DrawSponge()
{
if (g_SpongeIndicesCount == 0)
return;
// Set vertex buffer
UINT stride = sizeof(Vertex);
UINT offset = 0;
g_D3DDevCtx->IASetVertexBuffers(0, 1, &g_VertexBuffer, &stride, &offset);
// Set index buffer
g_D3DDevCtx->IASetIndexBuffer(g_IndexBuffer, DXGI_FORMAT_R32_UINT, 0);
// Set primitive topology
g_D3DDevCtx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Set shaders
g_D3DDevCtx->VSSetShader(g_VertexShader, NULL, 0);
g_D3DDevCtx->PSSetShader(g_PixelShader, NULL, 0);
// Render the primitives
g_D3DDevCtx->DrawIndexed(g_SpongeIndicesCount, 0, 0);
}
// Copy world/view/proj matrices and light parameters to shader constants
void SetShaderConstants(const Matrix4x4& world, const Matrix4x4& view, const Matrix4x4& proj)
{
D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT hr = g_D3DDevCtx->Map(g_ConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (SUCCEEDED(hr))
{
ShaderConstants *cst = (ShaderConstants *)mappedResource.pData;
cst->WorldViewProj = world * view * proj;
cst->WorldNorm = world;
cst->LightDir = (1.0f / Length(g_LightDir)) * g_LightDir;
cst->LightCoeff = 0.85f;
g_D3DDevCtx->Unmap(g_ConstantBuffer, 0);
}
g_D3DDevCtx->VSSetConstantBuffers(0, 1, &g_ConstantBuffer);
}
// Render a frame
void Render()
{
// Clear the back buffer
g_D3DDevCtx->ClearRenderTargetView(g_RenderTargetView, g_BackgroundColor);
g_D3DDevCtx->ClearDepthStencilView(g_DepthStencilView, D3D11_CLEAR_DEPTH, 1, 0);
// Set world/view/proj matrices and global shader constants
float aspectRatio = (float)g_DepthStencilDesc.Width / g_DepthStencilDesc.Height;
Matrix4x4 proj = Projection(FLOAT_PI/4, aspectRatio, 0.1f, 100.0f);
float dist = g_CamDistance + 0.4f;
Vector3 camPosInv = { dist * 0.3f, dist * 0.0f, dist * 2.0f };
Matrix4x4 view = Translation(camPosInv);
Matrix4x4 world = QuaternionToMatrix(g_SpongeRotation);
SetShaderConstants(world, view, proj);
// Draw the sponge
DrawSponge();
// Draw tweak bars
TwDraw();
// Present the information rendered in the back buffer to the front buffer (the screen)
g_SwapChain->Present(0, 0);
}
// Rotating sponge
void Anim()
{
static DWORD s_PrevTick = GetTickCount();
DWORD tick = GetTickCount(); // msec
float dt = float(tick - s_PrevTick) / 1000.0f; // sec
if (g_Animate && dt > 0 && dt < 0.2f)
{
Vector3 axis = Vector3::ZERO;
float angle = 0;
AxisAngleFromRotation(axis, angle, g_SpongeRotation);
if (Length(axis) < 1.0e-6f)
axis.v[1] = 1;
angle += g_AnimationSpeed * dt;
if (angle >= 2.0f*FLOAT_PI)
angle -= 2.0f*FLOAT_PI;
else if (angle <= 0)
angle += 2.0f*FLOAT_PI;
g_SpongeRotation = RotationFromAxisAngle(axis, angle);
}
s_PrevTick = tick;
}