趙 月,王建華
(江蘇科技大學(xué) 電子信息學(xué)院,江蘇 鎮(zhèn)江 212003)
虛擬漫游是在真實(shí)場(chǎng)景或虛擬場(chǎng)景中,借助必要的裝備以自然的方式在該場(chǎng)景中進(jìn)行漫游,從任意角度對(duì)環(huán)境中虛擬對(duì)象進(jìn)行觀察,從而產(chǎn)生身臨其境的感覺(jué),同時(shí)也可以對(duì)其中的物體進(jìn)行規(guī)劃和操作。
文中是為了實(shí)現(xiàn)在實(shí)時(shí)仿真系統(tǒng)虛擬環(huán)境中的漫游,建立視景驅(qū)動(dòng)框架實(shí)現(xiàn)實(shí)時(shí)驅(qū)動(dòng)虛擬場(chǎng)景的變化。Vega Prime(VP)提供多種開(kāi)發(fā)模式[1],一是使用 VP的 Lynx Prime圖形界面,適合簡(jiǎn)單交互性不高的小系統(tǒng);二是運(yùn)用VP的API函數(shù)進(jìn)行的開(kāi)發(fā),通過(guò)C++編程,調(diào)用VP的API函數(shù)實(shí)現(xiàn)對(duì)視景的驅(qū)動(dòng),可以開(kāi)發(fā)較復(fù)雜的系統(tǒng);三是結(jié)合前兩種模式進(jìn)行的全面開(kāi)發(fā),首先通過(guò)Lynx Prime用戶界面建立acf文件,再將VP的API函數(shù)與已經(jīng)配置完成的acf文件進(jìn)行結(jié)合。文中采用第3種模式,可以大幅減少源代碼開(kāi)發(fā)時(shí)間,降低對(duì)開(kāi)發(fā)人員要求,這是也最常用的開(kāi)發(fā)模式。
鑒于MFC的開(kāi)發(fā)框架有著友好的交互界面和消息驅(qū)動(dòng)機(jī)制,本文詳細(xì)介紹了在MFC框架下開(kāi)發(fā)基于Vega Prime視景驅(qū)動(dòng)程序的方法。
實(shí)現(xiàn)虛擬場(chǎng)景的漫游,首先需要解決的是三維視景實(shí)時(shí)驅(qū)動(dòng),在Windows平臺(tái)上進(jìn)行VP開(kāi)發(fā),采用VC++編程。MFC基于文檔/視圖結(jié)構(gòu)的應(yīng)用程序框架結(jié)構(gòu)是開(kāi)發(fā)Windows應(yīng)用程序的主要框架結(jié)構(gòu)。文檔對(duì)象用于保存應(yīng)用程序的數(shù)據(jù),視圖對(duì)象用于數(shù)據(jù)的顯示和用戶的交互[2]。兩種結(jié)構(gòu)聯(lián)系又獨(dú)立,分工明確,簡(jiǎn)化了開(kāi)發(fā)過(guò)程
MFC提供多線程應(yīng)用程序編程[3],包括用戶界面線程UI(User Interface)和工作者線程,兩種線程區(qū)別于UI線程有消息循環(huán)功能,工作者線程沒(méi)有。UI線程既是MFC的主線程,進(jìn)行創(chuàng)建窗口并處理發(fā)送到該窗口的消息,作為主要仿真界面,負(fù)責(zé)開(kāi)啟VP線程、終止VP線程等,控制VP仿真過(guò)程。相對(duì)UI線程外的其它線程是工作者線程。在MFC下實(shí)現(xiàn)VP程序,即創(chuàng)建一工作者線程,在線程主函數(shù)里調(diào)用VP初始化函數(shù)和執(zhí)行主循環(huán)。系統(tǒng)框架如圖1所示。
圖1 系統(tǒng)框架Fig.1 System framework
基于MFC的VP窗口顯示包含了MFC和VP窗口。MFC窗口應(yīng)作為VP窗口的父窗口,配置過(guò)程中將要利用VP中vr Window 類提供的 setParent(Window win)函數(shù)[4],將 MFC 的視圖類CView窗口句柄m_hWnd傳遞到vpWindow窗口即可。
假設(shè)基于MFC的VP項(xiàng)目名稱為MFCVP。
VP工作線程控制函數(shù)主要代碼如下:
啟動(dòng)VP工作線程主要代碼:
中止VP工作線程,在UI線程結(jié)束之前,應(yīng)先結(jié)束VP工作線程。因?yàn)閂P是基于多線程的,若直接退出主線程會(huì)引發(fā)異常。
//中止VP工作線程主要代碼:
MFC類庫(kù)編程環(huán)境下調(diào)用VP基本采用如下的方法:
首先單獨(dú)創(chuàng)建一工作線程運(yùn)行VP整個(gè)程序流程,包括初始化、定義、配置、仿真循環(huán)。然后在MFC框架視圖類中添加成員函數(shù)負(fù)責(zé)開(kāi)啟線程。再利用API函數(shù)AfxBeginThread()開(kāi)啟VP線程[5],并將該視圖類的指針傳到VP線程中,作為線程函數(shù)的輸入?yún)?shù),這樣使得文檔類和視圖類的公有成員變量在VP線程中可以被方便的讀寫(xiě),編程的靈活性得到了提高。
但這樣的方案卻存在著問(wèn)題,因?yàn)橐晥D類(CView)的指針被傳送到主線程之外其它線程中去,使得不同線程在運(yùn)行時(shí)可以訪問(wèn)到相同的CView類實(shí)例,而MFC類庫(kù)本身設(shè)計(jì)并沒(méi)有考慮到多線程的訪問(wèn),所以在線程間傳遞視圖類(CView)是不安全的。
在實(shí)際開(kāi)發(fā)應(yīng)用中,在不退出當(dāng)前應(yīng)用程序前提下進(jìn)行更新視圖會(huì)出現(xiàn)錯(cuò)誤。VP線程在沒(méi)有退出的情況下進(jìn)行重新配置將導(dǎo)致失敗,那么終止正在運(yùn)行的線程,然后再重新開(kāi)啟一新的線程,在初始化VP階段時(shí),VP也會(huì)出現(xiàn)地址訪問(wèn)保護(hù)錯(cuò)誤。
下面分析錯(cuò)誤產(chǎn)生的原因[6],基于MFC的VP應(yīng)用程序在配置中要利用VP中vrWindow類提供的void setParent(Window win)函數(shù),將MFC的Windows窗口句柄m_hWnd傳遞給VP作為其父窗口。由此可以判斷出,基于MFC的VP應(yīng)用程序?qū)嵸|(zhì)上是將VP渲染放入到MFC的View窗口中進(jìn)行的。
VP由于是基于多線程的,所以在初始化過(guò)程中會(huì)自動(dòng)開(kāi)啟一個(gè)VP窗口子線程。該子線程會(huì)根據(jù)傳送的窗口句柄去創(chuàng)建一個(gè)與該句柄對(duì)應(yīng)的窗口有相同大小的窗口,附在該窗口上。在用戶終止VP線程時(shí),該窗口線程不會(huì)自動(dòng)中止,而且也沒(méi)給用戶提供終止函數(shù)。再次啟動(dòng)VP線程,進(jìn)行初始化時(shí),不會(huì)啟動(dòng)新的VP窗口線程,任保留原來(lái)沒(méi)有被終止的窗口線程。這樣就會(huì)導(dǎo)致再次啟動(dòng)VP線程時(shí)會(huì)出現(xiàn)地址保護(hù)錯(cuò)誤。因此,終止VP線程也不能實(shí)現(xiàn)場(chǎng)景切換。
首先分別創(chuàng)建兩個(gè)進(jìn)程,VP進(jìn)程和MFC主進(jìn)程。就是將同一個(gè)應(yīng)用程序中的不同部分進(jìn)行相互黑箱化[7],彼此之間僅通過(guò)定義好的接口進(jìn)行訪問(wèn),這就最大限度地減小各部分之間的相互影響。
然后在此基礎(chǔ)上對(duì)Vega Prime場(chǎng)景中對(duì)實(shí)例的驅(qū)動(dòng),包括初始化、定義、配置、仿真循環(huán)和退出等操作封裝成一個(gè)類,對(duì)特效模塊如碰撞等效果封裝成一個(gè)類,各類功能互不干擾,程序互相訪問(wèn)調(diào)用。
最后解決VP進(jìn)程和MFC主進(jìn)程之間快速有效地進(jìn)行通信問(wèn)題。
因?yàn)樵赩P進(jìn)程和MFC主進(jìn)程間除了要傳遞大量的視景驅(qū)動(dòng)數(shù)據(jù)外,還要進(jìn)行一些控制信息的傳遞,鑒于實(shí)現(xiàn)的快捷性、進(jìn)程通信速度上的性能要求以及便與功能的擴(kuò)展等方面的綜合考慮,采用內(nèi)存映射文件的方式。
本文通過(guò)共享內(nèi)存實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)交換,利用windows消息機(jī)制實(shí)現(xiàn)進(jìn)程間的同步,兩種機(jī)制結(jié)合使用,不僅解決了交換數(shù)據(jù)量小的問(wèn)題,還解決了進(jìn)程并發(fā)存取數(shù)據(jù)的問(wèn)題。
從使用者的角度看,數(shù)據(jù)共享方法是通過(guò)讓兩個(gè)或多個(gè)進(jìn)程映射同一文件映射對(duì)象的視圖來(lái)實(shí)現(xiàn)的,這意味著他們將共享磁盤(pán)上同一文件或者物理存儲(chǔ)器的同一頁(yè)面。因此,當(dāng)一個(gè)進(jìn)程將數(shù)據(jù)寫(xiě)入一個(gè)共享文件映射對(duì)象的視圖時(shí),其它進(jìn)程可以立即看到它們視圖中的數(shù)據(jù)變更情況。通過(guò)上述操作,用戶操作文件就能夠達(dá)到操作內(nèi)存一樣的效率,多個(gè)進(jìn)程操作該映射文件實(shí)現(xiàn)進(jìn)程間內(nèi)存一級(jí)的高速數(shù)據(jù)交互。部分實(shí)現(xiàn)如下:
//創(chuàng)建文件內(nèi)核對(duì)象,其句柄保存于m_hFileSave
HANDLE m_hFileSave=CreateFile(m_FileName(保存文件 ), GENERIC_READ |GENERIC_WRITE, 0, NULL,OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL)
//以實(shí)際數(shù)據(jù)長(zhǎng)度創(chuàng)建另外一個(gè)文件映射內(nèi)核對(duì)象
m_hMapSave=CreateFileMapping (m_hFileSave, NULL,PAGE_READWRITE, 0, ((DWORD) qwFileOffset&0xffffffff)*sizeof(DWORD)); NULL);
//關(guān)閉文件內(nèi)核對(duì)象
CloseHandle (m_hFileSave);
//將文件數(shù)據(jù)映射到進(jìn)程的地址空間
IMapAddressSave = (PDWORD) MapViewOfFile(m_hMapSave, FILE_MAP_ALL_ACCESS, 0, 0,qwFileOffset*sizeof(DWO
RD));
//將數(shù)據(jù)從原來(lái)的內(nèi)存映射文件復(fù)制到此內(nèi)存映射文件
Memcpy (lpbMapAddressSave, lpbMapAddress,qwFileOffset*sizeof(DWORD));
//從進(jìn)程的地址空間撤銷文件數(shù)據(jù)映象
UnmapViewOfFile (lpbMapAddress); UnmapViewOfFile(lpbMapAddressSave);
//關(guān)閉文件映射對(duì)象 CloseHandle(m_hMap);
CloseHandle (m_hMapSave);
//刪除臨時(shí)文件
DeleteFile (m_FileName);
根據(jù)上文提出的方法,對(duì)于多進(jìn)程仿真系統(tǒng)中實(shí)現(xiàn)多場(chǎng)景的驅(qū)動(dòng)框圖如圖2所示,其中圖中省略了VP進(jìn)程部分對(duì)實(shí)例驅(qū)動(dòng)和特效模塊用不同的類進(jìn)行封裝的結(jié)構(gòu)圖。圖2中顯示了兩個(gè)進(jìn)程間實(shí)現(xiàn)的流程圖。進(jìn)程間采用內(nèi)存文件映射和消息機(jī)制進(jìn)行數(shù)據(jù)通信,MFC主進(jìn)程負(fù)責(zé)創(chuàng)建終止VP仿真進(jìn)程。
圖2 MFC與VP進(jìn)程間通信流程圖Fig.2 Communication flow chart between MFCand VPprocesses
文中介紹了虛擬場(chǎng)景中漫游驅(qū)動(dòng)的實(shí)現(xiàn),在基于MFC的Vega Prime框架的設(shè)計(jì),具體分析了多場(chǎng)景切換中遇到的問(wèn)題,從面向?qū)ο蟮慕嵌?,提出了較為全面和穩(wěn)定的解決方法。此方法可以滿足多場(chǎng)景的數(shù)據(jù)共享和快速通信,具有廣泛的通用性和實(shí)用性。
[1]張波,丁瑩.基于Vega Prime的視景仿真系統(tǒng)的研究[C]//第六屆全國(guó)仿真器學(xué)術(shù)會(huì)議,2007:261-265.
[2]車(chē)忠志,孫雪雁.MFC應(yīng)用程序基本框架分析[J].農(nóng)業(yè)網(wǎng)絡(luò)信息,2010,29(9):154-147.CHE Zhong-zhi,SUN Xue-yan.The analysis of MFC application framework[J].Agriculture Network Information,2010,29(9):154-147.
[3]王嬌艷,康鳳舉.MFC框架下多通道視景仿真技術(shù)[J].火力與指揮控制,2010,35(7):130-132.WANG Jiao-yan,KANG Feng-ju.Research on multi-channel based on Vega Prime under MFC framework[J].Fire Control&Command Control,2010,35(7):130-132.
[4]劉淑霞,范雄飛,孟艷.基于MFC的Vega Prime程序框架研究 [C]//系統(tǒng)仿真技術(shù)及其應(yīng)用學(xué)術(shù)會(huì)議論文集,2009:418-421.
[5]胡梓楠,于勁松.基于MFC框架的Vega Prime軟件集成技術(shù)的研究[J].系統(tǒng)仿真學(xué)報(bào),2007,21(14):4291-4295.HU Zi-nan,YU Jin-song.The research on the framework of vegaprime software integration technology based on MFC[J].Journal Of System Simulation,2007,21(14):4291-4295.
[6]孫科峰,李潔.基于Vega Prime的多場(chǎng)景仿真系統(tǒng)框架[J].計(jì)算機(jī)仿真,2007,24(12):193-196.SUN Ke-feng,LI Jie.Vrious scense renging system frame based on Vega prime[J].Computer simulation,2007,24(12):193-196.
[7]李軍,王邵棣,常建剛,等.基于Vega的視景驅(qū)動(dòng)軟件的分析與設(shè)計(jì)[J].系統(tǒng)仿真學(xué)報(bào),2003,15(3):397-401.LI Jun,WANG Shao-di,CHANG Jian-gang,et al.Aalysis and design of renging software based on Vega[J].Journal of System Smulation,2003,15(3):397-401.