游戏开发论坛

 找回密码
 立即注册
搜索
查看: 2951|回复: 3

让OGRE支持中文(二)----支持TTF字体了[原创][首发]

[复制链接]

193

主题

870

帖子

903

积分

高级会员

Rank: 4

积分
903
QQ
发表于 2004-7-31 05:05:00 | 显示全部楼层 |阅读模式
让OGRE支持中文(二)
        ----支持TTF字体了[原创](免费打工仔QQ:1850070)

       
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;

        //找英文的找出最高和最宽和最大空隙
        // 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中的数据和我们使用的数据的转换
            if( ( 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 );
            if( mTTFMsg->face->glyph->metrics.horiBearingY > max_bear )
                max_bear = mTTFMsg->face->glyph->metrics.horiBearingY;

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

            if( ( 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 );
            if( mTTFMsg->face->glyph->metrics.horiBearingY > max_bear )
                max_bear = mTTFMsg->face->glyph->metrics.horiBearingY;

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

                       
            if( (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位的位图 四个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 = new uchar[tex_side * tex_side * 4];//设置一个空间用来保存位图
                // Reset content
                memset(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 );//读取字体
            if (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;

            if (!buffer)
            {
                // Yuck, FT didn't detect this but generated a null pointer!
                LogManager::getSingleton().logMessage("Info: Freetype returned null for character " +
                    StringConverter::toString(i) + " in font " + mName);
                continue;//如果得不到 跳过
            }

                        //得到y的空隙 (最大空-空-本字空)就是说空出这些字底部就平了
            int y_bearnig = ( max_bear >> 6 ) - ( mTTFMsg->face->glyph->metrics.horiBearingY >> 6 );
                       
            for( j = 0; j < mTTFMsg->face->glyph->bitmap.rows; j++ )
            {
                                int row = j + m + y_bearnig;//相对行+本字在位图的总行+空隙
                int col = l;//列
                uchar* pDest = &imageData[(row * data_width) + l * 4];//找起点   
                for( k = 0; k < mTTFMsg->face->glyph->bitmap.width; k++ )//画图
                {
                    if (mAntialiasColour)//看不大懂,难道是灰色字体?
                    {
                        // Use the same greyscale pixel for all components RGBA
                        *pDest++= *buffer;
                            *pDest++= *buffer;
                            *pDest++= *buffer;
                    }
                    else
                    {
                        // Clamp colour to full white or off
                        if (*buffer > 0)
                        {
                            *pDest++= 0xFF;
                            *pDest++= 0xFF;
                            *pDest++= 0xFF;
                        }
                        else
                        {
                            *pDest++= 0;
                            *pDest++= 0;
                            *pDest++= 0;
                        }
                    }
                    // Always use the greyscale value for alpha
                                       
                    *pDest++= *buffer++;//alpha! 我是一点不了解了 为什么是这个?
                }
            }

            this->setGlyphTexCoords( 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
                        //如果到头容不下一个字
            if( tex_side - 1 < l + ( advance ) )
            {
                m += ( max_height >> 6 ) + mTTFMsg->char_spacer;
                l = n = 0;
            }

        }
                //把信息存到我们的mTTFMsg中 >_<
                if(l)
                        mTTFMsg->point.second = m + ( max_height >> 6 ) + mTTFMsg->char_spacer;
                else
                        mTTFMsg->point.second = m;

                mTTFMsg->point.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->setTextureFiltering(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~5169的unicode码,是轮空么?
       
        申请渲染(字)
        {
                if(有这个字)
                        返回位置
                else
                {
                        if(有空间)
                                画字
                        else
                            找不用的字//找不到就出错,没地方画了
                                删除 在这个位置上画字
                返回位置
                }
        }
        上面很直观把,大体就这个意思了,申请渲染的函数是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);
                if(this->mType==FT_TRUETYPE)//ttf?
                if(id>=161&&idx<OGRE_NUM_GLYPHS)//汉字?
                        if(!mTexCoords_v2[ idx ])//没有?
                                setChar(id);//画字去
            
            u1 = mTexCoords_u1[ idx ];
            v1 = mTexCoords_v1[ idx ];
            u2 = mTexCoords_u2[ idx ];
            v2 = mTexCoords_v2[ idx ];
        }
        b.画字等
        bool Font::setChar(unsigned long dwChar)
        {
                int j,k;
                uchar* pDest;//操作数据的指针
                mProportion=0.8;//不知为什么,总觉得中文都太扁平了,所以设置这个参数,在《让OGRE支持中文》中创造的参数
                Image::Rect  rect;//画字的位置
                if(!mTTFMsg->getRect(rect)/*回头看看这个函数吧*/)//如果得不到空位置
                {
                        for(int i=161;i<OGRE_NUM_GLYPHS;++i)//寻找不用的字
                        {
                                if(!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;
                                                memset(pDest,0,mTTFMsg->max.width*4);
                                        }
                                        mTexCoords_u1=mTexCoords_v1=mTexCoords_u2=mTexCoords_v2=0;

                                        break;
                                }
                        }
                       
                        if(rect.top==-1)//啊啊啊啊啊啊啊啊啊,贴图竟然都用玩了,没办法了,出错了
                        {
                                LogManager::getSingleton().logMessage("你太贪婪了,用的汉字太多了,这是对你的惩罚.");
                                return false;
                        }
       
                }
                       
               
                 //以下画字的都是招葫芦画瓢的,
                 FILE *fo_def = stdout;
                 FT_Error ftResult;

            // Load & render glyph
                 
            ftResult = FT_Load_Char( mTTFMsg->face, unicodeMap[dwChar-161], FT_LOAD_RENDER );//读取字体
            if (ftResult)
            {
                // problem loading this glyph, continue
                LogManager::getSingleton().logMessage("Info: cannot load character " +
                    StringConverter::toString(unicodeMap[dwChar-161]) + " in font " + mName);
                return false;//如果错误 跳过
            }

          unsigned char* buffer = mTTFMsg->face->glyph->bitmap.buffer;
                  //位图指针
            if (!buffer)
            {
                // Yuck, FT didn't detect this but generated a null pointer!
                LogManager::getSingleton().logMessage("Info: 1111Freetype returned null for character " +
                    StringConverter::toString(unicodeMap[dwChar-161]) + " in font " + mName);
                return false;//如果得不到 跳过
            }

                         int y_bearnig = ( mTTFMsg->max.bear) - ( mTTFMsg->face->glyph->metrics.horiBearingY >> 6 );
      
                       
            for( j = 0; j < mTTFMsg->face->glyph->bitmap.rows; j++ )
            {
                int row = j + rect.top+y_bearnig;
                int col = rect.left;//列
                                pDest=mTTFMsg->imgchunk.getPtr();

                       
                                pDest+=(row * (512*4)) + col * 4;
               for( k = 0; k < mTTFMsg->face->glyph->bitmap.width; k++ )//每行的点
                {
                    if (mAntialiasColour)
                    {
                        // Use the same greyscale pixel for all components RGBA
                                                        *pDest++= *buffer;
                            *pDest++= *buffer;
                            *pDest++= *buffer;
                    }
                    else
                    {
                        // Clamp colour to full white or off
                        if (*buffer > 0)
                        {
                            *pDest++= 0xFF;
                            *pDest++= 0xFF;
                            *pDest++= 0xFF;
                        }
                        else
                        {
                            *pDest++= 0;
                            *pDest++= 0;
                            *pDest++= 0;
                        }
                    }
                    // Always use the greyscale value for alpha
                                       
                                //        LogManager::getSingleton().logMessage(StringConverter::toString(*buffer)+"is cool");
                    *pDest++= *buffer++;
                }
                               
                        }
                        //设置位置
                        this->setGlyphTexCoords( 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;//贴图需要更新
                return true;
        }

(4)
        松口气吧,繁重的工作都做完了,就是更新贴图了。
        Font类中~
        inline void write()
                {
                        if(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支持中文》三部曲。
        下一篇应该是《可以输入中文了》。
        也可能写不出来的~~~~~~~~~~~~~~~~~~~~
            
                相关文件,一有空间就上传~
       
         

1万

主题

1万

帖子

2万

积分

管理员

中级会员

Rank: 9Rank: 9Rank: 9

积分
20527
发表于 2004-7-31 10:47:00 | 显示全部楼层

Re:让OGRE支持中文(二)----支持TTF字体了[原创][首发]

楼主的配套档案下载:http://www.gameres.com/Articles/Program/Visual/3D/ogre/ttf.files/file.rar

33

主题

445

帖子

446

积分

中级会员

Rank: 3Rank: 3

积分
446
发表于 2004-8-1 01:47:00 | 显示全部楼层

Re:让OGRE支持中文(二)----支持TTF字体了[原创][首发]

怎么这么长

121

主题

2029

帖子

2034

积分

金牌会员

Rank: 6Rank: 6

积分
2034
QQ
发表于 2004-8-1 09:39:00 | 显示全部楼层

Re:让OGRE支持中文(二)----支持TTF字体了[原创][首发]

我现在是想D3D怎么直接输出中文。。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-7-4 11:07

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表