专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »游戏开发 » ogre中文:让OGRE支持中文(二) - 支持TTF字体了 »正文

ogre中文:让OGRE支持中文(二) - 支持TTF字体了

来源: 发布时间:星期四, 2009年2月12日 浏览:189次 评论:0


让OGRE支持中文( 2) 

        ----支持TTF字体了 

        

0.还是前言 

        如果你希望能看懂这篇文章请先确定你已经看到了让OGRE支持中文( border=0>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就算它放大为全屏显示出画面也不会出现马赛克所谓矢量其实说白了就是用点和线来描述图形这样在图形需要放大时候只要把所有这个图形点和线放大相应倍数就可以了而且在网站WebSite上有很多TTF字库可以下载或者你可以去买些专门字库光盘然后在你发行你精心制作游戏时可以顺便捎上这些后缀为.ttf文件就行了包括Quake这样惊世的作也都是用TTF字库 

  

(2)FreeType2库 

        在 border=0>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 Using(std::vector<unsigned long>& caption)

                {

                        mem(this->mUsing,0,(this->mUsing));

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

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

                        {

                                (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和辅助变量我们通过个类来保存和处理这些变量 

         TTFMsg

                {

                         Max//类中类,用来保存几个"最大",

                        {

                                 nothing;//这个是用来占位没意义反正没他就运行出错可能和数据对齐有关吧 

                        public:

                                 height;//文字最大高度 

                                 width;//最大宽度 

                                 bear;//最大空隙? 

  

                        };

                public:

                        FT_Library ftLibrary;//FreeType2用 

                        FT_Face face;//FreeType2接口? 

                        u char_spacer;//文字空隙 

                        SDDataChunk ttfchunk;//数据块用来保存ttf信息 

                        FT_F26Dot6 ftSize;//FreeType2字体大小 

                        std::pair<u,u> po;//在位图上画字点 

                        

                        SDDataChunk imgchunk;//数据块用来保存位图信息 

                        bool dirty;//标记,看是否需要更新贴图 

                        

                        Max max;//几个最大 

                        inline bool init(Font*  font) //这个是在void Font::createTextureFromFont(void);中.

                        {       

                                //以下都是化,大部分都是从void Font::createTextureFromFont(void);移植过来 



                                dirty=false;

                                ( 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);

                                ( 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));

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

                                        Except( Exception::ERR_INTERNAL_ERROR, 

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

  

                                 true;

                        }

                        inline bool done

                        {

                                //在Font解构本来应该下面两个但是不知道为什么就出错不用倒没事 

                                //FT_Done_Face(face);

                                //FT_Done_FreeType(ftLibrary);

                                 true;

                        }

                        inline bool getRect(Image::Rect & rect)//这个以后用是用来找到可以画字贴图位置 



                        {

                                (511<po.second+max.height+char_spacer)

                                {

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

                                         false;

                                }

                                (511<po.first+max.width+char_spacer)

                                {

                                        

                                        po.secondmax.width+char_spacer;

                                        po.first=0;

                                        (511<po.second+max.height+char_spacer)

                                        {

                                        

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

                                                 false;

                                        }

                                }

                                rect.left=po.first;

                                rect.top=po.second;

                                rect.bottom=max.height;

                                rect.right=max.width;

                                po.firstmax.width+char_spacer;

                                 true;

                        }

                };



        上面类定义在Font类中在Font中增加 TTFMsg * mTTFMsg 数据并在构造中 mTTFMsg= TTFMsg;

        

        正是修改void Font::createTextureFromFont(void);主要几点首先是分离出很多变量和构造到TTFMsg类中了然后是贴图从2^n*2^n变成固定512*512为什么要这个数字呢256太小(废话了)能保证512*512大小文本区不会出现不够画字情况(所有英文+符号+没有重复汉字)这个安全区域基本上是够用(什么你要画满屏幕汉字?哪你自己看着改吧)还有个重要功能是要找100个汉字找出最高和最宽和最大空隙作参考修改完成这个样子了~

    void Font::createTextureFromFont(void)

    {

        mTTFMsg->init(this);//化FreeType2

        u i, l, m, n;

         j, k;

  

       

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

  

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

  

        u startGlyph = 33;

        u endGlyph = 167;

  

        // 找英文找出最高和最宽和最大空隙 

        // Calculate maximum width, height and bearing 

        for( i = startGlyph, l = 0, m = 0, n = 0; i < endGlyph; i )

        {

            FT_Load_Char( mTTFMsg->face, i, FT_LOAD_RENDER );

            //以后 <<6和>>6都是FreeType2中数据和我们使用数据转换 

            ( ( 2 * ( mTTFMsg->face->glyph->bitmap.rows << 6 ) - mTTFMsg->face->glyph->metrics.horiBearingY ) > max_height )

                max_height = ( 2 * ( mTTFMsg->face->glyph->bitmap.rows << 6 ) - mTTFMsg->face->glyph->metrics.horiBearingY );

            ( mTTFMsg->face->glyph->metrics.horiBearingY > max_bear )

                max_bear = mTTFMsg->face->glyph->metrics.horiBearingY;

  

            ( (mTTFMsg->face->glyph->advance.x >> 6 ) + ( mTTFMsg->face->glyph->metrics.horiBearingX >> 6 ) > max_width)

                max_width = (mTTFMsg->face->glyph->advance.x >> 6 ) + ( mTTFMsg->face->glyph->metrics.horiBearingX >> 6 );

        }

  

  

        //下面for是找100个汉字找出最高和最宽和最大空隙作参考姑且认为最汉字就在这百个里了 

        for( i = 20643, l = 0, m = 0, n = 0; i < 20743; i )

        {

            FT_Load_Char( mTTFMsg->face, i, FT_LOAD_RENDER );

  

            ( ( 2 * ( mTTFMsg->face->glyph->bitmap.rows << 6 ) - mTTFMsg->face->glyph->metrics.horiBearingY ) > max_height )

                max_height = ( 2 * ( mTTFMsg->face->glyph->bitmap.rows << 6 ) - mTTFMsg->face->glyph->metrics.horiBearingY );

            ( mTTFMsg->face->glyph->metrics.horiBearingY > max_bear )

                max_bear = mTTFMsg->face->glyph->metrics.horiBearingY;

  



            ( (mTTFMsg->face->glyph->advance.x >> 6 ) + ( mTTFMsg->face->glyph->metrics.horiBearingX >> 6 ) > max_width)

                max_width = (mTTFMsg->face->glyph->advance.x >> 6 ) + ( mTTFMsg->face->glyph->metrics.horiBearingX >> 6 );

  

                        

            ( (mTTFMsg->face->glyph->advance.x ) + ( mTTFMsg->face->glyph->metrics.horiBearingX ) > mTTFMsg->max.width)

                mTTFMsg->max.width = (mTTFMsg->face->glyph->advance.x  ) + ( mTTFMsg->face->glyph->metrics.horiBearingX );

        }

  

                //下面几行行不需要了 我们要512*512

                size_t tex_side=512;//就是这个了 512

        //定义数据宽度 是32位位图  4个char为个像素为什么要位这么大呢难道不能用8位我尝试着改来着不过改了的后就不透明了望高手看看能不能改 

                size_t data_width = tex_side * 4;

  

                LogManager::getSingleton.logMessage("Font " + mName + "using texture size " +

                        StringConverter::toString(tex_side) + "x" + StringConverter::toString(tex_side)); 

  

        uchar* imageData =  uchar[tex_side * tex_side * 4];//设置个空间用来保存位图 

                // Re content

                mem(imageData, 0, tex_side * tex_side * 4);//清零 

  

        for( i = startGlyph, l = 0, m = 0, n = 0; i < endGlyph; i )//遍历每个字 

        {

                        

            FT_Error ftResult;

  

            // Load & render glyph 

            ftResult = FT_Load_Char( mTTFMsg->face, i, FT_LOAD_RENDER );//读取字体 

             (ftResult)

            {

                // problem loading this glyph, continue 

                LogManager::getSingleton.logMessage("Info: cannot load character " +

                    StringConverter::toString(i) + " in font " + mName);

                continue;//如果跳过 

            }

  

                        // 应该是字宽 

                        FT_Int advance = (mTTFMsg->face->glyph->advance.x >> 6 ) + ( mTTFMsg->face->glyph->metrics.horiBearingX >> 6 );

                        

                        // 得到FreeType2位图 



            unsigned char* buffer = mTTFMsg->face->glyph->bitmap.buffer;

  

             (!buffer)

            {

                // Yuck, FT didn't detect this but generated a null poer! 

                LogManager::getSingleton.logMessage("Info: Freetype ed null for character " +

                    StringConverter::toString(i) + " in font " + mName);

                continue;//如果得不到跳过 

            }

  

                        // 得到y空隙 (最大空-空-本字空)就是说空出这些字底部就平了 

             y_bearnig = ( max_bear >> 6 ) - ( mTTFMsg->face->glyph->metrics.horiBearingY >> 6 );

                        

            for( j = 0; j < mTTFMsg->face->glyph->bitmap.rows; j )

            {

                                 row = j + m + y_bearnig;//相对行+本字在位图总行+空隙 

                 col = l;//列 

                uchar* pDest = &imageData[(row * data_width) + l * 4];//找起点   

                for( k = 0; k < mTTFMsg->face->glyph->bitmap.width; k )//画图 

                {

                     (mAntialiasColour)//看不大懂难道是灰色字体? 

                    {

                        // Use the same greyscale pixel for all components RGBA 

                        *pDest+ *buffer;

                            *pDest+ *buffer;

                            *pDest+ *buffer;

                    }

                     

                    {

                        // Clamp colour to full white or off 

                         (*buffer > 0)

                        {

                            *pDest+ 0xFF;

                            *pDest+ 0xFF;

                            *pDest+ 0xFF;



                        }

                         

                        {

                            *pDest+ 0;

                            *pDest+ 0;

                            *pDest+ 0;

                        }

                    }

                    // Always use the greyscale value for alpha 

                                        

                    *pDest+ *buffer;//alpha! 我是点不了解了为什么是这个? 

                }

            }

  

            this->GlyphTexCoords( i, 

                (Real)l / (Real)tex_side,  // u1

                (Real)m / (Real)tex_side,  // v1

                (Real)( l + ( mTTFMsg->face->glyph->advance.x >> 6 ) ) / (Real)tex_side, // u2

                (Real)( m + ( max_height >> 6 ) ) / (Real)tex_side // v2

                );//设置坐标 

  

            // Advance a column 

            l  (advance + mTTFMsg->char_spacer);

                        //l 本字宽+字空 

  

            // If at end of row 

                        //如果到头容不下个字 

            ( tex_side - 1 < l + ( advance ) )

            {

                m  ( max_height >> 6 ) + mTTFMsg->char_spacer;

                l = n = 0;

            }

  

        }

                // 把信息存到我们mTTFMsg中 >_<

                (l)

                        mTTFMsg->po.second = m + ( max_height >> 6 ) + mTTFMsg->char_spacer;

                 

                        mTTFMsg->po.second = m;

  

                mTTFMsg->po.first = 0;//另起行 

  

                //下面是保存几个最大 



                mTTFMsg->max.height = max_height >> 6;

                mTTFMsg->max.bear=max_bear >> 6;

                mTTFMsg->max.width=max_width;

  

                mTTFMsg->imgchunk.allocate( tex_side * tex_side * 4,imageData);

                

                //不知道为什么要设置img这个中间变量似乎可以直接从chunk创建贴图 

        //--Image img; 

                //img.loadRawData( imgchunk, tex_side, tex_side, PF_A8R8G8B8 );

  

                //贴图名 

        String texName = mName + "Texture";

                // Load texture with no mipmaps 

        // 把从img创建位图改成直接从chunk创建贴图 

                //TextureManager::getSingleton.loadImage( texName , img, TEX_TYPE_2D, 0  );

                TextureManager::getSingleton.loadRawData ( texName , mTTFMsg->imgchunk,tex_side, tex_side,PF_A8R8G8B8, TEX_TYPE_2D, 0  );

        TextureUnitState* t = mpMaterial->getTechnique(0)->getPass(0)->createTextureUnitState( texName );

                // Allow min/mag filter, but no mip 

                t->TextureFiltering(FO_LINEAR, FO_LINEAR, FO_NONE);

        // SDDatachunk will delete imageData 

        

    }

        你应该对照下以前改了很多 

  

  

(3) 

        然后最重要是动态申请和画字 

        首先最重要是我们要个思路方法得到汉字unicode码我们用到了unicodeMap提供了区位码到unicode码转换工作下提供这个表给你好不容易从网上找到可惜这个表没有中文标点对应如果希望支持中文标点就要努力找到unicode码并加进这个表了这个表是这样 char c[3]="";unicodeMap[94*(c[0]-0xa0-1)+c[1]-0xa0-1];就得到邸unicode码不知道为什么没有5165~5169unicode码是轮空么? 

        

        申请渲染(字) 

        {

                (有这个字)

                        返回位置 

                

                {

                        (有空间)

                                画字 

                        

                            找不用字//找不到就出错没地方画了 

                                删除 在这个位置上画字 

                返回位置 

                }

        }

        上面很直观把大体就这个意思了申请渲染是inline void getGlyphTexCoords(unsigned long id, Real& u1, Real& v1, Real& u2, Real& v2 )  const去掉const改得到 



        a.申请渲染 

        inline void getGlyphTexCoords(unsigned long id, Real& u1, Real& v1, Real& u2, Real& v2 )         

        {

                unsigned long idx = OGRE_GLYPH_INDEX(id);

                (this->mTypeFT_TRUETYPE)//ttf?

                (id>=161&&idx<OGRE_NUM_GLYPHS)//汉字?

                        (!mTexCoords_v2[ idx ])//没有?

                                Char(id);//画字去 

             

            u1 = mTexCoords_u1[ idx ];

            v1 = mTexCoords_v1[ idx ];

            u2 = mTexCoords_u2[ idx ];

            v2 = mTexCoords_v2[ idx ];

        }

        b.画字等 

        bool Font::Char(unsigned long dwChar)

        {

                 j,k;

                uchar* pDest;//操作数据指针 

                mProportion=0.8;//不知为什么,总觉得中文都太扁平了,所以设置这个参数,在让OGRE支持中文中创造参数 

                Image::Rect  rect;//画字位置 

                (!mTTFMsg->getRect(rect)/*回头看看这个吧*/)//如果得不到空位置 

                {

                        for( i=161;i<OGRE_NUM_GLYPHS;i)//寻找不用字 

                        {

                                (!mUsing&&mTexCoords_v2)

                                {

                                        // 得到坐标 

                                        rect.left=mTexCoords_u1*512;

                                        rect.top=mTexCoords_v1*512;

                                        rect.bottom=mTTFMsg->max.height;

                                        rect.right=mTTFMsg->max.width;

  

                                        

                                        

                                



                                        //擦除(这段代码没有试验过等做完输入类在检查吧) 

                                        for( j = 0; j < mTTFMsg->max.height; j )

                                        {

                                                pDest=mTTFMsg->imgchunk.getPtr;

                                                pDest((j + rect.top)* (512*4)) + rect.left * 4;

                                                mem(pDest,0,mTTFMsg->max.width*4);

                                        }

                                        mTexCoords_u1=mTexCoords_v1=mTexCoords_u2=mTexCoords_v2=0;

  

                                        ;

                                }

                        }

                        

                        (rect.top-1)//啊啊啊啊啊啊啊啊啊,贴图竟然都用完了,没办法了,出错了 

                        {

                                LogManager::getSingleton.logMessage("你太贪婪了,用汉字太多了,这是对你惩罚."); 

                                 false;

                        }

        

                }

                        

                

                  //以下画字都是招葫芦画瓢

                  FILE *fo_def = stdout;

                 FT_Error ftResult;

  

            // Load & render glyph 

                  

            ftResult = FT_Load_Char( mTTFMsg->face, unicodeMap[dwChar-161], FT_LOAD_RENDER );//读取字体 

             (ftResult)

            {

                // problem loading this glyph, continue 



                LogManager::getSingleton.logMessage("Info: cannot load character " +

                    StringConverter::toString(unicodeMap[dwChar-161]) + " in font " + mName);

                 false;//如果跳过 

            }

  

          unsigned char* buffer = mTTFMsg->face->glyph->bitmap.buffer;

                  // 位图指针 

             (!buffer)

            {

                // Yuck, FT didn't detect this but generated a null poer! 

                LogManager::getSingleton.logMessage("Info: 1111Freetype ed null for character " +

                    StringConverter::toString(unicodeMap[dwChar-161]) + " in font " + mName);

                 false;//如果得不到跳过 

            }

  

                          y_bearnig = ( mTTFMsg->max.bear) - ( mTTFMsg->face->glyph->metrics.horiBearingY >> 6 );

       

                        

            for( j = 0; j < mTTFMsg->face->glyph->bitmap.rows; j )

            {

                 row = j + rect.top+y_bearnig;

                 col = rect.left;//列 

                                pDest=mTTFMsg->imgchunk.getPtr;

  

                        

                                pDest(row * (512*4)) + col * 4;

               for( k = 0; k < mTTFMsg->face->glyph->bitmap.width; k )//每行点 

                {

                     (mAntialiasColour)

                    {

                        // Use the same greyscale pixel for all components RGBA 

                                                        *pDest+ *buffer;

                            *pDest+ *buffer;

                            *pDest+ *buffer;

                    }

                     

                    {



                        // Clamp colour to full white or off 

                         (*buffer > 0)

                        {

                            *pDest+ 0xFF;

                            *pDest+ 0xFF;

                            *pDest+ 0xFF;

                        }

                         

                        {

                            *pDest+ 0;

                            *pDest+ 0;

                            *pDest+ 0;

                        }

                    }

                    // Always use the greyscale value for alpha 

                                        

                                //      LogManager::getSingleton.logMessage(StringConverter::toString(*buffer)+"is cool");

                    *pDest+ *buffer;

                }

                                

                        }

                        // 设置位置 

                        this->GlyphTexCoords( dwChar, 

                                 (Real)rect.left / 512.0f,  // u1

                                (Real)rect.top / 512.0f,  // v1

                                (Real)( rect.left + (mTTFMsg->face->glyph->advance.x >> 6) ) / 512.0f, // u2

                                (Real)( rect.top + rect.bottom ) / 512.0f // v2

                                        );

                mTTFMsg->dirty=true;//贴图需要更新 

                 true;

        }



  

(4) 

        松口气吧繁重工作都做完了就是更新贴图了 

        Font类中~

        inline void write

                {

                        (mTTFMsg->dirty)

                        {       

                                // 重新载入贴图顺便说类似功能还有 

                                //virtual void  blitToTexture (const Image &src, unsigned uStartX, unsigned uStartY)=0 

                                //和virtual void  blitImage (const Image &src, const Image::Rect imgRect, const Image::Rect texRect)

                                //不过blitToTexture在ogre-win32-v0-14-0的前没有实现没有尝试使用 blitImage就算到现在也没有实现 

  

                                TextureManager::getSingleton.unload ( mName + "Texture");

                                TextureManager::getSingleton.loadRawData ( mName + "Texture" , mTTFMsg->imgchunk,512, 512,PF_A8R8G8B8, TEX_TYPE_2D, 0  );

                                mTTFMsg->dirty=false;

                        }

                } 

  

         在 void TextAreaGuiElement::updateGeometry 中这个 

  

  

4.结果 

        好长阿写了好长虽然多是代码但写注释也很累啊重温了这些天工作结果就像上样“在OpenGL和DX9.0中成功支持了中文但是DX7.0中竟然出现了运行具体问题还不清楚还望各位高手指教不过本来OGRE就不如何支持DX7不管了吧OpenGL中渲染比DX9.0清晰是我显卡原因么?以上winXp+vs2003.net+艾尔沙980se通过~ 

        顺便说两句上次发表了拙作没想到好多朋友和高人们找到了我甚至联系到了个南方公司说要做个引擎支持中文方面但是卖完400多火车票的后竟然住房问题作废了5555退票少了80元啊还是自己差阿如果再多学点可能就能要我了把~努力吧争取写让OGRE支持中文 3部曲 

        下篇应该是可以输入中文了 

        也可能写不出来~~~~~~~~~~~~~~~~~~~~    

  

  

  

  

    相关文件 

    chinese.fontdef//字体信息文件 放在资源文件加中 

    font.png//字体图片 放在资源文件加中 

  

    OgreFont.cpp

    OgreFont.h

    OgreFontManager.cpp

    OgreTextAreaGuiElement.cpp

    //上面文件覆盖同名文件 就可以 先备份 

   

   solo5.ttf//这个是个中文ttf 我直接替换了原来ttf

   unicodemap.h//这个重要是换算unicode码(文中提到) 

  

   这个是在ogre-win32-v0-14-0基础上改(上次是ogre-win32-v0-13-1) 

   请到 border=0>http://sourceforge.net/projects/ogre/下载 

  

    配套档案下载: border=0>http://www.gameres.com/Articles/Program/Visual/3D/ogre/ttf.files/file.rar 




 

    免费打工仔QQ:1850070

From: GameRes 

border=0>http://www.gameres.com
0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: