討論區快速選單
知識庫快速選單
網路投保旅行平安險 掌握Salesforce雲端管理秘訣 傑米的攝影旅遊筆記
[ 回上頁 ] [ 討論區發言規則 ]
glut 教學 - glPrintf() 秀中文字
更改我的閱讀文章字型大小
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 07:36:32
編寫 3D 程式, 圖像固然是主角, 可是, 不管圖像有多華麗也好, 總得有點文字描述吧. 有了文字描述, 於 開發 除錯 介面表達 也會有很大的幫助的.

glut 雖然內建了秀文字的功能, 但是, 它只限於 英文 而已. 身為 中國人 介面甚能夠只有英文呢?! 但是, 因為中文字元 比 英文字元 多很多, 在這範例裡, 編寫程式去秀文字時, 多花了點心思去作優化.

我要在 console 打印文字時, 跟很多人一樣, 會使用 printf() 的. 為了迎合我的個人喜好, 我把 秀文字函數 的使用方法刻意地設計成跟 printf()的 一般. 這亦是題目裡的函數命名的原因.

嗯嗯嗯, 好吧, 我們來秀秀中文字吧.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 07:40:28
/////////////////////////
// glutTest14.cpp
//
// Created by Gary Ho, ma_hty@hotmail.com, 2009
//

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <windows.h>

#include <GL/glut.h>

#include "glPrintf.h"

float theta = 0;

void display();

void main( int argc, char **argv )
{
  glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
  glutInitWindowSize( 512, 512 );

  glutCreateWindow( "glutTest14" );
  glutDisplayFunc( display );

  typedef BOOL (APIENTRY *PFNWGLSWAPINTERVALFARPROC)( int );
  PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = 0;
  wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)wglGetProcAddress("wglSwapIntervalEXT");
  wglSwapIntervalEXT(1);

  glutMainLoop();
}

void display()
{
  GLint viewport[4];
  time_t rawtime;
  struct tm * timeinfo;
  char str[256];

  time ( &rawtime );
  timeinfo = localtime ( &rawtime );
  strftime( str, 256, "%I:%M:%S %p", timeinfo );
  glGetIntegerv( GL_VIEWPORT, viewport );

  glEnable( GL_DEPTH_TEST );
  glEnable( GL_LIGHTING );
  glEnable( GL_LIGHT0 );

  glClearColor( .1, .2, .3, 1 );
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective( 45, double(viewport[2])/viewport[3], 0.1, 10 );
  
  glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt( 0,3,2.5, 0,0,0, 0,1,0 );

    glRotatef( theta, 0,1,0 );
    glutSolidTeapot(1);

  glPrintf( ">>glut" );
  glColor3f(1,1,1);
  glPrintf( "現在的時間是: %s\n", str );
  glPrintf( "\n" );
  glColor3f(1,1,0);
  glPrintf( "你好嗎?\n" );
  glPrintf( "我好好, 只是有點感冒.\n" );
  glColor3f(1,0,0);
  glPrintf( "你好嗎?\n" );
  glPrintf( "我好好, 只是有點感冒.\n" );
  glPrintf( "\n" );
  glColor3f(.4,1,1);
  glPrintf( "theta: %.2f\n", theta );

  theta = fmodf(theta+1,360);
  glutPostRedisplay();
  glutSwapBuffers();
}










作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 07:42:06
/////////////////////////
// glPrintf.h
//
// Created by Gary Ho, ma_hty@hotmail.com, 2009
//
#ifndef GL_PRINTF_H
#define GL_PRINTF_H

int glPrintf( const char *format, ... );

#endif
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 07:43:09
/////////////////////////
// glPrintf.cpp
//
// Created by Gary Ho, ma_hty@hotmail.com, 2009
//
#include <stdio.h>
#include <windows.h>
#include <GL/glut.h>

int glPrintf( const char *format, ... )
{
  DWORD fdwCharSet = CHINESEBIG5_CHARSET;
  char szFace[] = "Courier New";
  int nHeight = 16;


  char buffer[65536];
  va_list args;
  va_start(args, format);
  vsprintf(buffer, format, args);
  va_end(args);

  static GLuint base = 0;
  static bool loaded[65536] = {0};
  static HFONT hFont;
  if( base==0 )
  {
    base = glGenLists(65536);
    hFont = CreateFont( nHeight, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, fdwCharSet,
     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
     FF_DONTCARE | DEFAULT_PITCH, szFace );
  }

  float p0[4], p1[4], c0[2][4] = {{0,0,0,1},{1,1,1,1}};
  int i, j, len, offset[2]={1,0};
  wchar_t *wstr;
  GLint viewport[4];
  HDC hdc = 0;

  glGetIntegerv( GL_VIEWPORT, viewport );
  glGetFloatv( GL_CURRENT_RASTER_POSITION, p0 );

  glPushAttrib( GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT );
    glDisable( GL_LIGHTING );
    glDisable( GL_DEPTH_TEST );

  glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D( 0,viewport[2],0,viewport[3]);

  glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

  if( strcmp( buffer, ">>glut" )==0 )
  {
    glRasterPos2f( 4, viewport[3]-nHeight );
  }else if( strcmp( buffer, ">>free" )==0 )
  {
    glDeleteLists(base,65536); base=0;
    memset( loaded, 0, 65536*sizeof(bool) );
    DeleteObject(hFont);
  }else
  {
    glRasterPos2f( p0[0], p0[1] );
    glGetFloatv( GL_CURRENT_RASTER_COLOR, c0[1] );
    len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buffer, -1, 0, 0);
    wstr = (wchar_t*)malloc( len*sizeof(wchar_t) );
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buffer, -1, wstr, len);
    for( j=0; j<2; j++ )
    {
     glColor4fv( c0[j] );
     glRasterPos2f( p0[0]+offset[j], p0[1]-offset[j] );
     for( i=0; i<len-1; i++ )
     {
     if( wstr[i]=='\n' )
     {
     glGetFloatv( GL_CURRENT_RASTER_POSITION, (float*)&p1 );
     glRasterPos2f( 4+offset[j], p1[1]-(nHeight+2) );
     }else
     {
     if( !loaded[wstr[i]] )
     {
     if( hdc==0 )
     {
     hdc = wglGetCurrentDC();
     SelectObject( hdc, hFont );
     }
     wglUseFontBitmapsW( hdc, wstr[i], 1, base+wstr[i] );
     loaded[wstr[i]] = true;
     }
     glCallList(base+wstr[i]);
     }
     }
    }
    free(wstr);
  }

  glMatrixMode(GL_PROJECTION);
    glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
  glPopAttrib();

  return 0;
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 08:08:02
圖片

http://lh3.ggpht.com/_PpdR7LFER4U/Sq4umEZAAvI/AAAAAAAAABs/S098LMAyFM0/s512/glutTest14.png
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 08:14:14
本範例包含了三個檔案, 分別是 glutTest14.cpp, glPrintf.h 和 glPrintf.cpp. glutTest14.cpp 是主程式, 而 glPrintf.h 和 glPrintf.cpp 就是 glPrintf() 這個函數的 標頭檔 和 原碼檔.

本範例程式在執行時, 會在畫面上秀出不同顏色的中文字, 背景還有一茶壺在自轉.

現在, 我會逐一講解原碼的意思.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 08:28:18
在 display() 之內, 有一段原碼使用了一相似 printf() 的函數, 即 glPrintf(). 注意, 它們不僅是名稱相似, 就連應用的方法和技巧也相通的. 它們最主要的不同, 就是在每個 frame 你使用 glPrintf() 之前, 必須先呼叫 glPrintf( ">>glut" ); 去設定秀文字的起始任置. 順帶一提, 文字的顏色, 可以利用 glColor3f() 去設定的.

void display()
{
  // ...

  glPrintf( ">>glut" ); // 設定秀文字的起始任置
  glColor3f(1,1,1); // 設定文字顏色
  glPrintf( "現在的時間是: %s\n", str ); // 顯示現在時間, 然後換行
  glPrintf( "\n" ); // 單純的換行
  glColor3f(1,1,0); // 設定文字顏色
  glPrintf( "你好嗎?\n" ); // 顯示文字, 然後換行
  
  // ...
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 08:44:57
在 main() 之內, 有四句看來有點奇怪的指命, 它們是用來設定 vsync (垂直同步) 的. 如果我們把 vsync 啟用, 這樣 glutSwapBuffers() 會等待下一次顯示器更新完成, 才會結束; 反之, 則不會等待.

本範例會啟用vsync, 用意是限制畫面更新率跟 顯示器 一樣, 從而避免令 CPU 經常在 busy wait.

在 NVidia 系列的顯示卡, vsync 的預設值 是 啟用, 而在 ATI 系列的顯示卡, vsync 的預設值 是 不啟用的.

為了統一的原因, 所以我們會wglSwapIntervalEXT(), 去啟用 vsync.

但是, wglSwapIntervalEXT 這個函數是並沒有被預先載入的, 因此, 我們需要呼叫 wglGetProcAddress() 去載入它.

void main()
{
  // ...

  typedef BOOL (APIENTRY *PFNWGLSWAPINTERVALFARPROC)( int ); // 定義函數類型
  PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = 0; // 定義函數
  wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)
    wglGetProcAddress("wglSwapIntervalEXT"); // 載入 wglSwapIntervalEXT
  wglSwapIntervalEXT(1); // 啟用 vsync

  // ...
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 09:13:51
好, 主角來了, 我們說說 glPrintf() 的內容.

秀中文字好 秀英文字也好, 我們也需要先由視窗系統取得字型圖像, 但是, 這個步驟是很慢的一個步驟, 不適合每個字元也重複取得字型圖像. 如果是秀英文字的話, 我們可以預先把所有的字元都載入, 從而減省重複取得的時間. 反正, 英文字元的數目不多 (少於128個), 就算是全都載入也好, 也 不會需要很長的時間 和 不需要很大的記憶體.

但是, 中文字元數目很大, 就以 大五碼 為例, 包括 中文字元 和 其他字符 在內, 我們有多於 25000 個字元, 要全都預載的話, 會很好些時間 和 花費很大的記憶體. 為了解決這個問題, 我們會在程式執行時, 按需要選擇性地載入字元, 用過的才載入. 或是說, 雖然中文字元是很多, 但是, 常用的中文字其實只有 2000-2500 個, 再加上它只是某程式的顯示而已, 需要的字元就更少了.

這樣, 就算是秀中文也好, 效能還是省回來了.
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 09:19:40
為了簡化使用方法, 本程式沒有提供介面給用家選取 字碼 字型 和 字體大小. 這三個資料都硬寫在原碼裡.

( ^^", 要修改它們的話, 就得多花點功夫了. )

int glPrintf( const char *format, ... )
{
  DWORD fdwCharSet = CHINESEBIG5_CHARSET; // 字碼: 大五碼
  char szFace[] = "Courier New"; // 字型: Courier New
  int nHeight = 16; // 字體大小 : 16 pixel

  // ...
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 09:28:40
glPrintf() 這個函數的使用方法, 是希望能跟 printf() 同一樣. 當然, 要完成這個希望, 把 printf 的功能重寫一次就成, 但是, 這樣太花功夫了. 其實, 我們可以直接, 利用 argument list 和 vsprintf(), 去騎劫 sprintf(), 來取得 printf() 一般的功能的.

int glPrintf( const char *format, ... )
{
  // ...

  char buffer[65536];
  va_list args; // 定義 argument list
  va_start(args, format); // 取得本函數的 argument list
  vsprintf(buffer, format, args); // 把 argument list 交給 vsprintf() 處理, 並把字串存到 buffer
  va_end(args);

  // ...
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 10:03:36
本函數有三個 static local variables (分別是 base, loaded 和 hFont).

base 是用來儲存 字型圖像 的 display lists 的, 程式開始時, base 只有 65536個 空的 display lists, 在需要時 某字型圖像 才會被載入對應的 display list.

loaded 是用來記錄 字元 的 字型圖像 是否已被載入, 如果 某字元 的 字型圖像 已被載入, 則不需再載入.

hFont 是 font handle, 是視窗系統的東西, 我們要透過它才能向 視窗系統 取得 某字元的字型圖像.

int glPrintf( const char *format, ... )
{
  // ...

  static GLuint base = 0; // 字型圖像 的 display lists, 初始成 0
  static bool loaded[65536] = {0}; // 定義 65536 個 bool, 全都初始成 0
  static HFONT hFont; // font handle
  if( base==0 ) // 如果 base 還沒有被創造, 才執行以下的
  {
    base = glGenLists(65536); // 創造 65536個 display list
    hFont = CreateFont( nHeight, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, fdwCharSet,
     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
     FF_DONTCARE | DEFAULT_PITCH, szFace ); // 向 視窗系統 要求取得 font handle
  }

  // ...
}

註: static local variable 不是 local variable, 實質上它是 global variable. 本範例把 global variable 定義在 函數之內, 純粹只是為了把所有的東西都放在函數之內, 使之看來比較整潔而已. 但是, 我必須嚴肅地說, static local variable 不是好東西, 非不得而, 請千萬不要使用. 於 閱讀 除錯 維護 開發, static local variable 也是一大隱患 (global已是'患', '隱'則把傷害十倍之, 戒之 戒之).
作者 : ndark(NDark)
[ 貼文 25 | 人氣 0 | 評價 240 | 評價/貼文 9.6 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 10:05:40
我手上也自己寫了一個中文字體顯示器的類別.
底層大概跟你差不多.有興趣可以丟給你看一下.

我的經驗上是不建議把matrix放在字體顯示函式裡面.(應該說盡量不要把非關的設定寫死在呼叫裡面.)
因為永遠都不知道使用者想怎麼用你的函式.

這方法作出來是3Dpolygon.因此,
1.字型大小不用擔心.用scale就成了.
2.如前述,當matrix拉出來的時候,這馬上就變成一個3D的字型顯示器.在某些場合會更有彈性.

NDark
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 10:22:47
在秀文字之前, 我們要做一些準備功夫.

關掉光源, 因為字型圖像沒有法線, 加上光源也沒啥好看的, 倒不如用顏色更好.
關掉 depth testing, 讓文字在最上層顯示.
使用自訂的 model-view matrix 和 projection matrix, 使秀文字時, 都用統一的比例.


int glPrintf( const char *format, ... )
{
  // ...

  glGetIntegerv( GL_VIEWPORT, viewport ); // 取得 view-port, 設定 projection matrix 時需要的
  glGetFloatv( GL_CURRENT_RASTER_POSITION, p0 ); // 取得 raster position, 換新行時, 用它來計算座標的

  glPushAttrib( GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT ); // push attributes, 為還原設定作準備
    glDisable( GL_LIGHTING ); // 關掉光源
    glDisable( GL_DEPTH_TEST ); // 關掉 depth testing

  glMatrixMode(GL_PROJECTION); // 自訂 projection matrix
    glPushMatrix(); // 為還原 projection matrix 作準備
    glLoadIdentity();
    gluOrtho2D( 0,viewport[2],0,viewport[3]);

  glMatrixMode(GL_MODELVIEW); // 自訂 model-view matrix
    glPushMatrix(); // 為還原 model-view matrix 作準備
    glLoadIdentity();

  // ...
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 10:29:34
int glPrintf( const char *format, ... )
{
  // ...

  if( strcmp( buffer, ">>glut" )==0 ) // 如果來源字串是 ">>glut", 就把秀文字的起始位置設到左上角.
  {
    glRasterPos2f( 4, viewport[3]-nHeight );
  }else if( strcmp( buffer, ">>free" )==0 ) // 如果來源字串是 ">>free", 就把預載字型資料刪除. (範例沒有使用它的)
  {
    glDeleteLists(base,65536); base=0;
    memset( loaded, 0, 65536*sizeof(bool) );
    DeleteObject(hFont);
  }else // 秀文字
  {

   // ...
}


作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 11:00:35
好好好, 終於說到秀文字了. (><" 好累...)

在秀文字之前, 我們要把 single-byte double-byte 混合字串 換轉成 大五碼, 要轉來轉去, 大概是歷史因素吧, 這面鏡子太豐富, 我也不便多題了, 有興趣的網友們可以去參考 http://zh.wikipedia.org/zh-tw/%E5%A4%A7%E4%BA%94%E7%A2%BC .

換轉成大五碼之後, 我們就可以逐一字元處理.

如果 字元 是 '\n', 我們就換新行;

如果 字元 不是 '\n', 而該字元未被載入, 我們就把字元載入, 最後, 繪畫對應字元的 display list.

有一個部份網友們要特別注意的, 字串, 是被秀了兩次, 第一次秀字串 是 右下方移 1 pixel 和 用黑色, 第二次秀字串 是原來的位置 和 glColor() 所述的顏色. 這樣, 透出來的文字, 就會在 右下方 有點黑色的點綴, 就算是在相同顏色背景中, 也能被看到.


int glPrintf( const char *format, ... )
{
  // ...

  }else // 秀文字
  {
    glRasterPos2f( p0[0], p0[1] ); // (這句是沒實質作用的, 不過, 要先呼叫它, 下一句 glGetFloatv() 才能正確執行, 奇哉, 怪哉. )
    glGetFloatv( GL_CURRENT_RASTER_COLOR, c0[1] ); // 最得 glColor() 所述的顏色
    len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buffer, -1, 0, 0); // 計算轉換成大五碼後, 字串的長度
    wstr = (wchar_t*)malloc( len*sizeof(wchar_t) ); // 配置記憶體去儲存 大五碼字串
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, buffer, -1, wstr, len); // 把 buffer 轉換成 大五碼字串
    for( j=0; j<2; j++ ) // 秀兩次字串
    {
     glColor4fv( c0[j] ); // 設定 字串顏色
     glRasterPos2f( p0[0]+offset[j], p0[1]-offset[j] ); // 設定 字串起始位置
     for( i=0; i<len-1; i++ )
     {
     if( wstr[i]=='\n' ) // 如果是 '\n', 就換新行
     {
     glGetFloatv( GL_CURRENT_RASTER_POSITION, (float*)&p1 );// 取得現在的位置
     glRasterPos2f( 4+offset[j], p1[1]-(nHeight+2) ); // 設定新行的起始位置
     }else
     {
     if( !loaded[wstr[i]] ) // 如果字元未被載入
     {
     if( hdc==0 )
     {
     hdc = wglGetCurrentDC(); // 取得 device context 的 handle (視窗系統的東西)
     SelectObject( hdc, hFont ); // 選取 font handle (視窗系統的東西)
     }
     wglUseFontBitmapsW( hdc, wstr[i], 1, base+wstr[i] ); // 載入字元的字型圖像
     loaded[wstr[i]] = true; // 標示這字元已被載入
     }
     glCallList(base+wstr[i]); // 繪畫字元的 display list
     }
     }
    }
    free(wstr); // 把配置了的記憶體歸還系統
  }

  // ...
}
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/14 下午 11:12:33
如果你完成了這個範例的話, 請你用 glPrintf() 秀出 "白老鼠愛上貓" 六個字, 然後在這簽個名吧.

好, 終於都講述完畢了, 這個 glPrintf() 的函數, 不是什麼大作, 也不是用了很長的時間去寫, 只是貪圖使用方便的小工具, 為什麼講述起來這麼費勁?!

好累... 休息去...
作者 : wang3529(兔妹) 貼文超過200則人氣指數超過10000點
[ 貼文 307 | 人氣 27736 | 評價 50 | 評價/貼文 0.16 | 送出評價 36 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/16 下午 03:23:33
太感謝了
我之前使用底下這樣的方式秀中文
hdc = wglGetCurrentDC();
SelectObject( hdc, hFont );
然後使用TEXTOUT秀字 整個文字閃爍的好厲害
TextOut(hDC,0,0,Buffer,strlen(Buffer));
感謝白老鼠大大 總算可以正常顯示中文字了
作者 : wang3529(兔妹) 貼文超過200則人氣指數超過10000點
[ 貼文 307 | 人氣 27736 | 評價 50 | 評價/貼文 0.16 | 送出評價 36 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/18 上午 09:47:22
再請問白老鼠大大一個問題
我如果想把文字移到特定位置
比如說我從x=100 y=100的座標上開始秀字
要怎麼改呢
我試著修改glRasterPos2f的值
但是還抓不到他的規律
作者 : ma_hty(白老鼠(Gary))討論區板主 OpenGL卓越專家DirectX優秀好手C++頂尖高手貼文超過2000則人氣指數超過70000點
[ 貼文 2171 | 人氣 89850 | 評價 10110 | 評價/貼文 4.66 | 送出評價 79 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/9/18 下午 01:56:56
static int x0=0, y0=0; // 加
  if( memcmp( buffer, ">>glut", 6 )==0 ) // 改
  {
    sscanf( buffer, ">>glut(%i,%i)", &x0, &y0 ); // 加
    glTranslatef( x0, -y0, 0 ); // 加
    glRasterPos2f( 4, viewport[3]-nHeight );
  }else if( strcmp( buffer, ">>free" )==0 )
  {
    // ...
  }else
  {
    // ...
     if( wstr[i]=='\n' )
     {
     glGetFloatv( GL_CURRENT_RASTER_POSITION, (float*)&p1 );
     glRasterPos2f( 4+x0+offset[j], p1[1]-(nHeight+2) ); // 改

做了上述的五項改動. 你就可以用下列的三種方式去設定開始秀字的位置.

  glPrintf( ">>glut(%i,%i)", 100, 100 ); 或
  glPrintf( ">>glut(0,0)" ); 或
  glPrintf( ">>glut" );







作者 : cromayen2000(CROMAYEN2000) OpenGL卓越專家貼文超過500則人氣指數超過10000點
[ 貼文 645 | 人氣 22308 | 評價 2260 | 評價/貼文 3.5 | 送出評價 38 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/11/6 下午 01:56:30
類別化的精簡版 當然 功能沒有原來那麼豐富,比較像 MFC 的 Textout

變更了幾個地方
1. 用了 glWindowPos2i 這個 1.4 版的指令,記得要載近來
2. 因為有掛載 MFC 所以 CreateFont 變成 ::CreateFont 如果是其他環境可能要修改一下

class CTextout {
public:

DWORD m_CharSet; // 使用的字元集
char m_FaceName[128]; // 字型名稱
int m_FontHeight; // 文字大小
int m_ListBase; // OpneGL 顯示集
bool m_Loaded[65536]; // 已經載入的文字標記
HFONT m_hFont; // 字型物件
HDC m_hDC; // 裝置本文

CTextout();
virtual ~CTextout();
bool CreateFont(int Height = 16, char * Facename = "Courier New", BYTE CharSet = CHINESEBIG5_CHARSET);
bool TextOut( int x, int y, const char *Format, ...);
};

CTextout::CTextout():
m_CharSet(CHINESEBIG5_CHARSET),m_FontHeight(16),m_ListBase(0),m_hFont(NULL),m_hDC(NULL)
{
strcpy(m_FaceName,"Courier New");
ZeroMemory(m_Loaded,65536);
}

CTextout::~CTextout() {
glDeleteLists(m_ListBase, 65536);
DeleteObject(m_hFont);
}

bool CTextout::CreateFont(int Height, char * Facename , BYTE CharSet) {
// 如果沒有建立顯示列則建立
if( m_ListBase == 0)
m_ListBase = glGenLists(65536);
// 保留原本的字型
HFONT OldFont = m_hFont;
// 建立新的字型
m_hFont = ::CreateFont( Height, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, CharSet, OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_DONTCARE | DEFAULT_PITCH, Facename );
m_hDC = wglGetCurrentDC();
SelectObject( m_hDC, m_hFont );
// 如果已經建立字型則將原來的字型釋放
if(OldFont != NULL)
DeleteObject(OldFont);
return true;
}
作者 : cromayen2000(CROMAYEN2000) OpenGL卓越專家貼文超過500則人氣指數超過10000點
[ 貼文 645 | 人氣 22308 | 評價 2260 | 評價/貼文 3.5 | 送出評價 38 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2009/11/6 下午 01:58:25
bool CTextout::TextOut(int x, int y, const char *Format, ...) {
// 處理參數混合
char Buffer[65536];
va_list args;
va_start(args, Format);
vsprintf(Buffer, Format, args);
va_end(args);

if(m_hFont == NULL)
CreateFont();
// 取得繪圖區大小
GLint Viewport[4];
glGetIntegerv( GL_VIEWPORT, Viewport );

// 取得目前光柵的顏色
float Color[4];
glGetFloatv( GL_CURRENT_RASTER_COLOR, Color );

// 將屬性推入堆疊
glPushAttrib( GL_LIGHTING_BIT | GL_DEPTH_BUFFER_BIT | GL_CURRENT_BIT);
// 關掉燈光跟深度測試
glDisable( GL_LIGHTING );
glDisable( GL_DEPTH_TEST );

glWindowPos2i(x,y);
// 把文字轉換成寬字元
int len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Buffer, -1, 0, 0);
// 配置寬字元的記憶體
wchar_t *wstr = (wchar_t*)malloc(len * sizeof(wchar_t));
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Buffer, -1, wstr, len);
int Index;
for(int i = 0; i < len-1; ++i) {
Index = wstr[i];
// 如果 換行符號就調整位置
if( Index == '\n' ) {
// 取得目前光柵的位置移動到下一行的開頭去
float RasterPosition[4];
glGetFloatv( GL_CURRENT_RASTER_POSITION, RasterPosition );
glWindowPos2i(x, RasterPosition[1] - m_FontHeight);
}else{
// 如果沒有輸出過這個文字
if( !m_Loaded[Index] ) {
// 建立文字的 List
wglUseFontBitmapsW( m_hDC, Index, 1, m_ListBase+wstr[i] );
m_Loaded[Index] = true;
}
// 呼叫 文字的 List
glCallList(m_ListBase+wstr[i]);
}
}
free(wstr);
glPopAttrib();
return true;
}
作者 : boss901351(boss901351)
[ 貼文 6 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/4/28 上午 10:58:46
白老鼠老大小弟我執行你的程式碼在這段
     hFont = CreateFont( nHeight, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, fdwCharSet,
     OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
     FF_DONTCARE | DEFAULT_PITCH, szFace );
會顯示以下這個錯誤
錯誤 2 error C2664: 'CreateFontW' : 無法將參數 14 從 'char [12]' 轉換成 'LPCWSTR'

作者 : boss901351(boss901351)
[ 貼文 6 | 人氣 0 | 評價 0 | 評價/貼文 0 | 送出評價 0 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2010/4/28 上午 11:04:44
原來是CreateFont會自動判別為 CreateFontW
如果直接設為 CreateFontA就可以正常運作
作者 : wang3529(兔妹) 貼文超過200則人氣指數超過10000點
[ 貼文 307 | 人氣 27736 | 評價 50 | 評價/貼文 0.16 | 送出評價 36 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/7/3 下午 05:13:06
請問cromayen2000所提供的方式
我編輯時找不到glWindowPos2i的存在耶
請問他上面寫的1.4版本是指opengl1.4版嗎?

請問3D遊戲程式設計/基礎篇 作者:冬陽
這本書上面付的LIB與DLL是OPENGL哪一個版本的呀
我現在用的OPENGL的LIB就是這本光碟上附的
怎麼看版本呀
作者 : cromayen2000(CROMAYEN2000) OpenGL卓越專家貼文超過500則人氣指數超過10000點
[ 貼文 645 | 人氣 22308 | 評價 2260 | 評價/貼文 3.5 | 送出評價 38 次 ] 
[ 給個讚 ]  [ 給個讚 ]  [ 回應本文 ]  [ 發表新文 ]  [ 回上頁 ] [ 回討論區列表 ] [ 回知識入口 ]
2012/7/4 下午 04:29:29
>我編輯時找不到glWindowPos2i的存在耶
>請問他上面寫的1.4版本是指opengl1.4版嗎?

是的 這個函數要額外的掛載
你可以透過 glew (http://glew.sourceforge.net/)這個延伸函數載入庫來實現這個動作
 板主 : 白老鼠(Gary)
 > OpenGL - 討論區
 - 最近熱門問答精華集
 - 全部歷史問答精華集
 - OpenGL - 知識庫
  ■ 全站最新Post列表
  ■ 我的文章收藏
  ■ 我最愛的作者
  ■ 全站文章收藏排行榜
  ■ 全站最愛作者排行榜
  ■  月熱門主題
  ■  季熱門主題
  ■  熱門主題Top 20
  ■  本區Post排行榜
  ■  本區評價排行榜
  ■  全站專家名人榜
  ■  全站Post排行榜
  ■  全站評價排行榜
  ■  全站人氣排行榜
 請輸入關鍵字 
  開始搜尋
 
Top 10
評價排行
OpenGL
1 白老鼠(Gary) 2720 
2 CROMAYEN2000 1530 
3 aming 500 
4 東昇 380 
5 PLAYER 120 
6 富伯 110 
7 qq 100 
8 NDark 80 
9 ozzy 60 
10 simula 60 
OpenGL
  專家等級 評價  
  一代宗師 10000  
  曠世奇才 5000  
  頂尖高手 3000  
  卓越專家 1500  
  優秀好手 750  
Microsoft Internet Explorer 6.0. Screen 1024x768 pixel. High Color (16 bit).
2000-2018 程式設計俱樂部 http://www.programmer-club.com.tw/
0.09375