由于課題需要,我們實(shí)驗(yàn)室對(duì)openflight的API進(jìn)行了一些初步研究,下面是我們總結(jié)和體會(huì),也可以當(dāng)成一個(gè)初級(jí)教程,希望對(duì)大家有所幫助。 |
Openflight API是一個(gè)包含頭文件和鏈接庫(kù)的C語(yǔ)言庫(kù),它提供了訪問Openflight數(shù)據(jù)庫(kù)和Creator模型系統(tǒng)的接口方法。通過API可以進(jìn)行Openflight模型的轉(zhuǎn)換、實(shí)時(shí)的模擬仿真、自動(dòng)建模以及通過插件的形式對(duì)Creator進(jìn)行功能擴(kuò)展。
整個(gè)API由四個(gè)模塊構(gòu)成:
Read: 讀取,遍歷和查詢Openflight數(shù)據(jù)庫(kù)中的元素;
Write: 寫入,創(chuàng)建和修改Openflight數(shù)據(jù)庫(kù)中的層級(jí)節(jié)點(diǎn);
Extensions: 擴(kuò)展,定制Openflight格式的文件,自定義擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)以便給已存在的節(jié)點(diǎn)添加新屬性或創(chuàng)建自定義節(jié)點(diǎn);
Tools: 根據(jù)需求創(chuàng)建嵌入式的Creator插件;
常用的API自定義的數(shù)據(jù)類型包括:
mgstatus:指示程序運(yùn)行狀態(tài)的整型值,若出錯(cuò)則返回非零值,否則返回0;
mgrec:存儲(chǔ)模型數(shù)據(jù)庫(kù)中的記錄集對(duì)象,一般是層級(jí)結(jié)構(gòu)的節(jié)點(diǎn);
mgcode:mgrec類型的整型標(biāo)識(shí)符,每個(gè)mgrec對(duì)象具有唯一的整型值;
在編寫?yīng)毩⒌目蓤?zhí)行程序時(shí),調(diào)用API函數(shù)前必須先調(diào)用mgInit()初始化,即標(biāo)志開始使用API庫(kù);所有API函數(shù)調(diào)用結(jié)束后必須調(diào)用mgExit()中止API庫(kù)運(yùn)行。
Openflight開發(fā)庫(kù)提供可控的消息機(jī)制。應(yīng)用程序的消息可以被截獲并打印以提示代碼的運(yùn)行狀態(tài),并且該機(jī)制可以通過調(diào)用mgSetMessagesEnable(bool)被關(guān)閉或開啟,這無(wú)疑為我們調(diào)試程序帶來(lái)了極大的便利。在控制臺(tái)或MFC環(huán)境下的可執(zhí)行程序的調(diào)試中,可以利用mgGetLastError()截獲出錯(cuò)處的消息并利用printf()或者M(jìn)essageBox()實(shí)時(shí)地反饋給我們;而在Creator環(huán)境下則可通過mgSengMessage()在狀態(tài)欄中提示程序的執(zhí)行信息。
Openflight文件是以數(shù)據(jù)庫(kù)格式存取的。API提供了數(shù)據(jù)庫(kù)的新建、打開、保存、寫入、關(guān)閉等方法。對(duì)于任意的記錄集,均可以通過調(diào)用mgRec2Db方法轉(zhuǎn)換成獨(dú)立的數(shù)據(jù)庫(kù)文件。
Openflight格式簡(jiǎn)介
OpenFlight格式及API OpenFlight是Multigen開發(fā)的,在視覺仿真領(lǐng)域最為流行的標(biāo)準(zhǔn)文件格式。OpenFlight采用幾何層次結(jié)構(gòu)和節(jié)點(diǎn)(數(shù)據(jù)庫(kù)頭節(jié)點(diǎn)、組、物體、面等)屬性來(lái)描述三維物體,如圖2.6所示。允許用戶直接對(duì)層次結(jié)構(gòu)及節(jié)點(diǎn)進(jìn)行操作,保證從大型數(shù)據(jù)庫(kù)到物體單個(gè)頂點(diǎn)的精確控制[19]。
它的邏輯層次結(jié)構(gòu)及細(xì)節(jié)層次、截取組、繪制優(yōu)先級(jí)、分離面等功能,使得圖像產(chǎn)生器知道何時(shí)及如何繪制三維場(chǎng)景,極大地提高了實(shí)時(shí)系統(tǒng)的性能。
圖2.6 Openflight數(shù)據(jù)庫(kù)層級(jí)結(jié)構(gòu)
一個(gè)OpenFlight數(shù)據(jù)庫(kù)的層次結(jié)構(gòu)被作為一個(gè)文件存儲(chǔ)在磁盤上。文件由線性的二進(jìn)制流記錄組成。字節(jié)在文件中的存儲(chǔ)順序是按照Big Endian方式存儲(chǔ)的(即0x1234在內(nèi)存中存儲(chǔ)順序?yàn)?x12 0x34)。所有的OpenFlight記錄都是以4個(gè)字節(jié)序列開始的。序列的前兩個(gè)字節(jié)標(biāo)識(shí)了記錄類型(opcode),后兩個(gè)字節(jié)指明記錄的長(zhǎng)度。在這種結(jié)構(gòu)規(guī)律下,OpenFlight記錄很容易從磁盤中讀取并加以分析[20]。
◇ 所有的OpenFlight記錄的長(zhǎng)度都是4字節(jié)的整數(shù)倍。當(dāng)一個(gè)記錄不到4字節(jié)的整數(shù)倍時(shí),會(huì)自動(dòng)填補(bǔ)成4的整數(shù)倍。在某些情況下,OpenFlight記錄被填補(bǔ)成8字節(jié)的整數(shù)倍。
◇ 所有記錄的長(zhǎng)度(所有記錄的值)同所有值的偏移值都是以字節(jié)為單位的。
◇ 除非明確的說明,位域和掩碼都是從0開始計(jì)算的(例如第一個(gè)位的位號(hào)為0)。
◇ 除非明確的說明,矩陣記錄的元素是以行為主要順序存儲(chǔ)在OpenFlight文件中的。
◇ 所有OpenFlight記錄的長(zhǎng)度都被限制在能被2個(gè)字節(jié)或16個(gè)bit位(65535)表示的最大長(zhǎng)度。對(duì)于長(zhǎng)度固定的記錄,這個(gè)最大長(zhǎng)度是足夠的。
2.2.2 Openflight文件的創(chuàng)建和讀寫方法
一個(gè)基本的完整Openflight文件創(chuàng)建讀寫算法流程如圖2.7所示,主要包括程序初始化,創(chuàng)建數(shù)據(jù)庫(kù)頭節(jié)點(diǎn),創(chuàng)建組節(jié)點(diǎn)和其他特殊節(jié)點(diǎn),繪制幾何節(jié)點(diǎn),建立節(jié)點(diǎn)間的層級(jí)關(guān)系,寫入并關(guān)閉數(shù)據(jù)庫(kù),程序退出等步驟。
Openflight文件讀寫流程
控制臺(tái)主參數(shù)初始化->新建數(shù)據(jù)庫(kù)頭節(jié)->寫入數(shù)據(jù)庫(kù)mgWriteDb(db)->創(chuàng)建特殊節(jié)點(diǎn)(光、聲…) (fltLight,fltSound,fltLod…)->賦予記錄集屬性(光照、聲音…)->通過3維坐標(biāo)創(chuàng)建頂點(diǎn)mgSetCoord3d->創(chuàng)建子面addVertex()->創(chuàng)建面集makePoly()->創(chuàng)建體節(jié)點(diǎn)fltObject->創(chuàng)建組節(jié)點(diǎn) fltGroup->寫入數(shù)據(jù)庫(kù)mgWriteDb(db)
組件式開發(fā)方法
Creator是商品化的虛擬現(xiàn)實(shí)建模工具,因此,軟件源碼是不可獲取的。但是,在Openflight API平臺(tái)上可以通過“Plug_in”的方法對(duì)Creator的功能進(jìn)行擴(kuò)展。所謂“Plug_in”,就是Creator的嵌入式組件。在Creator的加載過程中,除了加載Multigen公司提供的基本模塊外,主程序亦會(huì)加載系統(tǒng)環(huán)境變量MPI_CREATOR_PLUGINDIR所指示的目錄下的所有第三方的擴(kuò)展模塊,這些模塊都是以動(dòng)態(tài)鏈接的方式被Creator加載的。也就是說,只要通過Openflight API將需要的功能編譯成動(dòng)態(tài)鏈接庫(kù)文件并放在Creator加載路徑下即可以實(shí)現(xiàn)基于Creator平臺(tái)的二次開發(fā)。
Creator提供了六種插件形式的功能擴(kuò)展[21]:
◇ 數(shù)據(jù)庫(kù)導(dǎo)入器:將不支持的數(shù)據(jù)庫(kù)格式導(dǎo)入至Creator中
◇ 數(shù)據(jù)庫(kù)導(dǎo)出器:Openflight格式導(dǎo)出成外部數(shù)據(jù)庫(kù)格式供其他應(yīng)用程序使用
◇ 圖像導(dǎo)入器:將不支持的圖像或紋理導(dǎo)入Creator中
◇ Viewer:相當(dāng)于數(shù)據(jù)庫(kù)的視圖,可供查詢但不能被修改
◇ Editor:修改數(shù)據(jù)庫(kù)的編輯器
◇ 輸入設(shè)備:將外部輸入設(shè)備獲取的幾何信息轉(zhuǎn)換成Creator模型
dll 文件可以被Creator加載,必須在編碼階段注意以下幾點(diǎn):
首先模塊在編碼階段必須聲明并注冊(cè),其形式如下:
mgDeclarePlugin(
/*開發(fā)者名稱*/ MVENDOR_NUAACBT,
/*插件名稱*/ "My Plug_in",
/* UUID */ "8c1560c0-083d-11d5-91a0-006008a37a65");
其中UUID是指在一臺(tái)機(jī)器上生成的數(shù)字,它保證對(duì)在同一時(shí)空中的所有機(jī)器都是唯一的。可以利用Visual Studio提供的Uuidgen工具生成這個(gè)獨(dú)立的標(biāo)識(shí)。
其次必須有dll入口函數(shù)的聲明和初始化以供Creator加載模塊時(shí)調(diào)用,同樣的,相應(yīng)的出口函數(shù)也必須被定義,它負(fù)責(zé)模塊的中止和退出。其關(guān)鍵編碼如下:
MGPIDECLARE(mgbool) mgpInit ( mgplugin plugin, int* argc, char* argv [] ){
moduleHandle = mgGetModuleHandle ( plugin ); //獲取模塊句柄
Resource = mgLoadResource ( moduleHandle ); //加載模塊中定義的資源
initOk = InitMenu ( plugin, Resource, argc, argv ); //模塊入口
}
MGPIDECLARE(void) mgpExit ( mgplugin plugin ){
ExitPlugin ( plugin ); //中止模塊執(zhí)行
mgUnregisterAllTools ( plugin ); //清空模塊注冊(cè)信息
mgUnloadResource ( Resource ); //釋放資源
}
最后必須定義啟動(dòng)功能模塊的GUI(Graphic User Interface)環(huán)境。所謂GUI,是指用戶與Creator界面交互的接口,如菜單、對(duì)話框、工具條等。比如在Creator的Info菜單下添加名為“My Plug_in”的子菜單以實(shí)現(xiàn)自定義功能,關(guān)鍵編碼形式如下:
mgbool InitMenu ( mgplugin plugin, mgresource resource, int* argc, char* argv [] ){
pluginTool = mgRegisterViewer (
plugin, "MyPlugin", StartMenuFunc, //子菜單響應(yīng)函數(shù)
resource, MTA_VERSION, "1.0", MTA_MENULOCATION, MENU_INFO, MTA_MENULABEL, "My Plug_in", MG_NULL);
return ( pluginTool ? MG_TRUE : MG_FALSE );
}
以上關(guān)鍵的步驟只是向Creator中加載了一個(gè)空的菜單項(xiàng),要實(shí)現(xiàn)更多的交互功能,就要用到對(duì)話框、控件以及Creator內(nèi)部的數(shù)據(jù)交換(DDX)機(jī)制。
對(duì)話框和控件是預(yù)先在工程的資源里定義的,可以利用Visual C++的資源編輯器創(chuàng)建對(duì)話框以及編輯框、進(jìn)度條、單選和復(fù)選框、列表框等標(biāo)準(zhǔn)的Windows控件。但是,對(duì)話框和控件的交互機(jī)制卻和Windows有很大的不同。對(duì)話框分為如下幾種類型[21]:
消息對(duì)話框:類似于Windows的彈出式警告框,通過調(diào)用mgMessageDialog實(shí)現(xiàn)
文件對(duì)話框:類似于Windows的文件打開保存對(duì)話框,通過調(diào)用mgPromtDialogFile直接創(chuàng)建并彈出
模態(tài)對(duì)話框:即Windows用于數(shù)據(jù)交換的通用對(duì)話框,其特點(diǎn)是不關(guān)閉則無(wú)法對(duì)程序繼續(xù)操作,通過調(diào)用mgResourceModalDialog從資源文件創(chuàng)建并彈出。
無(wú)模態(tài)對(duì)話框:類似模態(tài)對(duì)話框,唯一區(qū)別在于不關(guān)閉可以對(duì)程序的其他部分繼續(xù)操作,通過調(diào)用mgResourceGetDialog從資源文件創(chuàng)建,調(diào)用mgShowDialog彈出。
API也提供了大量操縱控件的函數(shù),控件從工程的資源中獲取后可以通過這些函數(shù)初始化或進(jìn)行相應(yīng)的變更。這和Windows的控件編程方法類似,具體不再贅述。
控件和主程序之間的數(shù)據(jù)交換機(jī)制是通過回調(diào)函數(shù)實(shí)現(xiàn)的。假如按下對(duì)話框中的一個(gè)按鈕控件OK對(duì)應(yīng)的操作是在函數(shù)OKisClicked()中實(shí)現(xiàn)的,那么可以定義這樣的回調(diào)函數(shù)OkCallback去實(shí)現(xiàn)操作:
if ( gui = mgFindGuiById (dialog, MGID_OK ) ) //返回標(biāo)識(shí)為OK的控件
mgSetGuiCallback ( gui, MGCB_ACTIVATE, OkCallback,userData );
static mgstatus OKCallback ( mggui gui, mgcontrolid controlId, mgguicallbackreason callbackReason, void *userData, void *callData ){
switch ( callbackReason ){ //對(duì)控件回調(diào)消息的相應(yīng)
case MGCB_ACTIVATE:
if ( mgControlIdsMatch ( controlId, MGID_OK) )
OKisClicked(); //若按鈕OK激活,則調(diào)用相應(yīng)的操作函數(shù)
}……