基于数字头盔的通用三维虚拟场景实时交互方法 【技术领域】
本发明涉及了计算机图形学、虚拟现实技术、用户接口管理系统技术、计算机硬件接口技术、计算机操作系统技术等领域,具体涉及了获取数字头盔状态信息、映射为标准输入设备鼠标、键盘消息,将模拟的鼠标键盘消息发送给三维可视化软件并进行三维场景的交互控制。
背景技术
人机交互技术是计算机图形学和虚拟现实领域中不可或缺的组成部分,是属于用户接口管理系统(User Interface Management System,UIMS)的一项重要技术。而交互设备是完成交互任务的重要基础,其主要实现定位、选择、取值,拾取等五种交互任务。目前的三维可视化软件如3d Max、Maya等主要通过鼠标和键盘进行场景的定位和变换,在三维游戏中则除了鼠标键盘外,还可以采用游戏杆进行交互。这些设备的交互能力有限,在软件中被人为地映射为场景平移、旋转、缩放等操作,交互方式不够自然、灵活。此外,这类交互软件还限制了用户的双手,使得更加复杂的交互难以实现。
虚拟现实技术最早出现在上世纪60年代,在航空航天、建筑、医疗、教育、艺术等领域有着广泛的应用。其中数字头盔直接佩戴在使用者头部,可以根据用户头部的运动控制场景的变换,极大的增强了三维交互式虚拟现实的自由感和沉浸。但数字头盔的使用需要虚拟现实系统等专门软件的支持,而一般的三维可视化软件均不支持数字头盔。
【发明内容】
本发明的目的在于解决目前数字头盔需要专门软件系统支持,不能运用于大多数三维可视化软件系统中的缺陷,提供一种基于数字头盔的通用三维虚拟场景实时交互方法,将使用者佩戴的数字头盔的运动状态映射为标准输入设备键盘、鼠标的状态消息传送给三维可视化系统,以实现用数字头盔控制三维可视化系统中的三维交互。
本发明的技术方案是:基于数字头盔的通用三维虚拟场景实时交互方法,该方法包括以下三个步骤:
步骤1利用数字头盔的硬件接口规范及数据协议或SDK从计算机接口中读取并解析数字头盔状态数据,获得头盔的空间位置及方位角参数;
步骤2根据三维虚拟场景软件交互配置定义,将从步骤1中获得的数据映射为标准交互设备(鼠标、键盘)输入状态信息;
步骤3调用操作系统API,将步骤2中得到的模拟标准交互设备输入状态信息转化为真实交互设备信息,驱动三维虚拟场景的实时交互。
所述步骤1中从计算机硬件接口中获取数字头盔状态信息的方法有两种:(1)利用数字头盔供应商提供的硬件接口规范及数据协议,调用操作系统API,从计算机硬件接口中获取数字头盔状态信息;(2)调用数字头盔供应商提供的软件开发包,利用开发包中提供的方法获取数字头盔状态信息。本方法所需要获得的数字头盔状态信息包括数字头盔空间位置数据(x,y,z)及数字头盔方位角数据(alpha,beta,gama)。
所述步骤2中的交互方式映射是指建立数字头盔运动状态信息与标准输入设备鼠标、键盘输入信息间的映射关系。数字头盔运动信息包括头盔位移分量(x,y,z)和头盔方位角(alpha,beta,gama);键盘信息包括按键(vk)及按键状态(vkState);鼠标信息包括鼠标位置(x,y)与鼠标状态(左键按下、左键释放、右键按下、右键释放、滚轮向前、滚轮向后、鼠标移动)。根据三维可视化系统的交互定义,将数字头盔的连续运动信息映射为键盘与鼠标的连续信息组合。将数字头盔位移状态映射到单位立方体,将数字头盔旋转状态映射到单位球体,然后再映射到三维可视化软件的交互窗口中。
所述步骤3中将步骤2所获得特定输入设备状态信息转化为真实交互设备信息发送给计算机,是通过操作系统自带的应用程序编程接口,将自定义的鼠标、键盘消息插入到系统消息队列中,并被三维可视化软件所接受处理,以实现对三维可视化软件的交互控制。
【附图说明】
图1是本发明实施例1的流程图
图2a是本发明实施例1平移映射规范空间
图2b是本发明实施例1旋转映射规范空间
具体实施方法
下面结合附图和实施例做进一步详细说明。
实施例1
本实例以Windows XP SP3为操作系统环境,Visual Studio 2008 SP1为开发环境,C++为开发语言,以Virtual Research公司HMD v8数字头盔控制AutoDesk公司的3D Max软件进行三维场景交互。
如图1所示,基于数字头盔的通用三维虚拟场景交互方法,按照如下步骤建立数字头盔交互配置程序HMD_panel.exe:
步骤1利用数字头盔的SDK从计算机接口中读取并解析数字头盔状态数据,获得头盔的空间位置及方位角参数;
步骤2根据三维虚拟场景软件交互配置定义,将从步骤1中获得的数据映射为鼠标、键盘的输入状态信息;
步骤3调用Microsoft Windows操作系统应用程序编程接口SendInput(),将步骤2中得到的模拟标准交互设备输入状态信息转化为真实交互设备信息,驱动三维虚拟场景的实时交互。
完成HMD_panel.exe之后,连接并安装头盔显示器,确认运行正常;启动三维软件,以3DMAX为例,明确其三维观察窗口的鼠标和键盘映射方式;启动HMD_panel.exe后,进入到用户配置界面,按照3DMAX的三维交互相关的鼠标和键盘映射,设置界面平移和旋转选项;启动头盔交互模式,利用数字头盔控制3D Max三维场景变换;最后结束头盔交互模式并退出数字头盔交互配置程序。
具体实施步骤如下:
步骤1利用数字头盔的SDK从计算机接口中读取并解析数字头盔状态数据,获得头盔的空间位置及方位角参数。
数据获取需要涉及设备初始化、设备读取、设备关闭三个方面。为了适应不同的头盔设备,定义相应地回调函数如下。只要满足如下接口的定义,则能适应任何一种头盔。
(1)初始化设备的回调函数接口
#typedef void*(*PROC_INIT)(void);
(2)数据读取回调函数接口
#typedef bool(*PROC_READER)(void*context,
float*x,float*y,float*z;
float*alpha,float*beta,float*gama);
其意义是头盔交互系统通过调用该函数接口,即可获得头盔的位置x,y,z以及方位角度alpha、beta、gama等重要数据。
(3)关闭设备回调函数接口:
#typedef void(*PROC_CLOSE)(void*context);
以上三个回调函数接口一般由用户针对具体的头盔硬件来实现。
(4)定义头盔数据获取类CHMD_Reader如下:
class CHMD_Reader
{
Void*m context;
PROC INIT m_callback_init;
PROC_READER m_callback_reader;
PROC_CLOSE m_callback_close;
void SetInit(PROC_INIT cb_init){m_callback_init=cb_init;}
void SetReader(PROC_READER cb_reader)
{m_callback_reader=cb_reader;}
void SetClose(PROC_CLOSE cb_close)
{m_callback_close=cb_close;}
};
其中成员变量m_context表示数据获取接口的运行情景,视具体的设备访问方法而定。CHMD_Reader的使用方法是这样的,先用这个类定义一个对象,接着调用这个类的方法:
a.定义一个对象:
CHMD_Reader reader;
b.设置用户定义的回调函数:
reader.SetInit(cb_init_usr_defined);
reader.SetReader(cb_reader_usr_defined);
reader.SetClose(cb_close_usr_defined);
c.调用回调函数开启设备、获取数据、最后关闭设备。
Float x(0),y(0),z(0),alpha(0),beta(0),gama(0);
Reader.m_context=reader.m_callback_init();
Reader.m_callback_reader(reader.m_context,x,y,z,alpha,
beta,gama);
Reader.m_callback_close(reader.m_context);
步骤2根据三维虚拟场景软件交互配置定义,将从步骤1中获得的数据映射为鼠标、键盘的输入状态信息。
(1)头盔、鼠标和键盘等设备状态的数据结构。
a.定义HS为头盔状态:HS
HS定义为结构体:
struct HS
{
float x,y,z;
float a,b,r;
};
其中x,y,z分别表示头盔的位移分量,a,b,r分表表示头盔的方位角。
b.定义键盘状态:KS
KS定义为结构体:
struct KS
{
BYTE state;
BYTE vk;
}
其中state表示按键状态:“1”表示是释放状态,“2”表示按下状态;vk表示虚拟键。一般只需要考虑功能键如″CTRL SHIFT SPACE″和字符键″A...Z;0,1,...,9,-,+″以及光标键″上下左右″。
c.定义鼠标状态:MS
MS定义为结构体:
struct MS
{
BYTE state;//鼠标状态,左键单击,移动鼠标
int x,y;
}//鼠标位置
其中state的取值范围可以为:
enum MS_STATE
{
MS_LBTN_PRESS=1,
MS_LBTN_UP=2,
MS_RBTN_PRESS=4,
MS_RBTN_UP=8,
MS_WHEEL_PRE=16,
MS_WHEEL_DWN=32,
MS_MOVE=64
};
分别表示鼠标的左右键状态、滚轮状态、移动状态等。
(2)定义两种映射:
H1:头盔的连续运动→连续的按键组合;
H2:头盔的连续运动→连续的鼠标运动。
其中H1视具体的交互情景而定,如将头盔的移动量映射为持续的光标键的按下状态;对于某些辅助按键并不需要实际的头盔数据,如Alt、Ctrl、Shift等,可根据需要自动产生。
(3)平移映射的计算
如图2a,给定头盔的移动范围:[0,HX],[0,HY],[0,HZ];以及三维场景的范围:[0,SX],[0,SY],[0,SZ]。
a.按键映射方法H1:
If头盔移动量大于零then发送与平移场景对应的按键消息
b.映射方法H2:
由头盔的位移分量(hx,hy,hz),计算屏幕上鼠标的位移分量(mx,my)。
头盔位移规范化:(中心对称)
uhx=hx/HX-0.5;
uhy=hy/HY-0.5;
uhz=hz/HZ-0.5;
虚拟场景移动量:
sx=uhx*SX+sx0;
sy=uhy*SY+sy0;
sz=uhz*SZ+sz0;
其中平移分量(sx0,sy0,sz0)为预先定义的参考基点,相当于观察者在虚拟三维场景中的初始位置。
鼠标移动量计算:
Mx=sx*kx;
My=sy*ky;
其中系数kx,ky分别为三维软件自定义的放缩系数,也可由用户自己任意设置.需要注意的是移动分量sz并没有被鼠标利用,但是可以映射为某个按键,即可表示跳起的高度.
(4)旋转映射的计算
如图2b,头盔的方位角度活动范围给为:[-A/2,A/2],[-B/2,B/2],[-R/2,R/2];
虚拟场景的旋转角度一般为:[0,2*Pi],[0,2*Pi],[0,2*Pi],分别表示绕x,y,z轴的旋转角度.
a.按键映射H1:
给定头盔的方位角度a,b,r,产生相应的按键消息,即
If头盔转动量大于零then发送与旋转场景对应的按键消息
其中转动量的计算增量方式,即新的角度减去旧的角度.
b.鼠标移动量映射H2:
给定头盔的方位角度a,b,r,产生鼠标移动消息和相应的移动量.这意味着对空间球体的连续旋转变换.而要从已知的空间旋转变换得到平面鼠标的移动往往没有唯一解.为简单起见,可将头盔的方位角视为连续实施的欧拉角度,从中任选两个角度分别映射到鼠标的移动分量上即可.
规范化头盔旋转量:
ha=2*a/A;
hb=2*b/B;
hr=2*r/R;
映射鼠标移动分量:
mx=ha*Pi*radius;
my=hb*Pi*radius;
其中radius为空间操纵球的半径,可以根据需要进行设置.
步骤3调用操作系统API,讲步骤2中得到的模拟标准交互设备输入状态信息转化为真实交互设备信息,驱动三维虚拟场景的实时交互。模拟传统输入设备的方法是:调用系统API。下面给出在本实施例中使用的关键数据结构。
(1)API定义以及参数说明.
UINT SendInput(UINT nInput,LPINPUT pInput,int cbsize)
该函数能够将自定义的鼠标,键盘、以及其他硬件消息插入到系统的消息队列中.当这些消息被三维软件所处理时,则能模拟三维软本身的交互行为.函数的参数意义如下:
nInput:一起发送的消息数目
LPINPUT:输入消息数组指针
Cbsize:单个消息结构体的的大小.
LPINPUT定义:
Typedef struct tagINPUT{
DWORD type;
Union { MOUSEINPUT mi; //鼠标输入结构体
KEYBDINPUT ki; //键盘输入结构体
HAREDWAREINPUT hi;}//其他硬件输入结构体
}
结构体中type字段可取值为:INPUT_MOUSE,INPUT_KEYBOARD,INPUT_HARDWARE.
结构体中mi字段为鼠标输入结构体,定义为:
Typedef struct tagMOUSEINPUT
{
LONG dx.dy;
DWORD mousedata,dwFlags.Time,dwExtraInfo;
}
其中dx、dy分别表示鼠标的绝对位置或者相对位置,这取决于字段dwFlags的设定:MOUSEEVENTF_ABSOLUTE.
结构体中字段ki为键盘输入结构体,定义为:
Typedef struct tagKEYINPUT
{
WORD wVK; //虚拟键码
WORD wScan; //硬件扫描码
DWORD dwFlags; //指示按键状态
DWORD time; //时间邮戳
DWORD dwExtraInfo
}
(2)将鼠标键盘状态转换为实际输入消息
对每一个被映射的设备状态,定义一个与之对应的INPUT结构体对象,然后调用SendInput()将所有INPUT对象发送到当前消息队列中去.下面给出该函数调用的方法。
a.模拟键盘输入的系统API方法.
定义原型函数:
void Simu_keyboard(KS*pKS,int nks);
其中参数pKS表示按键状态列表,nks表示列表长度.该函数的简要实现方法如下:
void Simu_keyboard(KS*pKS,int nks)
{
INPUT*inkey=new INPUT[nks];
for(int i=0;i<nks;++i)
{
Inkey[i].type=INPUT_KEYBOARD;
Inkey[i].wVK=VK_LEFT;
If(pKS[i].state==KS_PRESSED)
inkey[i].dwFlags&=~KEYEVENTF_KEYUP;
Else inkey[i].dwFlags|=KEYEVENTF_KEYUP;
}
//调用系统API,发送消息
SendInput(nks,inkey,sizeof(INPUT));
}
b.模拟鼠标输入的系统API方法.
定义原型函数:
void Simu_mouse(MS*pMS,int nms);
其中参数pMS表示按键状态列表,nms表示列表长度.该函数的简要实现方法如下:
void Simu_mouse(MS*pMS,int nms)
{
INPUT*inmus=new INPUT[nms];
for(int i=0;i<nms;++i)
{
inmus[i].type=INPUT_MOUSE;
inmus[i].dx=pMS[i].pos.x;
inmus[i].dy=pMS[i].pos.y;
if(pMS[i].state==MS_LBTN)//左键状态
inmus[i].dwFlags|=MOUSEEVENTF_LEFTDOWN;
else if(pMS[i].state==MS_RBTN)//右键状态
inmus[i].dwFlags|=MOUSEEVENTF_RIGHTDOWN;
if(pMS[i].state&MS_ABSOLUTE)//绝对移动还是相对移动
inmus[i].dwFlags|=MOUSEEVENTF_ABSOLUTE;
}
//发送鼠标消息:
SenfInput(nms,inmus,sizeof(INPUT));
}