sábado, 15 de marzo de 2014

Forward declarations with namespaces :S

To forward declare class type a in a namespace ns1:
namespace ns1
{
    class a;
}

lunes, 13 de enero de 2014

Compile-Time Assertions

From Modern C++ Design, S. Alexandrescu

It's a technique that relies in the fact that a zero-length array is ilegal:

#define STATIC_CHECK(expr) { char unnamed[(expr) ? 1 : 0]; }

To safe_reinterpret_cast(From from)
{
STATIC_CHECK(sizeof(From) <= sizeof(To));
return reinterpret_cast<To>(from);
}
...
void* somePointer = ...;
char c = safe_reinterpret_cast<char>(somePointer);
template <class To, class From>
Cons: The error message you receive is not terribly informative "Cannot create array of size zero"

martes, 17 de diciembre de 2013

Windows programming: Accessing raw input

There is a way to access every device's raw input. Basically there are three steps:


  1. Register to the input(s)
  2. Get the raw data.
  3. Process it

Register

To register to a device's input you have to call "RegisterInputDevice", which receives a    RAWINPUTDEVICE structure:

//http://msdn.microsoft.com/en-us/library/windows/desktop/ms645565(v=vs.85).aspxRAWINPUTDEVICE Rid[2];
Rid[0].usUsagePage = 0x01;
Rid[0].usUsage = 0x06; //keyboard?
Rid[0].dwFlags = RIDEV_INPUTSINK; //Receives the input even if the window is not currently active
Rid[0].hwndTarget = hWnd; //RIDEV_INPUTSINK needs a window handler, can be null otherwise
Rid[1].usUsagePage = 0x01;
Rid[1].usUsage = 0x02; //mouse?
Rid[1].dwFlags = RIDEV_NOLEGACY;   // adds HID mouse and also ignores legacy mouse messages
Rid[1].hwndTarget = 0;
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms645600(v=vs.85).aspxif( RegisterRawInputDevices(Rid, 2, sizeof(Rid[0])) == FALSE)
return FALSE;

Get the raw data


Simply calling GetRawInputData twice with the proper parameters, the first one to know the amount of data to be read and the second one to read the data:
//http://msdn.microsoft.com/en-us/library/windows/desktop/ms645596(v=vs.85).aspx
LPBYTE lpb = new BYTE[dwSize];
if (lpb == NULL)
{
return 0;
}
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize,
sizeof(RAWINPUTHEADER)) != dwSize )
OutputDebugString (TEXT("GetRawInputData does not return correct size !\n"));
RAWINPUT* raw = (RAWINPUT*)lpb;
 UINT dwSize;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));


Process the data

For example, I am going to write the keyboard info into a file and show some mouse info on the screen:

if (raw->header.dwType == RIM_TYPEKEYBOARD)
{
HRESULT hResult = StringCchPrintf(szTempOutput, STRSAFE_MAX_CCH, TEXT("%c"), raw->data.keyboard.VKey);
if (FAILED(hResult))
{
// TODO: write error handler
}
if (raw->data.keyboard.Flags == RI_KEY_MAKE) //Only saving the key down
{
fprintf (pFile, "%c", szTempOutput); OutputDebugString(szTempOutput);
}
}
else if (raw->header.dwType == RIM_TYPEMOUSE)
{
HRESULT hResult = StringCchPrintf(szTempOutput, STRSAFE_MAX_CCH, TEXT("Mouse: usFlags=%04x ulButtons=%04x usButtonFlags=%04x usButtonData=%04x ulRawButtons=%04x lLastX=%04x lLastY=%04x ulExtraInformation=%04x\r\n"),
raw->data.mouse.usFlags,
raw->data.mouse.ulButtons,
raw->data.mouse.usButtonFlags,
raw->data.mouse.usButtonData,
raw->data.mouse.ulRawButtons,
raw->data.mouse.lLastX,
raw->data.mouse.lLastY,
raw->data.mouse.ulExtraInformation);
if (FAILED(hResult))
{
       TODO: write error handler
}
OutputDebugString(szTempOutput);
}
delete[] lpb; 

jueves, 12 de diciembre de 2013

Normal, Tangent, Bitangent (Binormal) optimization

There are some scenarios where memory saving causes a performance improvement too, like handheld devices (in my case, the PS Vita).

One trick it was useful to improve the performance was to store the normal, tangent and bitangent (or binormal) in a memory-efficient way.

In the "normal" way, I'd use 3 arrays of 3 floats each one ( 3 arrays * 3 floats * 4 bytes per float). That makes 36 bytes.

The first optimization we could do would be to store the information using one byte per coordinate instead of floats (4 bytes). You probably are not going to need all that precision. We will need to clamp the values to the range (-127,+127) when storing the data and undo all this stuff when reading them.

But, do you really need to know the all those bitangent coordinates? The bitangent is the cross product between the normal and the tangent, so you only need to know which direction has the bitangent, the flip bit.


We could store that bit in the  tangent "w" coordinate.

This way you are going to need 2 arrays of 4 bytes = 8 bytes instead of the previous 36 bytes

Note the normal "w" coordinate is empty, so you could use it to save some stuff (in my case, the bone index).

Here you have the sample code:
normalData = new Vector4((0x7F * MathUtil.Clamp(currentNormal.X, -1.0f, 1.0f)), (0x7F * MathUtil.Clamp(currentNormal.Y, -1.0f, 1.0f)), (0x7F * MathUtil.Clamp(currentNormal.Z, -1.0f, 1.0f)), boneIndex);
                       
flip = (Vector3.Dot(Vector3.Cross(new Vector3(normalData.X, normalData.Y, normalData.Z), currentUTangent), currentVTangent) > 0) ? -1.0f : 1.0f; 
tangentFlipData = new Vector4((0x7F * MathUtil.Clamp(currentUTangent.X, -1.0f, 1.0f)), (0x7F * MathUtil.Clamp(currentUTangent.Y, -1.0f, 1.0f)), (0x7F * MathUtil.Clamp(currentUTangent.Z, -1.0f, 1.0f)), (0x7F * MathUtil.Clamp(flip, -1.0f, 1.0f)));
                           

domingo, 8 de diciembre de 2013

DLL and C#: DLLImport, Pointers, GCHandles, the "unsafe" context, paths, static constructors, ...

Some days ago I had to import a DLL Sony provides to optimize index and vertex buffers for the PS Vita.

But I only have a few months experience working with C# so a little bit of researching was required.

First of all I needed to declare the functions to import, I did it using DLLImport:

http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspx

using System;
using System.Runtime.InteropServices;
...
class Whatever
{
     [DllImport("geekStuff.dll"), 
     EntryPoint="MyFunction", 
     CallingConvention=CallingConvention.Cdecl]
     void MyFunction(void* vtx_buffer)
}

Another important thing: The parameters. My Dll was originally written in c++, so I needed write the proper types that matches the C++ ones. This conversion table was quite useful:


The functions I needed to use had void* pointers, to retreive them I used GCHandles:


Vector3 [] vtx_buffer;
...
GCHandle gch = GCHandle.Alloc(vtx_buffer);
void* p_vtx_buffer = (void*)gch.AddrOfPinnedObject();
MyFunction(p_vtx_buffer);
...

Edit: If you modify the data allocated by the GCHandle you will probably have to call gch = GCHandle.Alloc(vtx_buffer); before using it again. This was causing an "AccessViolationException" in my app.

Using void* will cause an error, because pointers can only use in an unsafe context. Enclosing the code in an unsafe block will fix the problem:


Another thing to comment is the path to the DLL. You can either specify the full path to the dll in DllImport or  set the Path environment variable by calling Environment.SetEnvironmentVariable. I choosed the second way:

string path = Environment.GetEnvironmentVariable("Path");
path += "C:" + Path.DirectorySeparatorChar.ToString()
             + "whatever" 
             + Path.DirectorySeparatorChar.ToString();
Environment.SetEnvironmentVariable(path);

I think the right place to do this is the "Whatever" Static Constructor:

using System;
using System.Runtime.InteropServices;
...
class Whatever
{
     [DllImport("geekStuff.dll"), 
     EntryPoint="MyFunction", 
     CallingConvention=CallingConvention.Cdecl]
     void MyFunction(void* vtx_buffer)
     static Whatever()
     {
          string path = Environment.GetEnvironmentVariable("Path");
          path += "C:" + Path.DirectorySeparatorChar 
                       + "whatever" 
                       + Path.DirectorySeparatorChar;
          Environment.SetEnvironmentVariable(path);
     }
}

Now the only problem is the huge crash is happening somewhere in the code :o) I hope to find it tomorrow morning...

New stuff!

I have been away for a while!

I moved to the UK to work with another company: Climax Studios. It is very exciting, I am working on a PS Vita videogame, learning lots of new things and meeting great, awesome programmers.

I would like to post new things in the following days, I have been very busy but I have a list with the things I would like to share:

  • DLL's and C#
  • Bounding spheres.
  • Normal, Tangent and Bitangent optimization.
  • Binary trees.
See you in a while!