2008年12月12日 星期五

雙核心(Twin Drive)啟動

最近新的動畫,鋼彈00驚險刺激的雙核心啟動可說是工作上的最佳寫照。

努力了一整個月,並在最後的兩天不眠不休地趕工。
然後在最後驗機日的上午0700左右,完成了所有教點,初步地讓整個系統自動運轉。
接著下午讓客戶觀看時,完整地展現了兩台2噸重的Robot手臂。
掛上走行軸(讓Robot手臂可整座移動的裝置)後,順暢地同步各別進行工作。
在這個的瞬間,真的有種Twin Drive發動的感覺。

雖然說同步使2個以機器人手臂同時動作在業界不算少見,
甚至進行的動作還比我們的精密。
不過在台灣,我們應該算是第一組讓這麼大的兩組還掛上走行軸進行作業,
想想還真是有種暢快感。

2008年11月20日 星期四

源氏物語千年記

http://genji-anime.com/
配音員發表

光源氏 役
櫻井 孝宏
弘徽殿の女御 役
藤田 淑子
紫の上 役
遠藤 綾
藤壺の女御 役
玉川 紗己子
桐壷帝 役
堀内 賢雄
頭の中将 役
杉田 智和
葵の上 役
平田 絵里子
六条の御息所 役
鶴 ひろみ

配音員列表一出來,這還真的是令人訝異耶
三位男角色還好,都是近年較常見的
重點是女角色的部分,竟然有許久不見的兩位老牌配音員
藤田 淑子是一休和尚
鶴 ひろみ則是布馬與阿圓(古靈精怪)
尤其阿圓竟然是配怨婦「六條御息所」
真是令人吃驚

雖然源氏物語的改編作品很多,電視劇、電影或是漫畫所在多有
但以較為接近原作(漫畫版 大和和紀:源氏物語)的部分來做成動畫卻是第一次
希望能夠成功
不過還不清楚動畫版人物設定是誰
少女漫畫的畫風要轉成動畫通常不太好掌握
要能成功這是個大重點啊

2008年11月11日 星期二

QuickGUI 8.09小試用心得


在搞定了Ogre 1.6.0之後,接下來就是曾經有投注過心力的QuickGUI測試。
不過因為版本大改特改,第二個release的版本已經從0.9.7一口氣跳到了8.09來了。
所以在重新使用上遇到了一些的問題,像是QuickGUI::registerScriptParser()的部分
要設法放在ResourceGroup讀取前,QuickGUI才能使用。
然後建立各視窗與按鈕等Widge類別前,
必須要先搞一個xxxDesc,才能建立。
重新習慣顯著有點麻煩。

當然不少地方也大幅改進,像是每個文字都可以獨立顯示其字型與顏色。
已經有辦法從寫好的Script檔直接著創建整個視窗Layout。
自定各各視窗Skin也有專屬的文字格可以使用等...
不過問題也不少,像是依然要自己想辦法動態載入新的中文字,無法像CEGUI直接處理。
Layout讀取的部分使用的是原Ogre的ScriptReader類別,遇上UTF-8的中文就死定了。

大體上還算是個不錯的GUI,效能上因為與Ogre緊密結合,還算OK。
那在稍微看過原始碼之後,開始覺得既然有把Ogre::Font給整合進去的話。
無法動態載入中文字感覺有點虛。來試著改改看吧。

2008年11月5日 星期三

Ogre 1.6.0 FINAL RELEASE


Ogre 1.6.0
在歷經了近兩個月後,1.6.0的最終版本終於問世,這個消息著實令人感到興奮。在上次RC1版本發表後,那些新的特色確實令人印象深刻。不過對於一直以GCC(Mingw)做為編譯器的我而言,RC1的版本還未完全相容於此一GNU的編譯器,是蠻傷腦筋的一件事。而另一個問題就是1.6.X版開始支援DX10,這對於大部分尚在使用XP的玩家,與使用Mingw來做編譯器的族群而言,實在是個頭痛的開始。雖然OpenGL的部分也有做強化,不過RC1跑Demo的經驗來說,會有著DX10 OK而OpenGL不能跑的狀況,反之也有發生。
但是,也才第一個版本,這些都可以慢慢來處理。而且在已經出社會,有工作在身的狀況下,我Ogre也還算一直在皮毛打轉,1.4.X還沒用的很熟咧。真的很佩服那些有工作還能投注相當心力於免費軟體開發的人們,謝謝你們。

2008年11月1日 星期六

塔塔加高山洗禮

在經歷過上週騎過台中縣三坑的復健練習
把近一個多月沒騎過上坡路段的感覺找回來後
終於是有一點點的信心可以挑戰一下海拔2600多公尺的塔塔加
不過對於同伴從水里出發的邀約還是敬謝不敏
我想,還是乖乖地從同富出發就好
畢竟海拔高度差近2000公尺可不是開玩笑的

清晨5:30分起床,起了個大早,準時於0600上龍井交流道
然後在0635到達名間交流道下與同伴會合
耶,終於輪到不是我睡過頭,不過這種事情其實也沒什麼好高興的
稍微等了一下同伴後重新出發,接著完成與補給車會合
與從水里出發的同伴相會,並發出上車邀約等事情後
0830終於到達了同富國中小學前,正式地開始了一整天的行程

一開始只覺得還算相當輕鬆,相較於上週的坡度,實在相當的小兒科
但是總長超過40KM的上坡旅程還是不可小看
尤其海拔漸漸上昇之後,空氣相對稀薄,所以一開始算放慢了一點點的速度
大約以9~12Km/H的速度一路向上
由於初始總是體力滿滿,不到兩個小時就來到了台21線116K的望高茶園
這邊大約騎了15公里左右


稍做休息,補充水分與糖份,並等後落單同伴確認一下狀況後
1030開始再做出發。
這時實在是太輕敵與看得起自己的體力了。
此時速度仍是不變,想說已經騎了大約1/3的路程了,還是生龍活虎地
腿力仍然沒有什麼異狀,應該是OK啦
一路騎上到觀峰開始休息就覺得不太對勁
雖然對於只剩下約一半多一點(25km左右,全長約45km)
但已經不敢再堅持逼近9Km/H左右的速度
開始以8Km/H為主,然後比較徒一點就6~7Km/H
然後大盤退到最小,開始祈禱腿力不要耗盡

果不其然,在接近中午12點過後,於1230開始正式覺得腿在酸了
雖然還沒有到痛的境界,也只是隱隱約約,明顯是個警訊了
同時感受到的還有速度方面,也一整個降低,此時只來到127Km左右的距離
騎了同樣的兩小時左右,只有推進不到15公里
還逼近到只有10Km的距離
不過肚子餓了,還是得先填飽肚子,就小休一下再說

休息了片刻,再騎了一個小時左右
終於順利穿過某個遂道,不過也只有這一個小時左右的腿力可以發揮
接著開始進入毅力決定一切的關鍵時刻
是的,此該恨不得從觀峰出發的同伴可以馬上下滑
開著補給車連人帶車把我接到塔塔加上
我騎得好酸啊~~~~~~~~~~Orz
是沒有同伴說高海拔所特有的空氣稀薄,吸不到氧氣的感覺
休息個片刻,也是馬上可以上鐵馬來騎個100多公尺
可是馬上腿就會酸,明顯已經沒力了,跟跑20KM半程馬拉松差不多

再這樣休休騎騎一個半小時左右
中途終於看到第三出發點出發的同伴終於下滑,準備開補給車
也遇到了從水里上來的同伴從後面追了上來
看到了友人的加油簡訊,稍稍產生了連續騎3公里的拚勁...
瞄到山壁上標高2310公尺的牌子,訝異於已經爬這麼高的實力
還有自己已經沒力了,還騎在車上心虛地接受開車上來旅客的加油聲 XD
1500左右,此時還差倒數第二站,夫妻樹約不到3公里了

當然這也意味著離最高點,今日的終點塔塔加已經只剩下5公里了
可是無奈於現實的殘酷:
  1. 晚上還要上課
  2. 我是自己一個人開車
  3. 還有天生的惰性 XD
最終我還是妥協只止於夫妻樹,畢竟最終的2公里可是要上昇140公尺的高度
就算我用牽的上去,只會超過預定的離開時間1600
同時山上天黑的快,而天黑的山,是很危險的(都是理由嘛 XD)

終於放開一切,拚著預留的最後腿力
1530左右,到達了夫妻樹

飲恨於此呀 Orz
不過,對於一位體重超過80公斤的胖子
已經相當不錯了嘛,對吧(自我安慰中)

然後車子上補給車,到達塔塔加後
開心地享用熱騰騰的湯麵
與不是很冰涼的啤酒
真的通體舒暢呀
如果腿的耐力再高一點,早上出發再早一點
這目標應該可以達成吧
不過,這樣就有個理由來個第二次啦!

2008年10月13日 星期一

假日的小探險~高美濕地~

週日下午,該辦的事都差不多了
對於自己又沒有早起去騎個山路感到後悔
決定延續上週騎到火力發電廠的平地探險之旅
但火力發電廠邊實在太無趣啦
隨手翻開地圖再瞄了一下
瞬間就想起同在台中港區附近的高美濕地

拚到高美濕地再回來
跟想像中的差不多,除了東北季風的強勁讓我覺得有些吃力外
也只比火力發電廠稍稍遠一點,來回大約1個小時半左右
人很多,尤其是情侶,所以風景很不錯(什麼嘛.....)
還有看到有人在拍婚紗照,那個長長的禮服就這樣整個垂到地上
沾到泥土,碰到海水,我想洗的人會很嘔吧(真沒浪漫情懷....)

提到高美濕地,那個巨大的風力發電機是著名的景色
那對新人在拍婚紗照的同時,也是以此為背景主題
可是無可救藥的我,怎樣都揮之不去的印象
是村雨良與城茂的戰鬥
假面騎士Spirits第二部最後一話,ZX vs 強人
果然無可救藥啦

2008年10月8日 星期三

Ogre中文輸入 Final

最新的精簡修改版本→Ogre中文輸入Final修改
想看長篇心得的可以繼續看

嗯…還是從頭講起好了
Ogre是個免費而又強大的OpenSource 3次元圖像引擎
但對於想開發中文(或所有其它多字元)的程式來說
輸入法的無法使用是個大問題(現在顯示已經沒什麼大問題)

原先,所有Ogre的Windows訊息全部被封裝在OgreWindowEventUtilities裡(1.4.X版)
所以實行中文輸入所必須取得的IME訊息均無法正常取得
目前由免費打工仔在簡體網站所製作修改支援中文輸入的部分主要是從這裡切入
不過必須修改原始碼而動到LGPL授權是一個問題
這邊作者也透露了另一個不修改原始碼的可能,直接自行建立自己的視窗,然後手動建立Ogre的Render視窗為子視窗,就這樣取得母視窗的主控制權,進而建立自己想要的訊息處理。

OK,講起來很容易,問題在於要怎麼寫?
Ogre的中國網站只先簡單地點出這段程式。
//假设之前已经执行完创建窗口以及Ogre::Root对象的过程
//hWnd为窗口句柄,root为Ogre::Root类型实例
Ogre::NameValuePairList params;//构造参数
std::stringstream ss;
ss<<hwnd; //窗口句柄
params["externalWindowHandle"] = ss.str();//把窗口句柄做为字符串形式设置到参数中
root->initialise(false);//Ogre::Root对象初始化参数为false,表示手动创建渲染窗口
//下面创建渲染窗口
Ogre::RenderWindow * window = _root->createRenderWindow("name", //名称
        width,//宽度
        height, //高度
        false, //是否全屏显示
        &params);
後續交給讀者...

喔,這可是個說簡單不簡單,說難也不會很難的問題。
如果已經直接讀完ExampleApplication與ExampleFrameListener範例程式碼的人。
手動建立一個Ogre視窗不會很難,你只要照著那個步驟,一個一個建立就行。
可是完全沒用到Windows的基本型式,這也無法先手動產生一個母視窗。
更何況訊息處理咧?還有IME的處理啊!
對於初學者來說,全部都很陌生領域。

所以這邊就大概講一下怎麼繼承ExampleApplication與ExampleFrameListener來做中文輸入。
以改造核心處理的ExampleFrameListener開始。首先建立一個檔案ExampleCFrameListener.h,
並用檔名做繼承的類別名稱。然後除了繼承的,我們需要再加入Singleton與IME用的表頭檔。
#include "ExampleFrameListener.h"
//為了使用Singleton
#include "OgreSingleton.h"
//使用IME函式庫
#include "imm.h"  

建構式建立的地方請直接參考後面的程式碼。
接著就是我們必須設定兩個空的函數去取得轉換完成與還在轉換中的字串
///這邊是取得轉換完成的字串,直接傳給GUI System的injectChar
virtual void GetIMECompResultString(const Ogre::UTFString& tempString) { } ///這邊是取得轉換中的字串,主要是給像新注音需要另外產生視窗來顯示暫時性文字的 virtual void GetIMECompString(const Ogre::UTFString& tempString) { }

然後在這邊加入視窗訊息處理函式。
static LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

這邊有點麻煩的是此函式必須為static設定,所以在裡面的處理需要取得指標來運算。
我們為了把GetIMECompResultString與GetIMECompString往這裡丟,所以才設定此物件有Singleton功能以方便馬上利用(註1)。如果還有其它的方式,麻煩請告知。

既然都到這裡了,那當然,就是把上面那兩個函數放在WM_IME_COMPOSITON的訊息裡囉。
不過當然還是要加一些IME本身的字串取得與處理。
//自己處理的IME轉換
case WM_IME_COMPOSITION:

HIMC hImc = ImmGetContext(hwnd);
//正式取得轉換的字串
if (lParam & GCS_RESULTSTR)
{
//ImmGetCompositionStringA( hImc, GCS_RESULTSTR, tCharPtr, dwBufLen );
DWORD dwBufLen;
wchar_t* tCharPtr;
dwBufLen = ImmGetCompositionStringW( hImc, GCS_RESULTSTR, 0, 0 );
if(dwBufLen > 0)
{
tCharPtr = new wchar_t[dwBufLen];
memset( tCharPtr, 0, sizeof(wchar_t) * dwBufLen );
ImmGetCompositionStringW( hImc, GCS_RESULTSTR, tCharPtr, dwBufLen );
tempString = tCharPtr;
ExampleCFrameListener::getSingletonPtr()->GetIMECompResultString(tempString);
}
}// if(lParam...
//取得轉換中的字串
else if (lParam & GCS_COMPSTR)
{
//ImmGetCompositionStringA( hImc, GCS_RESULTSTR, tCharPtr, dwBufLen );
DWORD dwBufLen;
wchar_t* tCharPtr;
dwBufLen = ImmGetCompositionStringW( hImc, GCS_COMPSTR, 0, 0 );
if(dwBufLen > 0)
{
tCharPtr = new wchar_t[dwBufLen];
memset( tCharPtr, 0, sizeof(wchar_t) * dwBufLen );
ImmGetCompositionStringW( hImc, GCS_COMPSTR, tCharPtr, dwBufLen);
tempString = tCharPtr;
ExampleCFrameListener::getSingletonPtr()->GetIMECompString(tempString);
}
else
ExampleCFrameListener::getSingletonPtr()->GetIMECompString("");

}// if(lParam...
ImmReleaseContext(hwnd, hImc);
break;

之所以要取得轉換中的字串,其實是為了新注音。
因為那個還沒轉好的字(正式輸出)是不可能就主動顯示在要輸入的地方,而舊注音有子視窗飄著沒問題。當然按上下鍵選字時,新注音的視窗也會跳出,不過還是看不到一整串的字。
所以才加了GetIMECompString來加以處理這部分。
先不管Singleton的額外設定,輸入中文最主要的部分在此。

再來是ExampleApplication的改造。一樣建個ExampleCApplication.h的檔案,然後引入舊的ExampleApplication.h與剛剛的ExampleCFrameListener.h。
單純繼承的部分就不說了,然後重點首先在於變數。
請加入
protected:
HINSTANCE mHInstance; // HInstance of application, for ime
HWND hwnd;//HWND of appliction for the new windows


然後再建立一個configure的函式修改如下:
if(mRoot->showConfigDialog())
{
// If returned true, user clicked OK so initialise
// Here we choose to let the system create a default rendering window by passing 'true'
/// But the chinese need a manual window. Set it false for manual.
mWindow = mRoot->initialise(false);

/// then create a new window for IME
setupWindow();
return true;
}


接著就是手動建立視窗的重頭戲了。
理所當然我們建立一個setupWindow()的新函式在後面。
接著取得目前的 hinstance
mHInstance = GetModuleHandle( NULL );

註冊一個WNDCLASS如下:
WNDCLASS wincl = { 0, ExampleCFrameListener::WindowProcedure, 0, 0, mHInstance,
LoadIcon(0, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW),
(HBRUSH)GetStockObject(BLACK_BRUSH), 0, "OgreChineseWnd" };

RegisterClass (&wincl);

請注意那個ExampleCFrameListener::WindowProcedure就是加在這裡。

接著我們建立並指定視窗的大小位置與視窗類別等等
取得其HWND
hwnd = CreateWindow("OgreChineseWnd", "", WS_OVERLAPPEDWINDOW,
left, top, width, height, HWND_DESKTOP, 0, mHInstance, 0);

這時我希望有個不同過去的中文化標題
SetWindowTextW( hwnd, L"Ogre中文輸入測試視窗");

再來就是照著最先的部分,讓Ogre設定一個同樣大小的視窗,為子視窗
Ogre::NameValuePairList params;
params["externalWindowHandle"] = StringConverter::toString((int)hwnd);
mWindow = mRoot->createRenderWindow("Ogre Render Window", width, height, false, &params);


最後不要忘了,讓母視窗顯示,才不會什麼都看不到
ShowWindow (hwnd, SW_SHOWNORMAL);


這樣大致上的設定就差不多了。
接下來的部分,首先要繼承上面的新類別(廢話 XD)
然後編譯的參數要加入imm32,這樣IME才有作用。
當然也不能忘了,要讓GUI System或Ogre能顯示中文字才有辦法看得到輸入
這部分就不加贅述了
要補充的是輸入的部分。
以CEGUI為例:
在CEGUI::System::getSingleton().injectChar( arg.text );前請加入這一行
if ( !ImmIsIME(GetKeyboardLayout(0)) )
讓IME啟動時,無法輸入英數,才不會在轉換前多了一堆自己Keyin的亂碼。
加入GetIMECompResultString的函式如下:
void GetIMECompResultString(const Ogre::UTFString& tempString)

Ogre::UTFString temp = tempString;
for (int i = 0; i < temp.size();i++)
{
CEGUI::System::getSingleton().injectChar( temp[i] ); }

injectChar只能處理單一字元,所以才會用迴圈一個一個輸入整個字串。
另一個GUI System "QuickGUI"的部分大同小異,要多記得把Ogre的中文顯示設定好(它沒辦法像CEGUI本身自己支援Unicode轉碼,要靠Ogre自已顯示),並使用setSupportedCodePoints
把要中文的字碼先往裡面塞,不然就算有輸入,它也會因為找不到對應的,而自動取消,會白忙一場。

最後,修改的完整範例在這裡:
ExampleCFrameListener.h
ExampleCApplication.h

一些地方略有差異,最主要就是第二個檔案的setupWindow的函式有參考RenderSystem的原始碼
可以自動生成置中且與一開始設定相同大小的視窗。
我想在Win32的環境下,使用中文輸入應該就這樣子吧。
當然還有一些IME的功能沒有很完整地利用,不過我想這個任務就交給其它有更深需求的人來做吧。我想寫的東西只要能輸入名字就很足夠了。

感謝在我研究IME過程給我幫助的網友,不然現在可能還是尾巴會帶出一堆亂碼來的版本。
另外測試的電腦有灌過Unicode補完計畫,我不清楚是否對IME有所影響。因為我所寫的是專門給寬字元(Wide Char)用的GetIme的函式,有可能會在不支援unicode的輸入法出差錯。
還有我的編譯器是mingw的gcc,VC還沒試著跑過,不過我想問題應該不大。

這東西其實搞起來難度不高,而且看來在1.4.X版出來後,就有辦法實做出來。
到底是搞中文的人少呢?還是即然已經有修改程式碼的辦法,就無所謂呢?
總之,斷斷續續,忙裡偷閒地搞了至少3年Ogre。
能實現一種中文輸入成功真的很高興。在此分享給大家。

註1:其實我還是個半生不熟的學習者,只知道可以拿來用,還不知道為什麼可以用,以及是否有其它延伸的問題。還忘多多海涵。