( Windows ) Tablet support - Wacom, etc.

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
Saku
Posts: 158
Joined: Wed Jan 05, 2005 8:48 am
Location: Denmark

( Windows ) Tablet support - Wacom, etc.

Post by Saku »

I'm guessing we're a mere handful that will ever need this but never the less here goes.

Messing with my Wacom tablet and Irrlicht I found that there weren't any simple ways to grab any kind of additional tablet data ( Pressure, tilt, etc. ) in Irr.
Using the standard WinTab API, there are basically two ways to access this data.
The first approach is calling a function along the line of getMessage() to read pending messages from the buffer.
The second approach is much like you would make a global key-hook. You basically your register thread at the system to make it pass all event to your message handler.

Since I wanted to make things as easy as possible, I decided to go with the second approach.
After a bit of research I easily managed to integrate WinTab into Irrlicht.

Lets start with the ingredients:
1 Irrlicht ( You should have this already )
1 WinTab ( Can be found here: http://www.wacomeng.com/devsupport/down ... kit126.zip )

And now for mixing it all together.

Lets start by making a folder name wintab in your irrlicht source folder.
Then put in the wintab.h, pktdef.h and wintab32.lib into the folder.
This is primarily for the sake of everything being in a place where we can find it. I you want to experiment here, go ahead.

Next we go over to the Irrlicht Include folder to do some initial preparation for our tablet event packets.
Open the IEventReceiver.h file.
At the top we start by adding a new enum, to make it detectable that we've received a tablet event:

Code: Select all

namespace irr
{
	//! Enumeration for all event types there are.
	enum EEVENT_TYPE
	{

		[...]

		//! A user event with user data. This is not used by Irrlicht and can be used
		//! to send user specific data though the system.
		EET_USER_EVENT,

		//! A Tablet event. Hurray ! :D
		EET_TABLET_INPUT_EVENT
	};
Note: We add the new enum at the end to prevent collision with other events in the case the DLL is used with a program that assumes we're using an original build.

Before closing IEventReceiver.h we go down a bit further and find the declaring of the struct holding the event data.
Here we add our own struct to keep the data while the event is passed through the system.

Code: Select all

struct SEvent
{
	EEVENT_TYPE EventType;

	union
	{

		[...]

		struct
		{
			//! Some user specified data as int
			s32 UserData1;

			//! Another user specified data as int
			s32 UserData2;

			//! Some user specified data as float
			f32 UserData3;
		} UserEvent;

		struct
		{
		    //! Cursor position on the screen
			s32 X;
			s32 Y;
			s32 Z;

			//! Cursor type ( Stylus, Eraser ... )
			u32 Cursor;

			//! Buttons pressed
			u32 Buttons;

			//! Normal and Tangent Pressure
			u32 NormalPressure;
			u32 TangentPressure;

			//! Orientation - data structure specifies the orientation of the cursor with respect to the tablet.
			struct
			{
                s32 Azimuth;
                s32 Altitude;
                s32 Twist;
			} Orientation;

		} TabletInput;
	};
};
Note: the order doesn't matter here, but I put it TabletInput struct next to the UserEvent for the sake of organization.

Next we go to CIrrDeviceW32.h in the Irrlicht Source folder.
At the top we include the WinTab files and declare a tablet handle, and the content of our original packet ( what we what the system to tell us about the tablet ).

Code: Select all

// Message packet structure
#define PACKETDATA ( PK_X | PK_Y | PK_Z | PK_BUTTONS | PK_CURSOR | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION )
#define PACKETMODE 0

#include "wintab/wintab.h"
#include "wintab/pktdef.h"

HCTX		hTab = NULL;         /* Handle for Tablet Context */
Now go to the CIrrDeviceW32.cpp - that's the .CPP file - and add a new case to the switch in the message handler:

Code: Select all

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	[...]

	switch (message)
	{

	[...]

	case WT_PACKET:
        PACKET pkt;
		if( WTPacket( (HCTX)lParam, wParam, &pkt ) )
		{
            event.EventType = irr::EET_TABLET_INPUT_EVENT;
            event.TabletInput.X = (irr::u32)pkt.pkX;
            event.TabletInput.Y = (irr::u32)pkt.pkY;
            event.TabletInput.Z = (irr::u32)pkt.pkZ;
            event.TabletInput.Cursor = pkt.pkCursor;
            event.TabletInput.Buttons = pkt.pkButtons;
            event.TabletInput.NormalPressure = pkt.pkNormalPressure;
            event.TabletInput.TangentPressure = pkt.pkTangentPressure;
            event.TabletInput.Orientation.Azimuth = pkt.pkOrientation.orAzimuth;
            event.TabletInput.Orientation.Altitude = pkt.pkOrientation.orAltitude;
            event.TabletInput.Orientation.Twist = pkt.pkOrientation.orTwist;

            dev = getDeviceFromHWnd(hWnd);
            if( dev )
                dev->postEventFromUser(event);
		}
		break;
With the hooks set out, now all we need to do is lure the fish in :)
The last thing we need to do is add the part that registers the thread to receive the tablet input packets form the system.
So, now scroll down a bit further and add:

Code: Select all

//! constructor
CIrrDeviceWin32::CIrrDeviceWin32(video::E_DRIVER_TYPE driverType,
				 core::dimension2d<s32> windowSize,
				 u32 bits, bool fullscreen,
				 bool stencilbuffer, bool vsync,
				 bool antiAlias,
				 bool highPrecisionFPU,
				 IEventReceiver* receiver,
				 HWND externalWindow,
				 const char* version)
: CIrrDeviceStub(version, receiver), HWnd(0), ChangedToFullScreen(false),
	FullScreen(fullscreen), IsNonNTWindows(false), Resized(false),
	ExternalWindow(false), Win32CursorControl(0)
{

	[ ... ]

	// attach external window
	if (externalWindow)
	{
		HWnd = externalWindow;
		RECT r;
		GetWindowRect(HWnd, &r);
		windowSize.Width = r.right - r.left;
		windowSize.Height = r.bottom - r.top;
		fullscreen = false;
		ExternalWindow = true;
	}

	[ START ]

	LOGCONTEXT      lcMine;           /* The context of the tablet */
	AXIS            TabletX, TabletY; /* The maximum tablet size */

	/* get default region */
	WTInfo( WTI_DEFCONTEXT, 0, &lcMine );

	/* modify the digitizing region */
	wsprintf( lcMine.lcName, "Irrlicht Digitizing %x", hInstance );

	lcMine.lcOptions |= CXO_MESSAGES | CXO_SYSTEM;
//	lcMine.lcMsgBase = WT_DEFBASE;
	lcMine.lcPktData = PACKETDATA;
	lcMine.lcPktMode = PACKETMODE;
	lcMine.lcMoveMask = PACKETDATA;
	lcMine.lcBtnUpMask = lcMine.lcBtnDnMask;

    /* Set the entire tablet as active */
	WTInfo(WTI_DEVICES,DVC_X,&TabletX);
	WTInfo(WTI_DEVICES,DVC_Y,&TabletY);

	lcMine.lcInOrgX = 0;
	lcMine.lcInOrgY = 0;
	lcMine.lcInExtX = TabletX.axMax;
	lcMine.lcInExtY = TabletY.axMax;

    /* output the data in screen coords */
	lcMine.lcOutOrgX = lcMine.lcOutOrgY = 0;
	lcMine.lcOutExtX = GetSystemMetrics(SM_CXSCREEN);
    /* move origin to upper left */
	lcMine.lcOutExtY = -GetSystemMetrics(SM_CYSCREEN);

	/* open the region */
	hTab = WTOpen( HWnd, &lcMine, TRUE );

	[ END ]

	// create cursor control

	Win32CursorControl = new CCursorControl(windowSize, HWnd, fullscreen);
	CursorControl = Win32CursorControl;

	[ ... ]
}
Now all you need to do is bake the cake :)
Compile Irrlicht and include the updated include files in your project and you should be able to receive the new event packets just as well as any other kind.

I'm getting sleepy now, so I'll leave it at that, but if you see any mistakes please leave a note, and I'll update the post.
I'll probably be back in a tad to update it a bit further anyways.

Oh yeah, unfortunately the WinTab API only seems to target the Windows platform, so no Linux support yet.
If you - unlike me - are a Linux guy/girl and have a tablet or just for some reason know how to implement Linux support, please share with us :)

Enjoy!
/Saku ( aka Wice on #Irrlicht ;) )

04-10-2007: Corrected some fatal mistakes and typos in the guide - everything should be able to compile and link now..
Last edited by Saku on Thu Oct 04, 2007 10:22 am, edited 7 times in total.
Call me Wice, Miami Wice!
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Maybe we need a better input system someday. It would be nice to register input devices with Irrlicht by adding a new handler somehow. I recently read about Linux joystick support...
Anyway, nice thing. I like new input methods :D
TheGameMaker
Posts: 275
Joined: Fri May 12, 2006 6:37 pm
Location: Germany

Post by TheGameMaker »

that realy cool!! I own a Wacom and have to say I allways wanted to use it with my own proggies^^ THX
Post Reply