| 導購 | 订阅 | 在线投稿
分享
 
 
 

講解如何讓OGRE支持中文(二)

來源:互聯網  2008-05-19 00:44:16  評論

0.還是前言

如果你希望能看懂這篇文章,請先確定你已經看到了《讓OGRE支持中文》(http://www.gameres.com/Articles/Program/Visual/3D/OgreSupChn.htm),因爲本文是在上一篇文章的基礎上寫的,並且假設文件都已經按照上一篇文章進行了手術。但是如果你只想簡單的使用TTF字體,只要下載本文附帶的文件,重新編譯就可以了。

1.檢討

正如上回說的,我們已經實現了一個位圖的字體。但是當冷靜下來思考時,就能發現這種方法的諸多缺陷。讀入一個2048*2048的位圖,等于在顯存中保存一個2048*2048的貼圖,不說是否所有顯卡都支持這麽大的貼圖,單是每個Font字體類吃顯存的胃口,就足以令人心驚肉跳。如果定義足夠多的字體類,我想你的遊戲的配置要求,在某些方面足以超過《DOOM3》了。

而且這並不是唯一的缺陷,文字大小相對于位圖大小比例相差太大,導致浮點數的文字位置誤差很大,你可以在一個文字旁邊看到其他文字的影子。(雖然可以通過增加文字間距來解決。)還有點陣字體本身的缺陷,就是字形單一,不適合放大縮小,一些文字邊緣的馬賽克,足以熄滅任何玩家的投入感。

似乎TTF是唯一的解決之道。

2.基本知識

(1)TTF字體。

TTF是一種矢量字庫。我們經常可以聽到矢量這個詞,像是FLASH中的矢量圖形,在100*100分辨率下制作的flash,就算它放大爲全屏,顯示出的畫面也不會出現馬賽克。所謂矢量,其實說白了就是用點和線來描述圖形,這樣,在圖形需要放大的時候,只要把所有這個圖形的點和線放大相應的倍數就可以了。而且,在網站上有很多的TTF字庫可以下載,或者你可以去買一些專門的字庫光盤。然後在你發行你精心制作的遊戲時,可以順便捎上這些後綴爲。ttf的文件就行了。包括Quake這樣的驚世之作,也都是用的TTF字庫。

(2)FreeType2庫

在http://www.freetype.org,有一個FreeType的免費庫,而且是OpenSource的。它目前有2個版本:1.0和2.0.其區別在于,1.0只能讀取TTF格式的,而2.0支持更多的文件格式,在使用它之前請詳細閱讀所要遵循的Licence,以下是摘自FreeType2.0對字庫的支持列表:

TrueType fonts (and collections)

Type 1 fonts

CID-keyed Type 1 fonts

CFF fonts

OpenType fonts (both TrueType and CFF variants)

SFNT-based bitmap fonts

X11 PCF fonts

Windows FNT fonts

(3)「主體思想」

請參照炎龍工作室的《遊戲中漢字顯示的實現與技巧》這篇文章,可惜使用的是Windows API,作者千裏馬肝,上網上搜一下吧。

附帶說一句,上面兩條都是直接從這文章中剪切下來的,_<;

不要罵我啊,不知道算不算侵權呢。

3.動手術――比你想象的要麻煩

(1)

首先要告訴字體類,我們下一次渲染需要哪些字。以便字體類可以在需要的時候釋放不使用的字體。

在Font類中~

a.增加數據 bool mUsing[OGRE_NUM_GLYPHS]; 用來標記文字是否使用。

b.增加函數

inline void setUsing(std::vector<;unsigned long& caption)

{

memset(this-mUsing,0,sizeof(this-mUsing));

std::vector<;unsigned long::iterator it;

for(it=caption.begin();it!=caption.end();++it)

{

if(OGRE_GLYPH_INDEX(*it)<;OGRE_NUM_GLYPHS)

this-mUsing[OGRE_GLYPH_INDEX(*it)]=1;//標記文字爲使用

}

}

並在 void TextAreaGuiElement::updateGeometry() 中調用這個函數。

(2)

然後是修改void Font::createTextureFromFont(void);

Font類是通過void Font::createTextureFromFont(void)來把通過FreeType2分析好的英文字畫在一個2^n*2^n的貼圖上,然後再保存英文字的位置。

我們需要修改的是:

a.從函數中分離並保存畫字的FreeType2和輔助變量。我們通過一個類來保存和處理這些變量。

class TTFMsg

{

class Max//類中類,用來保存幾個"最大",

{

int nothing;//這個是用來占位的,沒意義,反正沒他就運行出錯,可能和數據對齊有關吧。

public:

int height;//文字最大高度

int width;//最大寬度

int bear;//最大空隙?

};

public:

FT_Library ftLibrary;//FreeType2用

FT_Face face;//FreeType2接口?

uint char_spacer;//文字空隙

SDDataChunk ttfchunk;//數據塊,用來保存ttf信息

FT_F26Dot6 ftSize;//FreeType2字體大小

std::pair<;uint,uint point;//在位圖上畫字的點

SDDataChunk imgchunk;//數據塊,用來保存位圖信息

bool dirty;//標記,看是否需要更新貼圖

Max max;//幾個最大

inline bool init(Font*

font) //這個是初始化函數在void Font::createTextureFromFont(void);中調用。

{

//以下都是初始化,大部分都是從void Font::createTextureFromFont(void);移植過來的

dirty=false;

if( FT_Init_FreeType( &ftLibrary ) )

Except( Exception::ERR_INTERNAL_ERROR, "Could not init FreeType library!",

"Font::Font");

char_spacer= 5;

FontManager::getSingleton()。_findResourceData(font-mSource,ttfchunk);

if( FT_New_Memory_Face( ftLibrary, ttfchunk.getPtr(), (FT_Long)ttfchunk.getSize() , 0, &face ) )

Except( Exception::ERR_INTERNAL_ERROR,

"Could not open font face!", "Font::createTextureFromFont" );

ftSize = (FT_F26Dot6)(font-mTtfSize * (1 <;<; 6));

if( FT_Set_Char_Size( face, ftSize, 0, font-mTtfResolution, 0 ) )

Except( Exception::ERR_INTERNAL_ERROR,

"Could not set char size!", "Font::createTextureFromFont" );

return true;

}

inline bool done()

{

//在Font的解構函數中調用的,本來應該調用下面兩個函數,但是不知道爲什麽一調用就出錯,不用倒沒事。

//FT_Done_Face(face);

//FT_Done_FreeType(ftLibrary);

return true;

}

inline bool getRect(Image::Rect & rect)//這個函數以後用的,是用來找到可以畫字的貼圖的空的位置

{

if(511<;point.second+max.height+char_spacer)

{

rect.right=rect.top=rect.left=rect.bottom=-1;

return false;

}

if(511<;point.first+max.width+char_spacer)

{

point.second+=max.width+char_spacer;

point.first=0;

if(511<;point.second+max.height+char_spacer)

{

rect.right=rect.top=rect.left=rect.bottom=-1;

return false;

}

}

rect.left=point.first;

rect.top=point.second;

rect.bottom=max.height;

rect.right=max.width;

point.first+=max.width+char_spacer;

return true;

}

};

上面的類定義在Font類中,在Font中增加 TTFMsg * mTTFMsg 數據,並在構造函數中 mTTFMsg=new TTFMsg;

正是修改void Font::createTextureFromFont(void);函數,主要幾點,首先是分離出很多變量和構造到TTFMsg類中了,然後是貼圖從2^n*2^n變成固定的512*512,爲什麽要這個數字呢,因爲256太小(廢話了)。能保證512*512大小的文本區不會出現不夠畫字的情況(所有英文+符號+沒有重複的漢字),這個安全區域基本上是夠用的。(什麽,你要畫滿屏幕的漢字?哪你自己看著改吧。)還有一個重要功能是,要找100個漢字,找出最高和最寬和最大空隙作參考。修改完成這個樣子了~

void Font::createTextureFromFont(void)

{

mTTFMsg-init(this);//初始化FreeType2

uint i, l, m, n;

int j, k;

FILE *fo_def = stdout;//啥意思?我看不明白

int max_height = 0, max_width = 0, max_bear = 0;

uint startGlyph = 33;

uint endGlyph = 167;

// 找英文的找出最高和最寬和最大空

  0.還是前言   如果你希望能看懂這篇文章,請先確定你已經看到了《讓OGRE支持中文》(http://www.gameres.com/Articles/Program/Visual/3D/OgreSupChn.htm),因爲本文是在上一篇文章的基礎上寫的,並且假設文件都已經按照上一篇文章進行了手術。但是如果你只想簡單的使用TTF字體,只要下載本文附帶的文件,重新編譯就可以了。   1.檢討   正如上回說的,我們已經實現了一個位圖的字體。但是當冷靜下來思考時,就能發現這種方法的諸多缺陷。讀入一個2048*2048的位圖,等于在顯存中保存一個2048*2048的貼圖,不說是否所有顯卡都支持這麽大的貼圖,單是每個Font字體類吃顯存的胃口,就足以令人心驚肉跳。如果定義足夠多的字體類,我想你的遊戲的配置要求,在某些方面足以超過《DOOM3》了。   而且這並不是唯一的缺陷,文字大小相對于位圖大小比例相差太大,導致浮點數的文字位置誤差很大,你可以在一個文字旁邊看到其他文字的影子。(雖然可以通過增加文字間距來解決。)還有點陣字體本身的缺陷,就是字形單一,不適合放大縮小,一些文字邊緣的馬賽克,足以熄滅任何玩家的投入感。   似乎TTF是唯一的解決之道。   2.基本知識   (1)TTF字體。   TTF是一種矢量字庫。我們經常可以聽到矢量這個詞,像是FLASH中的矢量圖形,在100*100分辨率下制作的flash,就算它放大爲全屏,顯示出的畫面也不會出現馬賽克。所謂矢量,其實說白了就是用點和線來描述圖形,這樣,在圖形需要放大的時候,只要把所有這個圖形的點和線放大相應的倍數就可以了。而且,在網站上有很多的TTF字庫可以下載,或者你可以去買一些專門的字庫光盤。然後在你發行你精心制作的遊戲時,可以順便捎上這些後綴爲。ttf的文件就行了。包括Quake這樣的驚世之作,也都是用的TTF字庫。   (2)FreeType2庫   在http://www.freetype.org,有一個FreeType的免費庫,而且是OpenSource的。它目前有2個版本:1.0和2.0.其區別在于,1.0只能讀取TTF格式的,而2.0支持更多的文件格式,在使用它之前請詳細閱讀所要遵循的Licence,以下是摘自FreeType2.0對字庫的支持列表:   TrueType fonts (and collections)   Type 1 fonts   CID-keyed Type 1 fonts   CFF fonts   OpenType fonts (both TrueType and CFF variants)   SFNT-based bitmap fonts   X11 PCF fonts   Windows FNT fonts   (3)「主體思想」   請參照炎龍工作室的《遊戲中漢字顯示的實現與技巧》這篇文章,可惜使用的是Windows API,作者千裏馬肝,上網上搜一下吧。   附帶說一句,上面兩條都是直接從這文章中剪切下來的,_<;   不要罵我啊,不知道算不算侵權呢。   3.動手術――比你想象的要麻煩   (1)   首先要告訴字體類,我們下一次渲染需要哪些字。以便字體類可以在需要的時候釋放不使用的字體。   在Font類中~   a.增加數據 bool mUsing[OGRE_NUM_GLYPHS]; 用來標記文字是否使用。   b.增加函數   inline void setUsing(std::vector<;unsigned long& caption)   {   memset(this-mUsing,0,sizeof(this-mUsing));   std::vector<;unsigned long::iterator it;   for(it=caption.begin();it!=caption.end();++it)   {   if(OGRE_GLYPH_INDEX(*it)<;OGRE_NUM_GLYPHS)   this-mUsing[OGRE_GLYPH_INDEX(*it)]=1;//標記文字爲使用   }   }   並在 void TextAreaGuiElement::updateGeometry() 中調用這個函數。   (2)   然後是修改void Font::createTextureFromFont(void);   Font類是通過void Font::createTextureFromFont(void)來把通過FreeType2分析好的英文字畫在一個2^n*2^n的貼圖上,然後再保存英文字的位置。   我們需要修改的是:   a.從函數中分離並保存畫字的FreeType2和輔助變量。我們通過一個類來保存和處理這些變量。   class TTFMsg   {   class Max//類中類,用來保存幾個"最大",   {   int nothing;//這個是用來占位的,沒意義,反正沒他就運行出錯,可能和數據對齊有關吧。   public:   int height;//文字最大高度   int width;//最大寬度   int bear;//最大空隙?   };   public:   FT_Library ftLibrary;//FreeType2用   FT_Face face;//FreeType2接口?   uint char_spacer;//文字空隙   SDDataChunk ttfchunk;//數據塊,用來保存ttf信息   FT_F26Dot6 ftSize;//FreeType2字體大小   std::pair<;uint,uint point;//在位圖上畫字的點   SDDataChunk imgchunk;//數據塊,用來保存位圖信息   bool dirty;//標記,看是否需要更新貼圖   Max max;//幾個最大   inline bool init(Font*   font) //這個是初始化函數在void Font::createTextureFromFont(void);中調用。   {   //以下都是初始化,大部分都是從void Font::createTextureFromFont(void);移植過來的   dirty=false;   if( FT_Init_FreeType( &ftLibrary ) )   Except( Exception::ERR_INTERNAL_ERROR, "Could not init FreeType library!",   "Font::Font");   char_spacer= 5;   FontManager::getSingleton()。_findResourceData(font-mSource,ttfchunk);   if( FT_New_Memory_Face( ftLibrary, ttfchunk.getPtr(), (FT_Long)ttfchunk.getSize() , 0, &face ) )   Except( Exception::ERR_INTERNAL_ERROR,   "Could not open font face!", "Font::createTextureFromFont" );   ftSize = (FT_F26Dot6)(font-mTtfSize * (1 <;<; 6));   if( FT_Set_Char_Size( face, ftSize, 0, font-mTtfResolution, 0 ) )   Except( Exception::ERR_INTERNAL_ERROR,   "Could not set char size!", "Font::createTextureFromFont" );   return true;   }   inline bool done()   {   //在Font的解構函數中調用的,本來應該調用下面兩個函數,但是不知道爲什麽一調用就出錯,不用倒沒事。   //FT_Done_Face(face);   //FT_Done_FreeType(ftLibrary);   return true;   }   inline bool getRect(Image::Rect & rect)//這個函數以後用的,是用來找到可以畫字的貼圖的空的位置   {   if(511<;point.second+max.height+char_spacer)   {   rect.right=rect.top=rect.left=rect.bottom=-1;   return false;   }   if(511<;point.first+max.width+char_spacer)   {   point.second+=max.width+char_spacer;   point.first=0;   if(511<;point.second+max.height+char_spacer)   {   rect.right=rect.top=rect.left=rect.bottom=-1;   return false;   }   }   rect.left=point.first;   rect.top=point.second;   rect.bottom=max.height;   rect.right=max.width;   point.first+=max.width+char_spacer;   return true;   }   };   上面的類定義在Font類中,在Font中增加 TTFMsg * mTTFMsg 數據,並在構造函數中 mTTFMsg=new TTFMsg;   正是修改void Font::createTextureFromFont(void);函數,主要幾點,首先是分離出很多變量和構造到TTFMsg類中了,然後是貼圖從2^n*2^n變成固定的512*512,爲什麽要這個數字呢,因爲256太小(廢話了)。能保證512*512大小的文本區不會出現不夠畫字的情況(所有英文+符號+沒有重複的漢字),這個安全區域基本上是夠用的。(什麽,你要畫滿屏幕的漢字?哪你自己看著改吧。)還有一個重要功能是,要找100個漢字,找出最高和最寬和最大空隙作參考。修改完成這個樣子了~   void Font::createTextureFromFont(void)   {   mTTFMsg-init(this);//初始化FreeType2   uint i, l, m, n;   int j, k;   FILE *fo_def = stdout;//啥意思?我看不明白   int max_height = 0, max_width = 0, max_bear = 0;   uint startGlyph = 33;   uint endGlyph = 167;   // 找英文的找出最高和最寬和最大空
󰈣󰈤
王朝萬家燈火計劃
期待原創作者加盟
 
 
 
>>返回首頁<<
 
 
 
 
 熱帖排行
 
 
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有