Events in C++
This week I have decided to do some little work on Nova as I would like to use it for a game. But I’m currently missing a GUI. Since writing a GUI is often a pain while a nice GUI is its weight worth in gold it was worth to invest some time in it.
One of the very first things I have decided on is that I want a good and proper event management system. I like how it is done in C#
myButton.Click += new EventHandler(this.myButton_Click);
However something equally nice doesn't exist in plain C++, so I have decided to write one.
struct GuiEvent
{
bool cancel;
GuiEvent() : cancel(false) {}
};
class Button
{
public:
Event<GuiEvent> OnDown;
Event<GuiEvent> OnUp;
void FireClickEvent()
{
size_t cycle = 0; GuiEvent e;
while(OnDown.Fire(cycle, e)) {
if(e.cancel == true) return;
}
e = GuiEvent(); cycle = 0;
while(OnUp.Fire(cycle, e));
}
};
class Application : public BaseEvent::Receiver<Application>
{
Button m_StartButton;
public:
Application()
{
RegisterEvent<GuiEvent>(m_StartButton.OnDown, &Application::StartDown);
RegisterEvent<GuiEvent>(m_StartButton.OnUp, &Application::StartUp);
m_StartButton.FireClickEvent();
}
void StartUp(GuiEvent& eventParam)
{
std::cout << "The start button was released" << "\n";
}
void StartDown(GuiEvent& eventParam)
{
// If you set event param to true the release will never be called
//eventParam.cancel = true;
std::cout << "The start button is pressed down" << "\n";
}
};
int main(int argc, const char* argv[])
{
Application myApp;
return 0;
}
The above is rather primitive as I have written it quickly, but it looks nice and is code that is easy to understand. For the full source click here (C++ example of events)
Solved undesired template specification
A while ago (why do I always start with that) I wrote an blog entry about undesired template specification, what to encounter and how to work around it.
Anyway here is a quick definition of two structures I will be using in the article:
/**
* I will be using the following structures throughout the article
*/
template <typename T> struct Vector3<T> {
union {
struct { T x, y, z; }
T array[3];
};
Vector3<T>() : x(0), y(0), z(0) {}
Vector3<T>(T nx, T ny, T nz) : x(nx), y(ny), z(nz) {}
};
template <typename T> struct Vector4<T> {
union {
struct { T x, y, z, w; }
T array[4];
};
Vector4<T>() : x(0), y(0), z(0), w(0) {}
Vector3<T>(T nx, T ny, T nz, T nw) : x(nx), y(ny), z(nz), w(nw) {}
};
typedef Vector3<unsigned int> Vector3u;
typedef Vector3<float> Vector3f;
typedef Vector4<unsigned int> Vector4u;
typedef Vector4<float> Vector4f;
struct SVertex
{
Vector4f pos;
Vector3f normal;
Vector2f texcoord;
};
Because of Brick (3D random dungeon generator that takes design into account) I have noticed that there is one thing I do failry often:
Vector3f position; SVertex vertex; /* Need to draw it, so I store position in vertex */ vertex.pos = position; // ERROR! Trying to assign Vector3f to Vector4f!!
And finally I used defines to do the conversion for me:
#define VEC3TOVEC4(v) Vector4f((v).x, (v).y, (v).z, 0) #define VEC4TOVEC3(v) Vector3f((v).x, (v).y, (v).z) /* New code becomes */ vertex.pos = VEC3TOVEC4(position); // Works
Of course the above code is not nice to look at and I find it even plain ugly, but it works. However I don't want to do that in future projects (it feels like a hack), I would need to define something like that for every type (float, double, unsigned int et cetera) and on top of that it generates warnings:
Vector3<double> position; // Notice it is unsigned! SVertex vertex; /* Need to draw it, so I store position in vertex */ vertex.pos = VEC3TOVEC4(position); // Works, but generates warning about losing information
But I wouldn't be writing this post unless I tackled that little issue, and for once I can add that the solution is quite nice as well.
Vector3<double> position; // Notice it is unsigned! SVertex vertex; /* Need to draw it, so I store position in vertex */ vertex.pos.Set(position.array, 3); // Works, no errors and no warnings vertex.pos = Vector4f(position.array, 3); // Works fine as well
So what did I change?
Well, I used mutliple template (one for the class and for the function/constructor). This looks something like this:
template <typename T> struct Vector4
{
/* ... */
template <typename R>
explicit inline Vector4<T>(const R* values, const unsigned int elements/*=4*/);
template <typename R>
inline Vector4<T>& Set(const R* values, const unsigned int elements/*=4*/);
/* ... */
};
// Implementation
template <typename T> template <typename R>
Vector4<T>::Vector4(const R* values, unsigned int elements)
: x(elements > 0 ? (T)values[0] : 0), y(elements > 1 ? (T)values[1] : 0)
, z(elements > 2 ? (T)values[2] : 0), w(elements > 3 ? (T)values[3] : 0)
{
}
template <typename T> template <typename R>
Vector4<T>& Vector4<T>::Set(const R* values, const unsigned int elements)
{
x = (elements > 0 ? (T)values[0] : 0);
y = (elements > 1 ? (T)values[1] : 0);
z = (elements > 2 ? (T)values[2] : 0);
w = (elements > 3 ? (T)values[3] : 0);
return *this;
}
If you take a look at the code I think it is quite clear except that you might have some questions.
-
Q: Why do you use [cci_cpp]explicit[/cci_cpp] with the constructor?A: Because that prevents the implicit use of constructors. If I would allow it a [cci_cpp]Vector4u[/cci_cpp] could be implicit assigned to [cci_cpp]Vector3f[/cci_cpp], although it would be missing an argument. However I think that when you are converting, you should be somewhat aware of it, especially when it can be expensive.
-
Q: Why have you commented out the default value for [cci_cpp]elements[/cci_cpp]?A: Because you don't know how many elements there are in [cci_cpp]values[/cci_cpp] might be (there is a method to find out).
-
Q: So why don't you find out automatically and what is with those conditionals in the constructor?A: Those two are related. By telling it explicitly there is a real good chance that the compiler removes the conditionals, so the [cci_cpp] ( check ? true-value : false-value) [/cci_cpp] check will be removed.
Combo hit!! in code
I have always wondered how hard it would be to write a combo system. Not that hard I guess. And after a bit of morning programming I already got it working.
The only reason it took longer than anticipated was because of muscle memory. One of the features I wanted to test was the delay. For example you want to do the "asdf" combo, but you are for some reason not fast enough, than the combo should not start. Simulating this is easy, just begin the combo and stop somewhere for a second and then complete the combo. So "asd", pause and then "f".
However when I tried that for some reason the combo was sometimes completed. Only after adding the debug messages I noticed that I often automatically did complete the combo. The problem was muscle memory.
Anyway below is the code and if anyone wants to extend it (wrong next key, combo breakers, roman cancel, and follow-up combos) feel free to do so and let me know.
/*******************************************************************************
* The MIT License
*
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright (c) 2010 Wouter Lindenhof (http://limegarden.net)
*
* Demonstration of a simple ComboHit system
*******************************************************************************/
#include <Windows.h>
#include <iostream>
#include <vector>
#pragma comment(lib, "Winmm.lib")
#define DEBUGLOG 1 // Set to 0 to turn debug messages off
class KeyHit
{
public:
UINT m_Key;
UINT m_Delay;
UINT m_Waiting;
public:
KeyHit(UINT key, UINT delay) : m_Key(key), m_Delay(delay), m_Waiting(0) { }
};
class HitCombo
{
UINT m_SequenceIndex;
std::vector<KeyHit> m_Keys;
public:
HitCombo() : m_SequenceIndex(0) { }
void Cancel();
void Update(DWORD ms);
operator bool();
HitCombo& operator << (const KeyHit& hit);
};
int main(int argc, const char* argv[])
{
std::cout << "-----------------------------------------------" << std::endl;
std::cout << "This is a combo key tester: " << std::endl;
std::cout << "Press \"ASDF\" quickly to do a combo hit" << std::endl;
std::cout << "Press \"Wouter\" quickly to write my name" << std::endl;
std::cout << "Press SHIFT and then escape to quit" << std::endl;
std::cout << "-----------------------------------------------" << std::endl;
HitCombo QuitApplication, ComboHit, WouterCombo;
QuitApplication << KeyHit(VK_SHIFT, 250) << KeyHit(VK_ESCAPE, 250);
ComboHit << KeyHit('A', 250) << KeyHit('S', 250) << KeyHit('D', 250)
<< KeyHit('F', 250);
WouterCombo << KeyHit('W', 250) << KeyHit('O', 250) << KeyHit('U', 250)
<< KeyHit('T', 250) << KeyHit('E', 250) << KeyHit('R', 250);
DWORD lastTime = timeGetTime();
DWORD curTime = lastTime;
DWORD difference = 0;
while(true)
{
curTime = timeGetTime();
difference = curTime - lastTime;
lastTime = curTime;
if(ComboHit)
{
std::cout << "You did a combo hit!!" << std::endl;
WouterCombo.Cancel();
QuitApplication.Cancel();
}
if(WouterCombo)
{
std::cout << "You wrote 'Wouter', good for you!" << std::endl;
ComboHit.Cancel();
QuitApplication.Cancel();
}
if(QuitApplication)
{
std::cout << "You quit the application!" << std::endl;
ComboHit.Cancel();
WouterCombo.Cancel();
break;
}
ComboHit.Update(difference);
QuitApplication.Update(difference);
WouterCombo.Update(difference);
}
return 0;
}
void HitCombo::Cancel() {
m_SequenceIndex = 0;
}
// Implementation
HitCombo& HitCombo::operator <<(const KeyHit &hit) {
m_Keys.push_back(hit);
return *this;
}
HitCombo::operator bool() {
return m_SequenceIndex == m_Keys.size();
}
void HitCombo::Update(DWORD ms)
{
if(m_SequenceIndex < m_Keys.size())
{
KeyHit& hit = m_Keys[m_SequenceIndex];
hit.m_Waiting += ms;
if(GetAsyncKeyState(hit.m_Key) != 0)
{
if(hit.m_Waiting < hit.m_Delay)
{
hit.m_Waiting = 0;
m_SequenceIndex++;
#if DEBUGLOG = 1
std::cout << "you pressed key " << (char)hit.m_Key << std::endl;
#endif
}else
{
#if DEBUGLOG = 1
std::cout << "Delay too long" << std::endl;
#endif
hit.m_Waiting = 0;
m_SequenceIndex = 0;
}
if(m_SequenceIndex == 0)
{
hit.m_Waiting = 0;
}
}else if(m_SequenceIndex==0)
{
hit.m_Waiting = 0;
}else
{
if(hit.m_Waiting > hit.m_Delay)
{
m_SequenceIndex = 0;
hit.m_Waiting = 0;
#if DEBUGLOG
std::cout << "Delay too long" << std::endl;
#endif
}
}
}else
{
m_SequenceIndex = 0;
KeyHit& hit = m_Keys[m_SequenceIndex];
hit.m_Waiting = 0;
}
}
Rewriting examples: Direct3D tutorial 1 with D3DVERTEXELEMENT9
A while ago a friend asked me how to do something with Direct3D (better known as DirectX) about streams. I really enjoyed it since it was sometime I have worked with my favorite 3D API and so I explained the usage of VertexDeceleration by rewriting the very first tutorial of Direct3D of which you can see the result below
#include <windows.h>
#include <d3d9.h>
#pragma comment(lib, "d3d9.lib")
#include <d3dx9.h>
#pragma comment(lib, "d3dx9.lib")
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("DirectXStreamExample");
HWND hwnd;
MSG msg;
WNDCLASSEX wndclassex = {0};
wndclassex.cbSize = sizeof(WNDCLASSEX);
wndclassex.style = CS_HREDRAW | CS_VREDRAW;
wndclassex.lpfnWndProc = WndProc;
wndclassex.cbClsExtra = 0;
wndclassex.cbWndExtra = 0;
wndclassex.hInstance = hInstance;
wndclassex.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclassex.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclassex.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
wndclassex.lpszMenuName = NULL;
wndclassex.lpszClassName = szAppName;
wndclassex.hIconSm = wndclassex.hIcon;
if (!RegisterClassEx (&wndclassex))
{
MessageBox (NULL, TEXT ("RegisterClassEx failed!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindowEx (WS_EX_OVERLAPPEDWINDOW,
szAppName,
TEXT ("DirectXStreamExample"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
640,
480,
NULL,
NULL,
hInstance,
NULL);
ShowWindow (hwnd, iCmdShow);
UpdateWindow (hwnd);
// Now setup DirectX
IDirect3D9* d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS presentParameters;
memset(&presentParameters, 0, sizeof(D3DPRESENT_PARAMETERS));
presentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
presentParameters.BackBufferFormat = D3DFMT_UNKNOWN;
presentParameters.Windowed = true;
IDirect3DDevice9* device = 0;
d3d9->CreateDevice(0, D3DDEVTYPE_HAL, hwnd, D3DCREATE_MIXED_VERTEXPROCESSING, &presentParameters, &device);
// Create our custom vertex format
struct POSCOLORVERTEX
{
float X, Y, Z, W;
DWORD color;
};
POSCOLORVERTEX vertices[] = {
{150.0f, 50.0f, 0.5f, 1.0f, D3DCOLOR_XRGB(255,0,0)},
{250.0f, 250.0f, 0.5f, 1.0f, D3DCOLOR_XRGB(0,255,0)},
{ 50.0f, 250.0f, 0.5f, 1.0f, D3DCOLOR_XRGB(0,0,255)},
};
// Create a vertex decleration
// Think of a vertex decleration as an object that describes a single custom
// vertex.
// typedef struct _D3DVERTEXELEMENT9
// {
// WORD Stream; // Stream index
// WORD Offset; // Offset in the stream in bytes
// BYTE Type; // Data type
// BYTE Method; // Processing method
// BYTE Usage; // Semantics
// BYTE UsageIndex; // Semantic index
// } D3DVERTEXELEMENT9, *LPD3DVERTEXELEMENT9;
// Example 1: Tutorial 1 of the DirectX SDK
// using weighted position (aka screen coordinates) and colors but without
// using the Fixed Pipeline
D3DVERTEXELEMENT9 PosColorVertexElements[] = {
{0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0},
{0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
D3DDECL_END()
};
#if 0
// Example 2: If you want to add UV it would become something like this:
D3DVERTEXELEMENT9 PosColorUVVertexElements[] = {
{0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0},
{0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
{0, 20, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
D3DDECL_END()
};
// Example 3: And if you want UVs first
D3DVERTEXELEMENT9 PosColorNormalVertexElements[] = {
{0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
{0, 8, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0},
{0, 8+16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
D3DDECL_END()
};
// Example 4: And if you want to send two UV's
D3DVERTEXELEMENT9 PosColorNormalVertexElements[] = {
{0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0},
{0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
{0, 20, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, // Maps to TEXCOORD0 in shader
{1, 28, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, // Maps to TEXCOORD1 in shader
D3DDECL_END()
};
#endif
IDirect3DVertexDeclaration9* vertexDecleration = 0;
device->CreateVertexDeclaration(PosColorVertexElements, &vertexDecleration);
device->SetVertexDeclaration(vertexDecleration);
// Create vertex buffer
IDirect3DVertexBuffer9* vertexBuffer;
device->CreateVertexBuffer(
3*sizeof(POSCOLORVERTEX),
0,
0, // DON'T pass the FVF code if you are using vertex decleration
D3DPOOL_DEFAULT,
&vertexBuffer,
0);
// Fill the vertex buffer
void* pVertices = 0;
vertexBuffer->Lock(0, sizeof(vertices), (void**)&pVertices, 0);
memcpy(pVertices, vertices, sizeof(vertices));
vertexBuffer->Unlock();
bool keepRunning = true;
while(keepRunning)
{
// Render
device->Clear(0, 0, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );
device->BeginScene();
// Rendering of scene objects happens here
// 1. Set the vertex decleration
// 2. Set the stream source
device->SetVertexDeclaration(vertexDecleration);
device->SetStreamSource(0, vertexBuffer, 0, sizeof(POSCOLORVERTEX));
device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
// End the scene
device->EndScene();
device->Present(0,0,0,0);
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT) keepRunning = false;
else
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
}
vertexBuffer->Release();
vertexDecleration->Release();
device->Release();
d3d9->Release();
return msg.wParam;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage (0);
return (0);
}break;
}
return DefWindowProc (hwnd, message, wParam, lParam);
}
Donuts! (Procedural Torus in C++)

Recently I got a mail through the contact form of my website about texture mapping on torus (a donut) who said that [cci_cpp]D3DXCreateTorus[/cci_cpp] didn't not provide texture coordinates. The reason why DirectX and OpenGL (and GLUT) don't provide texture coordinates with these function is best seen when you generate a cube. A cube has 8 vertices, however when you add texture coordinates it will increase because otherwise they will share the same texture coordinate. With normals it is no problem (in fact you would prefer that) but if you have a dice (from 1 to 6) than each vertex would use a single texture coordinate for three sides. Just draw an unfolded dice on paper where each edge is connected, but does not cross another edge. It is simply not possible. These complexities are the reason why those procedural mesh functions are so simple. Adding these functionalities would increase complexity of the function and requires you to explain it. On top of that the developers have no idea how you are going to use that function. Maybe you don't want to use the legal texture range [0...1] but [0...3].
Anyway I have decided to take it on and here is the result. You can download the source code here: TorusDX.zip.I have not commented it, but it is pretty straight forward. The texture that I used is from Dilbert.
