我要发帖 回复

正式会员

5

主题

33

积分

0

专家分

:

私信
发表时间 : 2006-11-25 11:23:21 | 浏览 : 13492    评论 : 63
一、综述   
    在vega中,Motion Model可使用多种输入设备,但同一Motion Model对不同输入设备的支持是不同的。

    比如,vega自带的Flight Simulator,是一种复杂的空气动力学模型(用作演示教学或者观众参与操作的仿真是可以的),可以使用Mouse,FlyBox,JoyStick作为输入。为了力求操作真实感,摇杆类(Joystick、FlyBox)是不二之选。


二、JoyStick可FlyBox硬件层面的比较
    让我们先来比较一下高端飞行手柄和FLybox之间的硬件差异:
飞行手柄我选择Saitek的X45(我05年在北京开虚拟现实峰会,购与海龙)。X45支持一个摇杆(x,y输入),一个尾舵(z输入),两个rotary,一个节流阀(Throttle),共6个16位模拟通道,外加8个按键,两个POV,可谓功能强大。
   
    FlyBox可参考B&G System的官方网页。
   
    Flybox支持3个8位数字通道(toggles),8个12位模拟通道(xyz,throttle均使用模拟通道)。

    但从硬件指标上看,使用低成本(几百RMB)游戏级高端JoyStick替代高成本(近3K美金)FlyBox应该是没问题的,但vega并不这么想。

三、vega对JoyStick和FlyBox的支持
    参考vega关于输入设备的文档,我们可以清楚的知道:
                             toggles                sliders
                    | X Y Z | H P R | 0 1 2 3 4 5 6 7 8 9 | 0 1 2 3 4 5 6 7 8 9 |
Flybox           | x x   | x        |   x x x x x x x x      | x x                 |
PC Joystick    | x x   |           | x x o                    | o                   |

对于JoyStick,无论硬件支持什么,vega只支持xy输入,3个按键。尾舵?节流阀?没门!
对于FlyBox,可以使用xy,H即尾舵,slider0即节流阀。
看来,想用JoyStick替代FlyBox,困难在vega。并且,现在得JoyStick是USB接口的,FlyBox是Com口的。

大家都知道vega自带了一个HeliFight.adf文件,我们将其中的输入设备设置为JoyStick,结果可以发现,尾舵、节流阀全部不能用,加速按toggle1,减速按toggle2,全无操作的真实感可言。
想省钱不降效果真的就这么难么?办法还是有的。


领导叫我,离开一下,下午继续。
待续,未完

最近VR访客

静涛 评论于2006-11-25 15:12:07

四、第一种方法尝试——自定义输入设备----->失败了

四、第一种方法尝试——自定义输入设备----->失败了

       在vega中,可以使用自定义输入设备,如果自定义一个输入设备,通过windows操作JoyStick的API将JoyStick的各个轴数据放在自定义输入设备的Toggles和Sliders中,是不是可以达到模拟一个FlyBox的目的呢?相关vega函数代码如下:

[cpp]vgIDev *joyInput= vgNewIDev();
vgProp( joyInput, VGIDEV_DEVICETYPE, VGIDEV_USER_DEFINED );   
vgIDevUserOpenFunc(  joyInput, OpenFunc );  
vgIDevUserReadFunc(  joyInput, ReadFunc );   
vgIDevUserCloseFunc( joyInput, CloseFunc );  
vgIDevOpen ( joyInput );

int OpenFunc(vgIDevUserOpenStruct* openStruct)
{         
     openStruct->num_toggles = 8;        
     openStruct->num_sliders = 1;         
     return 1;
}

int ReadFunc(vgIDevUserReadStruct* readStruct)
{         

     int toggles[8];

     Joy.GetJoystickData();  //Joy是我自己写的手柄操纵类的实例,后面有详细实现代码。

     //读取JoyStick的各个按键的状态
     if (Joy.Button1IsPressed())
         toggles[0] = 1;
     else
         toggles[0] = 0;
     if (Joy.Button2IsPressed())
         toggles[1] = 1;
     else
         toggles[1] = 0;
     if (Joy.Button3IsPressed())
         toggles[2] = 1;
     else
         toggles[2] = 0;
    if (Joy.Button4IsPressed())
         toggles[3] = 1;
    else
         toggles[3] = 0;
   if (Joy.Button5IsPressed())
         toggles[4] = 1;
   else
         toggles[4] = 0;
   if (Joy.Button6IsPressed())
        toggles[5] = 1;
   else
        toggles[5] = 0;
   if (Joy.Button7IsPressed())
        toggles[6] = 1;
   else
        toggles[6] = 0;
   if (Joy.Button8IsPressed())
        toggles[7] = 1;
   else
        toggles[7] = 0;

   //将节流阀数据放到自定义输入设备的Sliders[0]中
   readStruct->sliders[0] = Joy.GetThrottle();

   //将JoyStick的x,y,rudder数据放到自定义输入设备的Pos的X,Y,H分量上   
   vgPosVec( readStruct->pos, Joy.GetPosX(), Joy.GetPosY(), 0, Joy.GetRudder(), 0, 0 );
  
   //按键状态到自定义输入设备的toggles中
   for (i = 0; i < 8; i++)
       readStruct->toggles[ i ] = toggles[ i ];
  
   return 1;
}

int CloseFunc()
{           
    return 1;
}

windows操纵JoyStick有特定的API,为了使用方便和后期扩展,我自己定义了个类,如下:

//MyJoyStick.h
#include <windows.h>
#include <mmsystem.h>
#if !defined(MYJOYSTICK_H_INCLUDE)
#define MYJOYSTICK_H_INCLUDE

class MyJoystick
{
public:
MyJoystick(int);
int  GetPosX();
int  GetPosY();
int  GetPosZ();
int  GetPosR();
int  GetPosU();
int  GetPosV();
int  GetPosPov();
float GetThrottle();
float GetRudder();
bool Button1IsPressed();
bool Button2IsPressed();
bool Button3IsPressed();
bool Button4IsPressed();
bool Button5IsPressed();
bool Button6IsPressed();
bool Button7IsPressed();
bool Button8IsPressed();
void GetJoystickData();
protected:
UINT jID;
    JOYINFOEX jInfo;
JOYCAPS   jCaps;
private:
int  PosX;
int  PosY;
int  PosZ;
int  PosR;
int  PosU;
int  PosV;
int  PosPov;
bool Btn1IsPressed;
bool Btn2IsPressed;
bool Btn3IsPressed;
bool Btn4IsPressed;
bool Btn5IsPressed;
bool Btn6IsPressed;
bool Btn7IsPressed;
bool Btn8IsPressed;
};
#endif


//MyJoyStick.cpp
#include "stdafx.h"
#include "MyJoyStick.h"
#include "stdio.h"
#include <math.h>
MyJoystick::MyJoystick(int jid=1)
{

if (jid == 1)
  jID = JOYSTICKID1;
else if (jid == 2)
  jID = JOYSTICKID2;
else jID = 0;
joyGetDevCaps(jID, &jCaps, sizeof(jCaps));
PosX = 0;
PosY = 0;
PosZ = 0;
PosR = 0;
PosU = 0;
PosV = 0;
PosPov = 0;
Btn1IsPressed = false;
Btn2IsPressed = false;
Btn3IsPressed = false;
Btn4IsPressed = false;
Btn5IsPressed = false;
Btn6IsPressed = false;
Btn7IsPressed = false;
Btn8IsPressed = false;
}
int  MyJoystick::GetPosX()
{
    //GetJoystickData();
return PosX;
}
int  MyJoystick::GetPosY()
{
    //GetJoystickData();
return PosY;
}
int  MyJoystick::GetPosZ()
{
    //GetJoystickData();
return PosZ;
}
int  MyJoystick::GetPosR()
{
    //GetJoystickData();
return PosR;
}
int  MyJoystick::GetPosU()
{
    //GetJoystickData();
return PosU;
}
int  MyJoystick::GetPosV()
{
    //GetJoystickData();
return PosV;
}
int  MyJoystick::GetPosPov()
{
    //GetJoystickData();
return PosPov;
}
float MyJoystick::GetThrottle()
{
float th;
//GetJoystickData();
th = 1.0f - ((float)PosZ) / ((float)(jCaps.wZmax - jCaps.wZmin));
return th;
}
float MyJoystick::GetRudder()
{
float ru;
ru = 2.0f*(((float)PosR) / ((float)(jCaps.wRmax - jCaps.wRmin)) - 0.5f);
if (ru > 0 )
     ru = abs(ru *10000) / 10000.0;
else
     ru = -abs(ru *10000) / 10000.0;
    return ru;
}
bool MyJoystick::Button1IsPressed()
{
    //GetJoystickData();
return Btn1IsPressed;
}
bool MyJoystick::Button2IsPressed()
{
    //GetJoystickData();
return Btn2IsPressed;
}
bool MyJoystick::Button3IsPressed()
{
    //GetJoystickData();
return Btn3IsPressed;
}
bool MyJoystick::Button4IsPressed()
{
    //GetJoystickData();
return Btn4IsPressed;
}
bool MyJoystick::Button5IsPressed()
{
    //GetJoystickData();
return Btn5IsPressed;
}
bool MyJoystick::Button6IsPressed()
{
    //GetJoystickData();
return Btn6IsPressed;
}
bool MyJoystick::Button7IsPressed()
{
    //GetJoystickData();
return Btn7IsPressed;
}
bool MyJoystick::Button8IsPressed()
{
    //GetJoystickData();
return Btn8IsPressed;
}
void MyJoystick::GetJoystickData()
{
MMRESULT res;

jInfo.dwSize = sizeof (jInfo);
jInfo.dwFlags = JOY_RETURNALL;
res = joyGetPosEx(jID, &jInfo);
if ( res== JOYERR_NOERROR)
{
  PosX   = jInfo.dwXpos;
  PosY   = jInfo.dwYpos;
  PosZ   = jInfo.dwZpos;
  PosR   = jInfo.dwRpos;
  PosU   = jInfo.dwUpos;
  PosV   = jInfo.dwVpos;
  PosPov = jInfo.dwPOV / 100;
  Btn1IsPressed = ((jInfo.dwButtons & JOY_BUTTON1) ? true : false);
  Btn2IsPressed = ((jInfo.dwButtons & JOY_BUTTON2) ? true : false);
  Btn3IsPressed = ((jInfo.dwButtons & JOY_BUTTON3) ? true : false);
  Btn4IsPressed = ((jInfo.dwButtons & JOY_BUTTON4) ? true : false);
  Btn5IsPressed = ((jInfo.dwButtons & JOY_BUTTON5) ? true : false);
  Btn6IsPressed = ((jInfo.dwButtons & JOY_BUTTON6) ? true : false);
  Btn7IsPressed = ((jInfo.dwButtons & JOY_BUTTON7) ? true : false);
  Btn8IsPressed = ((jInfo.dwButtons & JOY_BUTTON8) ? true : false);
}
else
{
  if (res == MMSYSERR_NODRIVER)
   printf("MMSYSERR_NODRIVER\n");
  if (res == MMSYSERR_INVALPARAM)
   printf("MMSYSERR_INVALPARAM\n");
  if (res == MMSYSERR_BADDEVICEID)
   printf("MMSYSERR_BADDEVICEID\n");
  if (res == JOYERR_UNPLUGGED)
   printf("JOYERR_UNPLUGGED\n");
  PosX = 0;
  PosY = 0;
  PosZ = 0;
  PosR = 0;
  PosU = 0;
  PosV = 0;
  PosPov = 0;
  Btn1IsPressed = false;
  Btn2IsPressed = false;
  Btn3IsPressed = false;
  Btn4IsPressed = false;
  Btn5IsPressed = false;
  Btn6IsPressed = false;
  Btn7IsPressed = false;
  Btn8IsPressed = false;
}
}
[/cpp]
经过试验,我发现,Filght Simulator运动模型,在使用自定义操作设备时,可以接收X,Y,H的输入数据,尾舵可以用了;但仍然存在问题,按钮我提供了8个,vega只接收前三个,节流阀我提供了,vega不接收。也就是说,这种自定义设备只比vega中的PC JoyStick设备多了个尾舵控制,其它没变化。

通过试验和分析,第一种方案失败了,但我也从中得到了一个结论:
vega中特定运动模型对不同输入设备,支持的输入数据是不同的。也就是说,FlyBox可以使用X、Y、H、Slider进行对Flight Simulator运动模型的控制,不是因为Flybox有这些输入,而是因为,只有输入设备是FlyBox时,Flight Simulator运动模型,才使用输入设备的X、Y、H、Slider进行控制。所以,我们虽然自定义了输入设备提供这些数据,但就是因为设备类型是VGIDEV_USER_DEFINED,而不是VGIDEV_FLYBOX,才失败了。

要使用JoyStick完全模拟FlyBox,自定义输入设备是没戏了,但真的没办法了么?
静涛 评论于2006-11-25 15:51:55

五、第二种尝试开始了

先自吹自擂一下,本人大学时期,最牛的两门课程,C++和汇编,一个96分,一个97分:lol :victory: ,所以,我起了逆向工程的念头。

我先反编译了psVg.dll,对motion model的代码进行了分析,有了重大发现,也验证了我在上个帖子中的结论,几乎每个motion model都对输入设备的类型进行了判断,翻译成C代码,大意如下:

if (输入设备 == VGIDEV_FLYBOX)
   ……
else if (输入设备 ==VGIDEV_JOY)
   ……
else if…………………………

看来,想模拟一个FlyBox,必须看看Vega底层是如何识别输入设备类型,并如何通不同类型输入设备进行通信的:( 。

再次分析psVg.dll,看到了这样的信息,vega中,对于所有设备的通信,都是通过一个DLL文件,xvsdll.dll,也就是obuil
大侠提到vega程序如何打包时必须一起带走的文件。

ok,再次祭出法宝,反编译xvsdll.dll。

整个分析理解汇编代码的过程非常繁冗复杂,就简单说说结论吧。

1。FlyBox是串口设备,默认通信速率为19200
2。xvsdll.dll在初始化FlyBox时,向串口发送一个字符'T',然后从串口接收43个字节的版权信息
3。版权信息结构如下“Copyright (c), BG Systems19XX RevisionA.BCD”,其中XX为两位数字,自己随便写,表示版权年份;A是主版本号,为一位数字,可取1、2、3;B是从版本号,一位数字;C为alpha版本号,一位数字;D为bug版本号,一位小写字符。
4。所有版本号决定了FlyBox的型号,不同型号,通信方法不同。经过我的试验,可以使用这样的版权信息"Copyright (c), BG Systems1999 Revision3.07k",FLyBox最高端,最新的型号。
5。对于3.07k的版本,初始化后,xvsdll.dll发送一个字节0X0A,确认是否初始化成功,接收两字节返回信息,如第一个字符时‘a’,表示设置成功。
6。对于3.07k的版本,通信时,xvsdll.dll发送一个字符‘o',要求FlyBox输入各个轴和按钮的数据,共24个字节。
7。最开头的字节和最后一个字节必须是'B' 和'\n'
8。其它每个字节都减掉0x21,得到的是数据。
9。'B'之后的6个字节为3个数字信道对应数据区,为按键信息。相邻2个字节为一组,每个字节的低4位拼接成一个8位数字,为一个数字信道数据。
10。其它字节为8个模拟信道对应数据区,相邻2个字节为一组,每个字节低6位拼接成一个12数字,为一个模拟信道数据。
静涛 评论于2006-11-25 16:03:36

顺便想到的

之前在论坛上看过一个帖子,也是问Flight Simulator下怎么使用USB的游戏手柄

我在反编译时顺便看了一下,发现,vega是支持USB手柄的。
尽管我们在Lynx上看到PC JoyStick的设置上需要指定串口号和速率,但是这些信息都没有用,因为VegaNT是使用windowsAPI实现的读取手柄信息的。所以只要windows控制面板上的游戏手柄窗口下可以正确识别手柄,不论什么接口,vega都可以使用。:victory:
静涛 评论于2006-11-25 16:17:56

六、最后冲刺,成功!!!

既然FlyBox的通信细节都弄清楚了,我就可以动手实现整个模拟了,再来说几条关键结论:

1。第一个数字信道,0-8为表示的是按键0-8,每个按键1位,1为按下,0为没按下。
2。第二个数字信道的最低为表示按键0。也就是说FlyBox一共9个按键。
3。第一个模拟信道为X轴,取值范围0-4095
4。第二个模拟信道为Y轴,取值范围0-4095
5。第三个模拟信道为Z轴(尾舵),取值范围0-4095
6。第四个模拟信道为节流阀,取值范围0-4095

这样我们就可以合成整个FlyBox的数据了。

下面说说我设计的实现方案:

我用的图形工作站是DELL 360n,两个串口,我就弄了个串口通信线,连接com1和com2口。
在vega中设置FlyBox输入端口为com1,自己写软件打开com2,通过软件,获取JoyStick的数据(也就是用的上面的MyJoyStick类),按着FlyBox的数据格式组织好,当有让FlyBox发送信息的要求时,就通过com2发送。
因为com2和com1相连,vega就读取了我模拟的FlyBox数据。
经过测试,完全OK:lol :lol :lol

我写的模拟程序目前还只针对X45手柄,待我改进其通用性后传上来,和大家分享。

[ 本帖最后由 静涛 于 2006-11-25 04:28 PM 编辑 ]
flyBox.JPG
静涛 评论于2006-11-25 16:26:12

写在最后

整个工程进行了整整2个星期,但乐在其中。

      我想说的是,这并不是简单的JoyStick->Flybox的实现方法,也给大家提供了一种更底层,更彻底的输入设备扩展方案。

      感谢vrchina给虚拟现实的工作者和爱好者提供了一个开放的平台,我是这里的受益者,所以,我愿意把自己知道的跟大家一起分享,让更多的人受益。也希望我们上坛子的每个人,都学习的层面上开放自己的成果,为坛子,为中国的虚拟现实发展作出贡献。
      我目前正在做将TrackIR作为vega的HMD设备的研究,希望有兴趣的朋友们一起探讨。

                                                                      静涛
                                                                     2006年11月25日 于长春理工大学 信息技术研究所 计算机仿真技术研究室

最后,下台,鞠躬。大家鼓掌,斑竹加分吧:lol
静涛 评论于2006-11-25 17:45:02
原帖由 june841125 于 2006-11-25 05:11 PM 发表
套用老罗的话,我只能说,楼主~你TM牛B了。。。



呵呵,是你呀,上次问USB手柄的也是尊驾吧,我回答了你的问题,vega支持usb手柄的,不妨弄个,自己试试,效果不错。
独山子 评论于2006-11-25 19:53:06
可喜可贺,分享你的喜悦!

[ 本帖最后由 独山子 于 2006-11-25 08:02 PM 编辑 ]
逆水行舟,不进则退。
obuil 评论于2006-11-25 23:40:05
我从官方听说vega不支持usb的 我也就这么认为了

呵呵,看来我说错了

非常不错的文章
obuil 评论于2006-11-25 23:43:13
好的文章将会被转载好多年的

呵呵,而好的只言片语容易成为别人文章的一部分

希望大家多发类似于VC知识库中的文章一样

我也会这么干,
静涛 评论于2006-11-28 15:49:32
原帖由 13stone 于 2006-11-28 10:38 AM 发表
对了,再弱弱的问一下,如何反编译啊?? 需要什么工具??


反编译工具很多,我用的是IDAPro4.15
yhsui 评论于2006-12-15 21:07:25
绝对好贴!但我权限不够,很遗憾,无法看附件。
hantang50 评论于2006-12-16 14:14:27
我正在做一个飞行器仿真的程序,运动模式采用Flight simulator,输入设备设置成PC Joystick,采用USB接口的Saitek EVO操纵杆,但是每次运行时,程序都会死掉,静涛兄说vega是支持USB手柄的,但是我这个程序为什么不能用呢?
yhsui 评论于2007-1-15 23:45:20

我的飞行手柄怎么用不成呢?

我的飞行手柄也是Saitek的X45(usb接口),我只想把它作为普通游戏杆用。但是我在Vega中怎么找不到它呢?在Windows的控制面板里我看到它是正常的,可以测试各个按钮的响应。可是用Vega的Input device tool却发现不了,这是怎么回事呢?
静涛 评论于2007-1-21 15:38:18
device的类型需要设置为pc joystick
天行健 评论于2007-1-23 21:59:23
确实很强!
已保存!
宏势 评论于2007-1-30 20:24:57

阁下挺强的!

呵呵!下学期我们也要开汇编语言的课程了,可惜一个礼拜只有一大节,不过没关系,我一定会好好学的, 看了这个牛X的贴以后, 感觉就来了啊!好比兴奋哦了。
41captain 评论于2007-1-30 23:39:53

敬佩中...

感谢静涛分享
HoydenishBug 评论于2007-3-1 17:13:24
强!     强!      强!
一剑西来 评论于2007-4-15 09:37:09
刚起来就看到这样的贴子,顶了!

手机版|VR开发网 统计 津ICP备18009691号
网安备12019202000257

GMT+8, 2021-2-27 09:13 PM

返回顶部