[How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-03-12

Problems building or running the engine, queries about how to use features etc.
Post Reply
iblues1976
Gnome
Posts: 379
Joined: Fri Sep 16, 2011 4:54 pm
x 10

[How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-03-12

Post by iblues1976 »

LAST EDIT: FEB - 3 - 2012
Hi,
Feel free to use this in any way. If you put this in a hard copy settings (book, article) an acknowledgment to me and to the sources I have quoted would be nice. If you are working with Input, 3D Navigation or 3D Interfaces, please PM me to see if we can collaborate in publications.

I have a 3D Connexion (3DX) which provides 6DOF. While I'm not so happy with the movement as of now, I want to share my code. Next week I will try get the wii mote working... At the end, I will add all of it to my upcoming tutorial, "First Person Shooter" tutorial that I'm preparing. So far, I have the gamepad using direct input and 3dx navigator working. I will be adding some additional input devices.
Image

This is a "HOW TO" / Tutorial that I will be soon adding to the wiki, once I get feedback from the community. Please note that this is not the only option to read the data from the navigator or the only way to integrate it with windows. I'm looking for an alternative which I will post, to allow to have it as part of my game loop as opposed to have it signal every time there is data.

It is always important to give credit where credit's due. Here it goes:

The code here is a combination of
  1. Kojack code from one of his many post.
  2. C++ Professional Book GREAT BOOK
  3. sample called WM_INPUT found in the example ftp site of 3dx. To access the FTP Site, username is examples and password is the same (examples)
  4. my own code inside (do I? :? )
  5. I have it working in the Advanced Framework provided here in the ogre tutorials.
HOW TO SET UP A 3DX Navigator
========================================

You will need to use the observer design pattern. Here is the code

Code: Select all

//Win3DXOgreListener.h
#ifndef WIN3DXOGRELISTENER_H
#define WIN3DXOGRELISTENER_H
#include <OgreVector3.h> 
#include <OgreVector4.h>

typedef struct DOF6 {
	int tx;
	int ty;
	int tz;
	int rx;
	int ry;
	int rz;
} DOFSix;

typedef struct BDATA {
	unsigned char p3;
	unsigned char p2;
	unsigned char p1;
} BData;

class Win3DXOgreListener
{
public: 
	virtual bool handleNavigator(int message,DOFSix & D,BData & B) = 0;
	
	
};
#endif

Code: Select all

//Win3DXOgreEventRegistry.h
#ifndef WINSDKOGREEVENTREGISTRY_H
#define WINSDKOGREEVENTREGISTRY_H

#include "Win3DXOgreListener.h"
#include <vector>
#include <map>

class Win3DXOgreEventRegistry
{
    public:
        static bool registerListener(int inMessage, Win3DXOgreListener* inListener);
		
        static bool signalNavigator(int inMessage, int outMessage,DOFSix & D,BData & B);
		
		enum MESSAGES {M_UNKNOWN = 100,M_BUTTON = 101,M_NAVIGATOR = 102};
		enum REGISTER {LISTEN_ALL=0};

    protected:
        static std::map<int, std::vector<Win3DXOgreListener*>> sListenerMap;
		
};
#endif

Code: Select all

// Win3DXOgreEventRegistry.cpp
// Implements the EventRegistry class
#include "Win3DXOgreEventRegistry.h"
#include <iostream>

using namespace std;

// Define the static map.
map<int, vector<Win3DXOgreListener*>> Win3DXOgreEventRegistry::sListenerMap;


bool Win3DXOgreEventRegistry::registerListener(int inMessage, Win3DXOgreListener* inListener)
{
	if (inMessage != REGISTER::LISTEN_ALL) return false;

	sListenerMap[inMessage].push_back(inListener);
	return true;
}

bool Win3DXOgreEventRegistry::signalNavigator(int inMessage, int outMessage,DOFSix & D,BData & B)
{ 
    // Check to see if the message has *any* listeners. This check is required
    // because otherwise, accessing sListenerMap[inMessage] would create
    // a new entry when it’s not yet in the map. See Chapter 12.
	if (inMessage != REGISTER::LISTEN_ALL || sListenerMap.find(inMessage) == sListenerMap.end())
		return false;
	for (auto iter = sListenerMap[inMessage].begin();
		iter != sListenerMap[inMessage].end(); ++iter) {
			(*iter)->handleNavigator(outMessage, D,  B);

	}
	return true;
}
Now I will explain the code for the observer pattern. Originally, I created the listener to work with Windows SDK (WM_INPUT event). But I think I can remove the Win part of the name of the files and classes, because I believe that I can standarize this for windows,mac and linux. However, as of now, I have only tested with Windows and I have not downloaded the SDK or seen examples in how to work with 3DX in mac or linux.
The code is simple. First, Win3DXOgreListener class is used to be class that will be inherited to handle the messages. For example, I have it added to AdvancedOgreFramework and AppState to be able to handle this

Code: Select all

class OgreFramework : public Ogre::Singleton<OgreFramework>, OIS::KeyListener, OIS::MouseListener ,	Win3DXOgreListener
{
...
bool handleNavigator(int message,DOFSix & D, BData & B);
...
};
I have also added the handling to my GameState,MenuState and PauseState

Code: Select all

class GameState : public AppState
{
...
bool handleNavigator(int message,DOFSix & D, BData & B);
};
later Win3DXOgreEventRegistry allows to register all listener and handle the messages. Notice that there is an InMessage and OutMessage. I'm only sending the handler the outMessage. The inMessage I have it in case I want to expand this later, for other 3DX devices or additional functionality. Maybe different handlers... For now, inMessage is always LISTEN_ALL (0), which is used for the map. Notice also that the map is not needed, since we could have used another data structure. But I want to keep like this since I may want to expand this later.

Now the code that in my case, I placed in top of the file AdvancedFramework.cpp
the first part of the code, shows what is needed to collect information from raw devices, but in specific for the 3dx navigator.

Code: Select all



//***************************************************************************
//3DX Specific
//***************************************************************************
// ----------------  RawInput ------------------
#include <crtdbg.h>
#include <windows.h>

// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
//****************************************************************************
//
PRAWINPUTDEVICELIST g_pRawInputDeviceList;
PRAWINPUTDEVICE     g_pRawInputDevices;
int                 g_nUsagePage1Usage8Devices;

BOOL InitRawDevices(void)
{
		// Find the Raw Devices
	UINT nDevices;
	// Get Number of devices attached
	if (GetRawInputDeviceList(NULL, &nDevices, sizeof(RAWINPUTDEVICELIST)) != 0)
	{ 
		OgreFramework::getSingletonPtr()->m_pLog->logMessage("[3DX] WARNING: No RawInput devices attached");
		return FALSE;
	}
	// Create list large enough to hold all RAWINPUTDEVICE structs
	if ((g_pRawInputDeviceList = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * nDevices)) == NULL)
	{
		OgreFramework::getSingletonPtr()->m_pLog->logMessage("[3DX] Error: malloc RAWINPUTDEVICELIST");
		return FALSE;
	}
	// Now get the data on the attached devices
	if (GetRawInputDeviceList(g_pRawInputDeviceList, &nDevices, sizeof(RAWINPUTDEVICELIST)) == -1) 
	{
		OgreFramework::getSingletonPtr()->m_pLog->logMessage("[3DX] Error: GetRawInputDeviceList");
		return FALSE;
	}

	g_pRawInputDevices = (PRAWINPUTDEVICE)malloc( nDevices * sizeof(RAWINPUTDEVICE) );
	g_nUsagePage1Usage8Devices = 0;

	// Look through device list for RIM_TYPEHID devices with UsagePage == 1, Usage == 8
	for(UINT i=0; i<nDevices; i++)
	{
		if (g_pRawInputDeviceList[i].dwType == RIM_TYPEHID)
		{
			UINT nchars = 300;
			TCHAR deviceName[300];
			if (GetRawInputDeviceInfo( g_pRawInputDeviceList[i].hDevice,
									   RIDI_DEVICENAME, deviceName, &nchars) >= 0)
									   fprintf(stderr, "Device[%d]: handle=0x%x name = %S\n", i, g_pRawInputDeviceList[i].hDevice, deviceName);

			RID_DEVICE_INFO dinfo;
			UINT sizeofdinfo = sizeof(dinfo);
			dinfo.cbSize = sizeofdinfo;
			if (GetRawInputDeviceInfo( g_pRawInputDeviceList[i].hDevice,
									   RIDI_DEVICEINFO, &dinfo, &sizeofdinfo ) >= 0)
			{
				if (dinfo.dwType == RIM_TYPEHID)
				{
					RID_DEVICE_INFO_HID *phidInfo = &dinfo.hid;
					fprintf(stderr, "VID = 0x%x\n", phidInfo->dwVendorId);
					fprintf(stderr, "PID = 0x%x\n", phidInfo->dwProductId);
					fprintf(stderr, "Version = 0x%x\n", phidInfo->dwVersionNumber);
					fprintf(stderr, "UsagePage = 0x%x\n", phidInfo->usUsagePage);
					fprintf(stderr, "Usage = 0x%x\n", phidInfo->usUsage);

					// Add this one to the list of interesting devices?
					// Actually only have to do this once to get input from all usage 1, usagePage 8 devices
					// This just keeps out the other usages.
					// You might want to put up a list for users to select amongst the different devices.
					// In particular, to assign separate functionality to the different devices.
					if (phidInfo->usUsagePage == 1 && phidInfo->usUsage == 8)
					{
						g_pRawInputDevices[g_nUsagePage1Usage8Devices].usUsagePage = phidInfo->usUsagePage;
						g_pRawInputDevices[g_nUsagePage1Usage8Devices].usUsage     = phidInfo->usUsage;
						g_pRawInputDevices[g_nUsagePage1Usage8Devices].dwFlags     = 0;
						g_pRawInputDevices[g_nUsagePage1Usage8Devices].hwndTarget  = NULL;
						g_nUsagePage1Usage8Devices++;
					}
				}
			}
		}
	}

	// Register for input from the devices in the list
	if (RegisterRawInputDevices( g_pRawInputDevices, g_nUsagePage1Usage8Devices, sizeof(RAWINPUTDEVICE) ) == FALSE )
	{
		OgreFramework::getSingletonPtr()->m_pLog->logMessage("[3DX] Error: calling RegisterRawInputDevices");
		return FALSE;
	}

	return TRUE;
}

void FreeRawInputDevices(void)
{
	free(g_pRawInputDeviceList);
}

void ProcessWM_INPUTEvent( LPARAM lParam )
{
	#ifdef SHOW_DETAILS
	fprintf(stderr, "WM_INPUT lParam=0x%x\n", lParam );
	#endif

	RAWINPUTHEADER header;
	UINT size = sizeof(header);
	if ( GetRawInputData( (HRAWINPUT)lParam, RID_HEADER, &header,  &size, sizeof(RAWINPUTHEADER) ) == -1)
	{
		OgreFramework::getSingletonPtr()->m_pLog->logMessage("[3DX] Error: GetRawInputData(RID_HEADER)");
		
		return;
	}
	else
	{
		fprintf(stderr, "rawEvent.header: hDevice = 0x%x\n", header.hDevice );
	}

	// Set aside enough memory for the full event
	size = header.dwSize;
	LPRAWINPUT event = (LPRAWINPUT)malloc(size);
	if (GetRawInputData( (HRAWINPUT)lParam, RID_INPUT, event, &size, sizeof(RAWINPUTHEADER) ) == -1)
	{
		OgreFramework::getSingletonPtr()->m_pLog->logMessage("[3DX] Error: GetRawInputData(RID_INPUT)");
		
		return;
	}
	else
	{
		if (event->header.dwType == RIM_TYPEHID)
		{
			static BOOL bGotTranslation = FALSE, 
				        bGotRotation    = FALSE;
			static int all6DOFs[6] = {0};
			LPRAWHID pRawHid = &event->data.hid;

			#ifdef SHOW_DETAILS
			fprintf(stderr, "rawInput count: %d\n", pRawHid->dwCount);
			fprintf(stderr, "          size: %d\n", pRawHid->dwSizeHid);
			for(UINT i=0; i<pRawHid->dwSizeHid; i++)
			{
				fprintf(stderr, "%d ", pRawHid->bRawData[i] );
			}
			_RPT0( _CRT_WARN, "\n" );
			#endif

			// Translation or Rotation packet?  They come in two different packets.
			if (pRawHid->bRawData[0] == 1) // Translation vector
			{
				all6DOFs[0] = (pRawHid->bRawData[1] & 0x000000ff) | ((signed short)(pRawHid->bRawData[2]<<8) & 0xffffff00); 
				all6DOFs[1] = (pRawHid->bRawData[3] & 0x000000ff) | ((signed short)(pRawHid->bRawData[4]<<8) & 0xffffff00); 
				all6DOFs[2] = (pRawHid->bRawData[5] & 0x000000ff) | ((signed short)(pRawHid->bRawData[6]<<8) & 0xffffff00);
				bGotTranslation = TRUE;
			}
			else if (pRawHid->bRawData[0] == 2) // Rotation vector
			{
				all6DOFs[3] = (pRawHid->bRawData[1] & 0x000000ff) | ((signed short)(pRawHid->bRawData[2]<<8) & 0xffffff00); 
				all6DOFs[4] = (pRawHid->bRawData[3] & 0x000000ff) | ((signed short)(pRawHid->bRawData[4]<<8) & 0xffffff00); 
				all6DOFs[5] = (pRawHid->bRawData[5] & 0x000000ff) | ((signed short)(pRawHid->bRawData[6]<<8) & 0xffffff00);
				bGotRotation = TRUE;
			}
			else if (pRawHid->bRawData[0] == 3) 
			{// Buttons (display most significant byte to least)
				DOFSix D; 
				BData B;
				//
				D.tx = 0;
				D.ty = 0;
				D.tz = 0; 
				D.rx = 0;
				D.ry = 0;
				D.rz = 0;
				

				B.p3 = (unsigned char)pRawHid->bRawData[3];
				B.p2 = (unsigned char)pRawHid->bRawData[2];
				B.p1 = (unsigned char)pRawHid->bRawData[1];

				Win3DXOgreEventRegistry::signalNavigator(Win3DXOgreEventRegistry::LISTEN_ALL,
														Win3DXOgreEventRegistry::M_BUTTON,
														 D,B); 

				fprintf(stderr, "Button mask: %.2x %.2x %.2x\n",(unsigned char)pRawHid->bRawData[3],(unsigned char)pRawHid->bRawData[2],(unsigned char)pRawHid->bRawData[1]);
			}


			if (bGotTranslation && bGotRotation)
			{
				
				DOFSix D; 
				BData B;
				//
				D.tx = all6DOFs[0];
				D.ty = all6DOFs[1];
				D.tz = all6DOFs[2];
				D.rx = all6DOFs[3];
				D.ry = all6DOFs[4];
				D.rz = all6DOFs[5];			
		

				B.p3 = 0;
				B.p2 = 0;
				B.p1 = 0;
				
				Win3DXOgreEventRegistry::signalNavigator(Win3DXOgreEventRegistry::LISTEN_ALL,
														 Win3DXOgreEventRegistry::M_NAVIGATOR,
														 D,B); 

				bGotTranslation = bGotRotation = FALSE;

				fprintf(stderr, "RAW DATA all6DOFs: T=[%d,%d,%d] ", all6DOFs[0], all6DOFs[1], all6DOFs[2]);
				fprintf(stderr,           " R=[%d,%d,%d]\n", all6DOFs[3], all6DOFs[4], all6DOFs[5]);
			}
		}
	}

}
You will notice above, that in ProcessWM_INPUT method I signal the event with signalNavigator

and later the code that kojack had provided. which I added in AdvancedFramework.cpp as well, right below the other code.

Code: Select all

//***************************************************************************
//Handle raw messages for 3dx and later called ogre. 
//***************************************************************************

WNDPROC g_ogreWinProc2 = NULL; 
LRESULT APIENTRY SubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{   
   switch(uMsg)
   {
      // handle your WM_**** stuff here
	   case WM_INPUT:
			ProcessWM_INPUTEvent( lParam );
			break;
   }
   return CallWindowProc(g_ogreWinProc2, hwnd, uMsg, wParam, lParam);
}
in the advancedFramework.h , under public I added bool m_bRawDevicesOn; this is just to know if the device is on while the initialization. The code may have to change (which I may do later, to be able to detect the device at any time of plugin.)
in the destructor of advancedFramework class, I have added at the end

Code: Select all

if (m_bRawDevicesOn) 
		FreeRawInputDevices();
and at the end of my initialization in initOgre of AdvancedFramework I added, the following code

Code: Select all

	//init Navigator
	if (!InitRawDevices())
	{
		OgreFramework::getSingletonPtr()->m_pLog->logMessage("[3DX] Error InitRawDevices()");
		m_bRawDevicesOn = false;
	}
	else
	{
		m_bRawDevicesOn = true; 
		HWND hwnd;
		OgreFramework::getSingletonPtr()->m_pRenderWnd->getCustomAttribute("WINDOW", (void*)&hwnd);
		g_ogreWinProc2 = (WNDPROC) SetWindowLong(hwnd, GWL_WNDPROC, (LONG) SubclassProc); 
		Win3DXOgreEventRegistry::registerListener(Win3DXOgreEventRegistry::LISTEN_ALL,
												  this); 
	}
***ADDED MARCH - 3 - 2012 Here is the partial code of GameState.cpp. Here I handle the event (I need to fix it a bit)

Code: Select all

bool GameState::handleNavigator(int message,DOFSix & D, BData & B)
{
	if (message == Win3DXOgreEventRegistry::M_NAVIGATOR)
	{
		
		long sumAll = D.tx+D.ty+D.tz+D.rx+D.ry+D.rz;
		//3DX keeps sending signal for a little while ...
		if (sumAll == 0) 
			return true; 


		if (D.tx != 0) 
			m_TranslateVector.x = m_MoveScale * (float)D.tx * m_Factor3DX;
		if (D.tz != 0) 
			m_TranslateVector.y = m_MoveScale * (float)D.tz * -m_Factor3DX;
		if (D.ty != 0)
			m_TranslateVector.z = m_MoveScale * (float)D.ty * m_Factor3DX;
		
		//yaw z axis ???
		//roll x axis ???
		//pitch y axis ???
		if (D.ry != 0) 
			m_pCamera->yaw(Degree(D.rz * -m_Factor3DX * 0.1)) ; 

		if (D.rx != 0) 
			m_pCamera->pitch(Degree(D.rx * -m_Factor3DX * 0.1));
		
		if (D.ry != 0) 
			m_pCamera->roll(Degree(D.ry * -m_Factor3DX * 0.1));

		moveCamera(); 
		fprintf(stderr,"M_NAVIGATOR T=[%d,%d,%d] R=[%d,%d,%d]\n",D.tx,D.ty,D.tz,D.rx,D.ry,D.rz); 
	
	}
	else if (message == Win3DXOgreEventRegistry::M_BUTTON)
	{
		//I'm only using navigator has two buttons 
		int bNum = B.p3 | B.p2 | B.p3;
		fprintf(stderr,"M_BUTTON Number = %d\n",bNum); 
	}
	else
	{
		fprintf(stderr,"Called without proper message???\n"); 
	}
	
	return true;
}
I will be updating this as I go on. I appreciate feedback so I can do a better article/howto/tutorial in the wiki. I have to find out how that is done, which I will do soon. I think is a great practice to add this smaller how to once is mature into the wiki how to, to gain experience which will help for my main tutorial.

My setup is Ogre 1.7.x (32 bit) and Windows 7 64 bit with Visual Studio 2010.

Important is to note that navigator translation X may not be the X in your screen and the same for the rest. Since the navigator has only two buttons, my buttons come as button 1 and button 2 (1 and 2) . Also know that the events for WM_INPUT when is not buttons, come in 2 separate events, that I combined into one.
I use fprintf for my console screen and the log only for important aspects of Ogre.

I hope you like this.

PS: Grammar correction are WELCOME!

************ MARCH - 03 - 2012
Three additional comments.
1) If you use the WM_INPUT, it is my understanding that you are not using their drivers. This means that if you make changes in their control panel application, none of this will matter. This is "RAW DATA"

2) This is how I currently handle the event .. it is not perfect. As I update this thread, I will update this part of the code as well. The code has been posted in the original message. Look for Partial GameState.cpp
3) I didn't mention anything about the two structs thare are used to passed the messages
typedef struct DOF6 {
int tx;
int ty;
int tz;
int rx;
int ry;
int rz;
} DOFSix;

typedef struct BDATA {
unsigned char p3;
unsigned char p2;
unsigned char p1;
} BData;


DOFSix provides the translation (T) components and rotation (R) components. Tx,Ty,Tz,Rx,Ry,Rz.
BData has the button information. Since I have the navigator, I only have 2 buttons. This gets identified in p1 as 1 or 2. It is very likely that if you binary or them when you have more buttons will yield the correct result. You must do P = p3 | p2 | p1 (see the ProcessWM_INPUTEvent method for more information about this.)

************
User avatar
johnhpus
Platinum Sponsor
Platinum Sponsor
Posts: 1186
Joined: Sat Apr 17, 2004 2:49 am
x 3

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by johnhpus »

This looks like good stuff. I only wish I had one of those devices so that I could play with it. Thanks for sharing.
iblues1976
Gnome
Posts: 379
Joined: Fri Sep 16, 2011 4:54 pm
x 10

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by iblues1976 »

You can get student/academic discount if you are in education.

The device is 99 dollars (Space navigator) I'm not so trill about the device.
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 534

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by Kojack »

The Logitech Nulooq seems like an awesome and cheaper alternative (Logitech own 3D Connexion too). It's got 3DOF (X, Y and Yaw) and an ipod style touch sensitive wheel on the top. Don't trust it though, while it's still available for sale, Logitech stopped making drivers for it years ago. It's got no windows 7 drivers and doesn't work in photoshop above CS3.
Just a warning, sorry for going a bit off topic (I almost bought a Nulooq last year, luckily I found the angry mob on the logitech forums first).
iblues1976
Gnome
Posts: 379
Joined: Fri Sep 16, 2011 4:54 pm
x 10

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by iblues1976 »

I think if I play a bit with the navigator, I can make it more smooth... I will look in their forum to see if anyone has any tricks... I will post them here once I find out. This week I hope to get the Wii remote working, so I can use it with Ogre.

Kojack, you mention an alternative to use the WM_INPUT messaging... can you elaborate on this... I think was some WndProc via Ogre?
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 534

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by Kojack »

Kojack, you mention an alternative to use the WM_INPUT messaging... can you elaborate on this... I think was some WndProc via Ogre?
I'm teaching message pumps with ogre and cegui in 2 days, so I'm in a verbose mood. The useful bit is towards the end of the post. :)


A typical windows "message pump" looks like this:

Code: Select all

MSG  msg;
while( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
	TranslateMessage( &msg );
	DispatchMessage( &msg );
}
That one is copy/pasted from ogre's source.

Peek reads the next available windows message (and removes it from the queue due to PM_REMOVE). GetMessage would work too, but it waits for messages which is bad if there's none in the queue.
TranslateMessage checks if it's a low level virtual key event and injects higher level ascii character events back into the queue (WM_KEYDOWN is converted into WM_CHAR, if the key is ascii compatible). Ignore it, I'm just being thorough.
DispatchMessage sends the message to the application's winproc, which is a callback function attached to a window.

Ogre has a winproc that handles a bunch of window messages for things like window move, window resize, window close, etc.

One way to use ogre is to attach frame listeners (to handle your logic updates) and call Root::startRendering(). This goes into an infinite loop which does:
- call message pump
- call renderOneFrame

In this case the message pump and winproc are entirely inside of ogre, you have no control over them. If you want to receive windows messages like WM_INPUT, you need to insert your own code into the winproc chain. That's what the SetWindowLong stuff was doing. It makes your winproc the first one called by the message pump, then you call the old one (ogre's). So ogre gets to move and resize windows, and you can read messages too.

The other way to use ogre is to have a custom loop. Don't call startRendering.
In your own custom loop, you can do the message pump yourself. Still call DispatchMessage like above (so ogre's winproc gets to look at them) but you can also directly handle messages there yourself. No need to chain winprocs.
Now your main game loop could look like this:

Code: Select all

while(true)
{
   MSG  msg;
   while( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
   {
      TranslateMessage( &msg );
      DispatchMessage( &msg );
      if(msg.message == WM_INPUT)
      {
         // do all the GetRawInputData stuff
      }
   }
   updateLogic(); //should really check time so it's only called at fixed rate, I'm too lazy to add timer stuff to this example)
   root->renderOneFrame();
}
I don't use framelisteners either, I have logic called at a fixed rate (60Hz usually) instead of frame rate.



I think if I play a bit with the navigator, I can make it more smooth
Hmm, it should be very smooth already. Maybe it's sensitivity in the driver software needs adjusting.
This week I hope to get the Wii remote working
Take a look at WiiYourself. It's a pretty cool lib which supports every part of the wiimote and addons. My students used it a few years ago to make an ogre game with multiple wiimotes (one as a gun, one as a head tracker).
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 534

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by Kojack »

The smoothness issue may be due to the way you are handling events.
In my input lib, I only store the state of the connexion device. WM_INPUT messages can arrive at any speed they want, I just store the most recent state.
In my logic code, I query the current state from the input lib and use that to move and rotate the camera. This is locked to 60Hz.

In your code, you move and rotate the camera every time a WM_INPUT message is received. Since they are sent every time the state changes, the more you wriggle the control puck the more messages arrive. This makes the speed erratic. If you could hold the puck perfectly still while it's pushed, your camera would probably stop moving because the WM_INPUT messages would stop.
iblues1976
Gnome
Posts: 379
Joined: Fri Sep 16, 2011 4:54 pm
x 10

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by iblues1976 »

Kojack... thank you for all the info. I'm going to look into.
iblues1976
Gnome
Posts: 379
Joined: Fri Sep 16, 2011 4:54 pm
x 10

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by iblues1976 »

@Kojack

While I make the changes, I wanted to pointed out that one of the issues that I have seen with the devices, is that the 6DOF is not clear...
For example, if I only want to translate, there is always other numbers reporting rotations, but I'm not rotating... I could fix this a bit I think...
I don't know if you have one, but I had forgotten to say that...

thanks
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 534

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by Kojack »

You could do a dominant control system, where which ever info (translation or rotation) has the largest value is the only one that is used. Although that means you can't turn while moving.
Or implement a deadzone like the pic I posted in your gamepad thread a few minutes ago.

What I do is ignore roll (if I was in a space sim, it's ok, but for 99% of my uses roll is pointless) and just use all the values directly. No dead zone. I find it fairly natural to move and steer that way. Some buttons could be used to turn different axes on/off.
I don't know if you have one, but I had forgotten to say that...
I've got a Space Navigator and a Space Pilot Pro.
Here's a thread about my Space Pilot Pro rendering an ogre camera into it's colour lcd display. :)
http://www.ogre3d.org/forums/viewtopic. ... 0&p=414451
iblues1976
Gnome
Posts: 379
Joined: Fri Sep 16, 2011 4:54 pm
x 10

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by iblues1976 »

Kojack wrote:

Code: Select all

while(true)
{
   MSG  msg;
   while( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
   {
      TranslateMessage( &msg );
      DispatchMessage( &msg );
      if(msg.message == WM_INPUT)
      {
         // do all the GetRawInputData stuff
      }
   }
   updateLogic(); //should really check time so it's only called at fixed rate, I'm too lazy to add timer stuff to this example)
   root->renderOneFrame();
}
@Kojack

One quick question:
I'm changing my code to reflect your suggestions.

If the message is removed and it wasn't one that I processed, wouldn't it not be processed by Ogre? (like other windows events)

Because with the SetWindowLong, inside of my code, since it gets calls first, then I called
CallWindowProc(g_ogreWinProc2, hwnd, uMsg, wParam, lParam); to forward all the events.

thanks
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 534

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by Kojack »

The DispatchMessage function is the one that passes messages to the ogre winproc (or your winproc which chains to the ogre one).
So in that code every incoming message is given to ogre to look at, then the WM_INPUT stuff is checked. It would be better to check the message first and only do DispatchMessage if it's something your code doesn't want to handle, I was just lazy. :)
iblues1976
Gnome
Posts: 379
Joined: Fri Sep 16, 2011 4:54 pm
x 10

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by iblues1976 »

thanks!
iblues1976
Gnome
Posts: 379
Joined: Fri Sep 16, 2011 4:54 pm
x 10

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by iblues1976 »

I have added this. I still believe that I could make the input to be smoother by adding some additional logic...
For now, I did this:

Code: Select all

while( navOn && PeekMessage( &msg, hwnd, 0, 0, PM_REMOVE ) )
			{
				fprintf(stderr,"Inside of PeekMessage\n");
				TranslateMessage( &msg );
				
				if(msg.message == WM_INPUT)
				{
					OgreFramework::getSingletonPtr()->ProcessWM_INPUTEvent(msg.lParam);
					fprintf(stderr,"ProcessWM_INPUTEvent\n");
					//break;
				}
				else
				{
					DispatchMessage( &msg );
				}
			}
before messagePump. If it is done after, it won't work (as one would expect.)

Now, if I add a break below the ProcessWM_INPUTEvent , it will respond only to one per each turn in the "game loop"

I do think this way is better. I would add to make it work better with Ogre and my loop are the following (which I will get to it soon)

1) Add non-linear transformation to each of the numbers, to make it more smooth.

There is still something that I could make the input to work better with ogre (I mean adding it to the loop)

This whole thing with input, while may seem trivial, I have begun to find it very interesting.

I will be editing the first post with the final solution this week.
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 534

Re: [How TO]3DX Navigator with OGRE3D {Win}Last Updated:03-0

Post by Kojack »

before messagePump. If it is done after, it won't work (as one would expect.)
Do you mean the Ogre messagePump function? You can't call that one, your code is an alternative message pump. You can only have one being used, otherwise they would steal messages from each other (WM_INPUT messages could be grabbed by the ogre one and thrown away instead of yours, depending on when they arrive).
This whole thing with input, while may seem trivial, I have begun to find it very interesting.
It can be painful, but it's also quite cool. :)
I really want to get a Logitech G27 force feedback steering wheel, I've never coded for real force feedback before. But I don't like car games (except GTA / Saints Row, which aren't really steering wheel based), so it's probably a waste. I might get a jog/shuttle control instead, that could be fun. Or maybe a novint falcon. :)


Something really cool to do with the space navigator is to apply it to an object with physics. I had a test app using PhysX which could either move the camera, or move boxes in the world. When in box mode, it applied all 6DOF to the box as force and torque. PhysX allows you to set one-way physics responses, so effectively I made it that the entire scene was static except the one object being moved. This lets you easily stack objects without knocking them over accidentally. It would make an awesome scene editor for environments like elder scroll games.
Post Reply