Nibiru经测试,用了OpenGL的wglUseFontBitmaps,glCallLists函数来显示文字。 $ ]# e G/ E$ `9 ~
也许是一个汉化的突破口? ' h. i4 X* l2 k7 @' e
! \! i ]1 s0 ^6 x---- 本文详细讨论了在OpenGL中显示文本的几种方法。 * [4 T [" O$ s# n) E
8 L/ X1 _/ k! i- y I3 A; ]2 o4 {
----也许大多数程序员使用OpenGL更多的是将精力集中于动态三维图形应用,因此,OpenGL中的文本显示往往被忽视,使人有不见积薪之感。本文介绍了几种文本显示的方法,希望能对使用OpenGL的编程者有所帮助。 + X4 m: h& j, D4 M8 g, H* H% ~' a
: G* |, R4 R" ~
建立并修改程序
0 g, J& z( f4 R% I----建立一个MFC SDI Windows应用工程Text,除单文档属性外,使用其他的所有默认选择。在菜单Project打开Settings对话框,在Link属性页的 object/library modules编辑框中加入opengl32.lib glu32.lib glaux.lib三个GL库。我们利用这些库函数完成图形编辑工作。 ; r1 q# Z) Q1 \) C( w8 z" c, I
----为使VC++的AppWizard产生的SDI应用程序能使用 OpenGL绘图,还需要作一些修改,说明如下。
* `- x2 ~+ ~0 M# h$ y
1 f8 ?4 u' c- U0 D' u0 z" t----1.介绍PreCreateWindow函数
0 k: D- E8 t' d4 V7 y) L2 R! H9 y# [
---- OpenGL窗口必须具有WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时剪裁子窗口所覆盖的区域)和WS_CLIPIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)两种风格。此外,窗口类属性不能包括CS_PARENTDC风格。具体程序实现如下: " P( }2 t! P7 M: h
, \' k6 j8 u9 y2 Q$ a; P$ i3 }8 fBOOL CTextView::PreCreateWindow
, g) @# v. u' I(CREATESTRUCT& cs)
# c/ J2 x% y9 ]& `. ?/ L{ ( K' U, Q2 X. @: Z: C
// TODO: Modify the Window class or styles here by modifying ( ~ J* y2 \- p* Z. t) N! h
// the CREATESTRUCT cs 5 Y% w7 e; e4 b2 ]1 ?: r; z
2 P, K8 u/ b: s( A, J//An OpenGL window must be created with the following flag
: _% w0 E N7 w: q. W// and must not include CS_PARENTIDC for the class style.
) _/ c2 ^0 G k& }2 E a$ C3 Ycs.style|=WS_CLIPSIBLINGS|WS_CLIPCHILDREN; : H9 I( f" d: W0 ]/ F2 E! y
* [) I1 E# Z* v( h2 f* b! z5 h
return CView::PreCreateWindow(cs); 9 `' v, \# g# E5 R9 `9 i' b
} # V7 e( E+ x2 |- }, }7 T# ^
+ ~8 r1 h( a$ a
----2.OnCreate函数中定义像素格式PIXELFORMAT和创建 RC ' X$ g+ E6 J% V8 C
2 m3 U+ `+ h; |
----要使窗口支持OpenGL绘图,必须对窗口进行初始化。其中包括定义像素格式PIXELFORMAT和创建RC,为OpenGL指定一个合适的像素格式,创建着色上下文并将它和窗口的设备上下文关联起来。着色上下文保存着当前着色环境的信息。可在OnCreate中调用一个自建视口成员函数SetupPixelFormat(),具体函数如下:
, e! T# R0 |* Q
4 X4 D l3 J4 @+ kBOOL CTextView::SetupPixelFormat()
$ k3 w! R$ }3 G{ & u4 B5 C# }! {0 s7 t. ?
//Create a rendering context
/ W7 D8 x. D/ c, I+ ECDC* m_pDC=GetDC();
, E" B" y$ _" P; M# @if(m_pDC==NULL) //failure to get DC ' }. k5 d H, H& G# q" w
{
$ s' j2 v( N: F UMessageBox(“Could't get a valid DC."); / [% `# H8 v; y p2 ~' d6 ]
return FALSE; ) f3 j" R' U! d: k5 j$ [) E
}
2 ?* d: c/ s5 i5 ]/ ]! d
% v5 _6 F9 S, C+ D//Default pixel format is a single-buffered, : S- a3 n# o7 o7 o: A
//OpenGL support hardware-accelerated,RGBA mode format # [! F7 A( F! N S
PIXELFORMATDESCRIPTOR pfd =
0 G! |8 \% v7 D( t{
a" G9 M7 M1 [' o8 K" U( rsizeof(PIXELFORMATDESCRIPTOR),//Structure size. 9 z7 B: l$ q% {. |8 ?
1,
! h) g" a' U- |// Structure version number.Property flags(特性标志): / \; ]$ c9 e3 N \( T2 Y
PFD_DRAW_TO_WINDOW | // support window
- n5 ^& H0 j" U: e% GPFD_SUPPORT_OPENGL | // support OpenGL 9 h: H+ ?) ~6 B) N9 G6 ^# i6 ~
PFD_DOUBLEBUFFER, * H: P3 Q2 G' c) {( q" F/ c
PFD_TYPE_RGBA, // RGBA type . w1 n* }5 C: `- {3 K
24, // 32-bit color.
7 m7 A3 r, @! o# W$ y0, 0, 0, 0, 0, 0, // Not concerned with these:不涉及的属性
4 a @! s+ M2 U1 d& N1 p: v# }5 R0, // No alpha :无alpha缓存 * R$ B* a7 N4 E# U& W
0, // Shift bit ignored:忽略转换位
0 B9 N& Z) ]! ~* @" _ z8 z3 T$ }0, 0, 0, 0, 0,// No accum buffer:没有累积缓存
! M3 C" I G B2 m3 r32, // 32-bit depth buffer. 5 M) |7 O# D3 k! x& U
0, // No stencil:无模板缓存 ! v- u$ ?5 W2 E" `, `- _
0, // No auxliliary buffers:无辅助缓存 % W0 N6 K8 `$ |( \% L E
PFD_MAIN_PLANE, // Main layer type.:主层类型
# d; M6 p: i, B# I6 L0, // Reserved.:保留结构数 ; |8 k9 n8 i* P1 f
0, 0, 0 // Unsupported.:不支持结构数
2 W% p3 `, d& l" ~/ f1 O) m" E$ b}; / D2 t. W* w% R6 ?7 m
int nPixelFormat=
, R' g# M$ q7 \) q% ` |ChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd); , t% h+ E5 g9 K' v
if( nPixelFormat ==0) ( x4 b* M4 a% O0 c! h& ^: x( g
{
9 V! a+ y2 n+ S" @MessageBox(“ChoosePixelFormat failed.");
% v; R* F! o0 C/ S" Ureturn FALSE;
7 h! h, k, z1 S, f) }$ A+ i}
1 }! w4 b, l+ O# ~# }0 f% v! L. M4 Z2 ~# J0 z1 ~( \: c1 {, r, \
if(SetPixelFormat(m_pDC->GetSafeHdc(),
+ ?- ~- ]* z+ J* A M6 L2 ^nPixelFormat,&pfd)==0) : l! ^7 w7 ]. X4 f, b
{ ) Y9 ~5 H- O" O7 S" o! \' o
MessageBox(“SetPixelFormat failed."); 6 ?, G p: _ V& a
return FALSE; , ^. E0 r) Y$ O
} c6 r7 }# |3 M8 }
$ l8 h6 b$ _$ O7 j8 s
if( (m_hRC=wglCreateContext(m_pDC-> 1 i, M6 f6 K# e x* @1 W
GetSafeHdc())) ==0) 4 u5 [" @. g4 N _' `0 y( l; w9 b
{ : o9 N" ~6 g2 g) q5 V
MessageBox(“wglCreateContext failed."); & x( J3 l: T& e- [8 L0 e
return FALSE;
/ D. c$ u% r3 y9 K! w$ d: L# {} 1 T" z4 v3 g5 A; ?5 j
if( (wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC)) ==0) , P# ]; }# J2 b! O
{ $ o& i, Y7 e) d3 a/ F- p
MessageBox(“wglMakeCurrent failed.");
% s& r7 H) _& d8 L$ J d9 c3 Breturn FALSE; 0 v ]. T2 t7 d7 S ]2 n G1 h
} * N# t" l ?% C+ X& n% c3 X
6 a9 _8 F q3 X' lif(m_pDC) ReleaseDC(m_pDC);
2 U T" v6 e0 M* I, @" greturn TRUE; ! z0 `# |3 S' L
} $ }! u) F" j* y. S0 k
! {- A' F- b4 N _3 ]6 g- U
----3.在OnCreate()函数中调用初始化背景函数 InitializeOpenGL()
1 E3 G: J4 l% v* f, G# W( Y* c
+ \) O6 s3 r! [- A) a, \3 ]void CTextView::InitializeOpenGL() & X& D7 F: b9 C/ v8 d
{
/ L2 z. j% E3 U* w iglClearColor(0.2f,0.2f,0.2f,0.0f);
( P- ]/ K' N: l6 sglClearDepth(1.0);
% n) _' T/ M9 T) x* [) _ dglDepthFunc(GL_LESS);
; V* f$ G! m: b. v9 Z% OglEnable(GL_DEPTH_TEST); . G, d; _$ ~) {0 e' f7 P; S
glShadeModel(GL_SMOOTH); 8 G4 U5 h' I7 c' ^) n! ]
} 2 ^* z1 V H6 j4 L7 k( w3 d* T
' X9 k' @$ L* }) w( _1 k----4.在OnCreate()中启动动画定时器 & T+ W# c, r# c
; k/ x' c6 @' p/ ]SetTimer(0,40,NULL);
5 i, ]$ I' h4 o! s" x! V# y6 u ~7 P: }4 E
----5.在收到WM_SIZE消息时要重新计算场景尺寸,用OnSize 设置图形显示模式
F- G6 Y, x5 {* x9 ^
5 N7 \' i* G2 J% [! Q5 z0 B: i----为了使物体能合适的显示,必须要经过投影和确定视口的工作。
5 B! }/ s; K9 w: B6 r5 q
# J; n8 U# P7 ]4 x' x, S- zvoid CTextView::OnSize(UINT nType, int cx, int cy)
! X0 W3 ]3 w5 F4 T{ 4 \1 f! B7 m" P: e+ M6 P
CView::OnSize(nType, cx, cy); " \: Q' w. K+ m( I
" u( D2 {' E% y l ]* p8 M
// TODO: Add your message handler code here - K* T* E4 R. N, p- ~, ~ d
//Save the wide and height of the current window Client ! j0 _. S6 v2 k6 U9 m3 q
GLsizei nWidth=(GLsizei)cx;
! S3 A# m& Z# _1 i- a' UGLsizei nHeight=(GLsizei)cy;
2 x" [8 Q4 f5 j6 G6 U: R7 Xratio=(double)cx/(double)cy;
; c9 _: }0 E# e1 r% t
1 c# F' X6 z* {% _, G1 m//Coupute the aspect ratio
0 {" J+ ]1 e3 G0 E3 ^* v" \% tGLdouble dAspect=(GLdouble)nWidth/(GLdouble)nHeight; * D6 v7 C1 A" @ ~
4 k5 ?4 Y* A& d$ PglViewport(0,0,nWidth,nHeight); * t) @$ i2 l, z$ _
glMatrixMode(GL_PROJECTION); " p6 x4 [( V, Y) N7 c! k8 w
glLoadIdentity();
2 h- l% g \+ Y' egluPerspective
* ?6 A, Q! K: N(FOV,dAspect,NEARPLANE,FARPLANE);
/ t$ u; u9 j ~. {0 }' k
$ |9 |$ d( |/ C/ MglMatrixMode(GL_MODELVIEW);
8 C8 k/ v, t9 S9 p; XglLoadIdentity(); ! i9 _+ u8 ?) `8 ^" B
}
@) `/ b ^: D1 {/ W$ f0 [/ m
7 x3 J" t0 ~& f! D! Y; z( f3 Q. m1 J----6.在OnDraw()中简单调用DrawScene()以执行OpenGL函数
. I5 u6 y8 P) \, [, M# N+ {0 y. f+ n# c0 u
void CTextView::OnDraw(CDC* pDC)
" `) j+ n; p. X8 G( @1 p# U) g5 z{
+ Y+ @/ {; L9 |1 N9 A, wCTextDoc* pDoc = GetDocument();
% R: ?' D: q, Z. CASSERT_VALID(pDoc);
3 f( A# l# B9 x" H K# p- V
- i9 x' j. r1 B4 D' n" O L// TODO: add draw code for native data here 7 y/ m7 j+ V, S& j
DrawScene(); " v9 h& X; O# B8 o: t5 Q7 B* }9 W
//Invalidate();
$ S% X& E& P5 u3 k$ }1 X}
4 I8 g" B9 w6 v! i
$ i! K' L' `( v6 L( u----7.撤销视窗时删除上下文并撤销定时器 . n9 S( `, e; O, ~; ~
/ e6 C5 g8 N P" ^: C- r$ }
void CTextView::OnDestroy()
+ p# \2 n1 L9 V4 X4 f{ 1 i8 b5 L d7 O4 I2 T
CView::OnDestroy(); 2 {8 [4 v/ A' x) A' k" I1 [* d4 |" o
% n; v8 t5 T: M
// TODO: Add your message handler code here
2 M; c3 m6 F% R2 M$ U7 w//This call makes the current RC not current
* l, ~* t4 U& yif(wglMakeCurrent(0,0)==FALSE)
8 V3 h1 X! v% M9 S, w3 p; w# b7 ~MessageBox(“wglMakeCurrent failed."); $ h( p( _: V/ ]) Y& B
, c3 Z" j; X3 E6 ]# c: }//delete the RC " K+ s# x8 n5 X T: \
if(m_hRC && (wglDeleteContext(m_hRC)==FALSE))
4 w) b9 [% J) \3 BMessageBox(“wglDeleteContext fail."); ' `. {; Y3 o0 f" S
KillTimer(1); J [. f H) w* h3 N1 p$ ]% R" y8 V
}
' E6 G: h8 C$ N1 T/ B; Z& p
: v7 |9 \4 f( _: r----8.当40ms定时器时间到时,简单地将整个场景的客户区置无效,使之重画 ) i9 u6 v2 b9 d0 `! \3 C
' n% @8 p* Z0 K7 N+ t* l# M# n
void CTextView::OnTimer(UINT nIDEvent) , j/ w2 x) @7 x
{
Q. Z+ ]$ ^+ u q; y// TODO: Add your message handler code here and/or call default
" H% a: d5 d# g7 @3 lInvalidate();
$ ^' x" t" G ^: ]: lCView::OnTimer(nIDEvent); ) S6 H7 [* a' n2 L& S
} m; }, E: ?) |6 v1 M
& V. S1 U& ?3 J+ M( e修改界面
6 p" U% e) R+ b& [---- 删除菜单IDR_MAINFRAME中File下除去Exit菜单项外的所有选项,添加“显示GDI文字” “列表制作文字”“列表三维文字”三个菜单项,并给他们分配适当的ID。使用ClassWizard为这三个菜单项在视类中添加命令处理函数和界面更新函数,其中显示GDI文字的对应的函数如下:
2 c/ }( Z' N' Z0 R1 c4 h6 tvoid CTextView::OnGdiText()
+ I+ A" a5 u, F; B# u" s# F, |4 w{ 4 s4 D, F- H& `9 J0 [$ U* [
// TODO: Add your command handler code here % Q: G% C: O0 \) M0 P+ H
m_iWhichText=0;
/ {* T/ L. a) V3 S5 y8 K: _5 XInvalidate();
. `3 U A, B9 b7 {} * x* x5 [" v9 `/ t. {1 H5 O4 M
# O: B+ ]' g, U/ v1 c
void CTextView::OnUpdateGdiText(CCmdUI* pCmdUI) ( [5 H! K$ Z6 T" ^9 h* L" D$ ?7 u
{ , M4 o' n, ~$ {1 b6 t% `' e& A6 N
// TODO: Add your command update UI handler code here
3 m3 ]5 M b. Y: `! Kif(m_iWhichText==0) pCmdUI->SetCheck();
1 U. l W: P( Gelse pCmdUI->SetCheck(0);
( [+ @2 f- ?" j}
' v; h+ z6 P' G( d. S. \
) I2 ^/ F+ u/ [, v$ s; }& y6 v----增加Draw3DText()、DrawListText()和DrawGdiText()三个函数用于三种不同的文本绘制方法。增加DrawScene()函数,它被OnDraw函数调用,用于绘制场景。在DrawScene()函数中,当m_iWhichText为0、1、2时,分别调用 DrawGdiText()、DrawListText()和Draw3DText()显示文本。具体函数实现如下: 0 r# w8 Q" F$ X8 e3 v, v, |9 B
f8 |4 I9 n' V1 C2 S$ P2 E$ Z
void CTextView::OnDraw(CDC* pDC) 9 Y% U6 n- u) O% ^* o7 g
{
1 Y8 M" H* _- ~( ?+ o4 vCTextDoc* pDoc = GetDocument();
6 I9 o- \9 [7 f( G7 Y5 Q1 mASSERT_VALID(pDoc); 4 L1 q+ }' g2 d2 q( b
3 h9 ~5 x0 b$ {6 r) w) o% p" O// TODO: add draw code for native data here
2 @$ ?, A) ~7 i' n I" l3 P" DDrawScene();
9 {+ i, z i* B0 m8 M//Invalidate(); ) G$ x& `/ D+ N1 y# H: S
}
) L) Y F! n3 S
+ N8 n7 S, {# c+ H1 J$ f( Mvoid CTextView::DrawScene() ' S$ } x3 I+ r a
{ & \' c0 R' I$ Y2 @% j
glClear 8 B$ a) }6 k/ I; R' O/ H2 U
(GL_COLOR_BUFFERBIT|GL_DEPTH_BUFFER_BIT); ! o! M# I- |( ?3 U) w8 q5 M
9 c7 a T4 q. R. b+ mglPushMatrix();
) l( g6 o% }3 K" qglTranslatef(0.0f,0.0f,-FARPLANE);
2 l) I; M% n6 {- M//TextureMap(); 1 x; M# V. s! b7 f0 t
glPopMatrix(); / ?; ~7 O: u0 u+ T. I" O
glPushMatrix(); 0 P. C( E& F5 b9 Z( I0 e; q4 L
glTranslatef
2 s* l) N N$ I0 y$ f! ~2 |* t(0.0f,0.0f,-(FARPLANE+NEARPLANE)/2); * o! Q* S3 g5 N$ g1 ]
1 H8 }9 B/ F" k2 e
if(m_iWhichText==1) DrawListText();
1 i% q: u: e6 p6 x3 l' @if(m_iWhichText==2) Draw3DText(); / P9 j* l# v: s% ^- C! S& {
glPopMatrix(); . L B# |% w2 `! m4 A0 g8 Y
glFinish();
' @7 F( M0 y8 Z( KSwapBuffers(wglGetCurrentDC()); : [! w4 _7 ^. W9 L/ X# v
" e6 U `( ?+ q4 R
if(m_iWhichText==0) DrawGdiText(); 6 w. f8 q) p, q# ?) z9 h
} T {6 \8 t' h7 X. y7 x
( K2 J# e4 Y) y
GDI 显示文本
% R/ Z9 T/ e2 p---- 调用wglGetCurrentDC()函数取得当前的设备上下文,使用TextOut函数显示文本,不过要注意在DoubleBuffer模式下,绘制函数要在glFinish()和 SwapBuffers(wglGetCurrentDC())函数之后调用,否则会产生闪烁,在绘制OpenGL结束之前使用GDI函数,要除去闪烁则只能使用SingleBuffer模式,具体函数如下: ! {) Q. n! q8 E# y) _
void CTextView::DrawGdiText()
- H+ H- e; Y4 ^' U{ , S( {8 {. @1 P: p
HDC hdc=wglGetCurrentDC(); # h7 L8 |$ W, P' f1 O! y
::SetBkMode( hdc, TRANSPARENT ); 2 W4 ^3 W/ X' |( `3 t
::SetTextColor( hdc, RGB(250,0,0) ); 2 k9 n. m8 R& E& n4 k/ Y4 n9 C
) q# g9 H7 r6 i9 J1 q% f4 h3 jCString sState(“显示GDI文本。"); 2 @6 l2 Z& Z! Y" E4 j4 ]% z
::TextOut(hdc,5,5,sState,sState.GetLength());
/ n( W7 ^9 L8 F# T7 K} 2 n) B/ C6 t7 P4 v! F( v! H9 ]7 n
8 [8 P I* k' `wglUseFontBitmaps
7 |8 N4 B" t& \; ]- `: J9 P函数显示文字
2 }; X- M5 i" ^$ m b' I% {----使用wglUseFontBitmaps()将ASCII字符装入显示列表,然后使用glCallLists()函数利用显示列表序列显示文本。wglUseFontBitmaps有四个参数,分别是当前使用的DC、从第几个ASCII字符起始装入列表、装入列表的ASCII字符数和起始的列表序号。glListBase()指定glCallLists执行的起始列表序列号。glCallLists()含有三个参数:执行列表序列的个数、列表值的类型和所要显示的文本。注意如果所要显示的文本是字符串,它所提供的信息是相对于起始装入ASCII字符的偏移量,因此最终所显示的ASCII字符是从glListBase()所指定的列表起始号在经过glCallLists()中偏移后的列表,因此wglUseFontBitmaps的从第几个ASCII字符起始装入列表参数、glListBase()指定的 glCallLists执行的起始列表序列号和glCallLists()中的所要显示的文本参数都可以影响最终显示结果。由于显示的是ASCII 字符,因此不能显示汉字。glRasterPos3f函数决定在 OpenGL视景体坐标系下的偏移。具体函数实现如下: 5 \% _5 I/ ` U" k+ g- O
void CTextView::DrawListText() & _) z2 y, s9 u" F5 F+ V8 a, U/ I
{
/ J: i- M( Z) k, d( d& U( mwglUseFontBitmaps(wglGetCurrentDC(),0,256,1000);
. o, {9 f% ]8 r, ], ~( j. }glListBase(1000);
. a6 ?) i+ a! u' xglRasterPos3f(-5.0f,0.0f,0.0f); # U8 K/ U! u! [. i! K( s, y
glCallLists(20,GL_UNSIGNED
+ D4 Y3 l8 u4 i/ W5 f( t_BYTE,“Draw with List Text.");
) C6 M2 i L7 q& L- r$ k! O}
& t& Y+ J' _: X3 W" a0 l) y& I8 O3 l% ?0 s5 ]3 U! J+ O
wglUseFontOutlines 2 Y% N! L: j: v0 M3 ^
函数显示三维文字 5 {$ T- X3 S7 \; w9 _8 b
----wglUseFontOutlines使得OpenGL可以显示三维文字。它的用法与wglUseFontBitmaps函数大致相同,但是多了内插计算参数、字体深度、显示方式和装载字模的缓存四个参数,且只能显示TrueType字体,显示前应该先选择字体类型。具体函数实现如下: * [1 S, b9 u' o& {; C
void CTextView::Draw3DText()
9 @/ ?2 j5 a2 V% b{ . C' i1 R: y! @ b" W% j
GLYPHMETRICSFLOAT agmf[256];
2 j3 p3 J) @" J- P$ ]! ?1 L# @// create display lists for glyphs 0 through 255
3 n8 ]- b) y( ^- p9 J// with 0.1 extrusion and default deviation. ! `, o& T$ q% S' A5 E# Z0 a
//The display list numbering starts at 1000
0 N2 Y0 Y# u- z" Y3 P(it could be any number)
7 ^- N# a! u6 E6 k- P8 }wglUseFontOutlines(wglGetCurrentDC(), $ P& e, P0 v8 F( u
0,255,1000,0.3f,0.8f, WGL_FONT_LINES ,agmf); 5 r1 N' F: ]4 b) z$ Q8 U
5 \* s+ Q J1 Q0 r/ P0 E1 t
// Set up transformation to draw the string 5 D7 {1 Z3 \$ A+ ]+ _
glTranslatef(-15.0f,0.0f,0.0f);
3 |7 S4 b) |: W* b) ?glScalef(4.0f, 4.0f, 4.0f); ! O e6 ]2 f0 b5 D6 G
// Display a string
% t. } ^! [7 s6 X; V! y8 l" }glListBase(1000);
) r; F5 u9 ^/ ]9 ?// Indicates the start of display lists for the glyphs
& O- E3 N3 B9 a# b3 z7 }+ D3 ?// Draw the characters in a string ) ~2 @& e, b# L" c+ l
% Y5 v, }; K6 C9 Q
glCallLists(26, GL_UNSIGNED_BYTE, 0 ]* Z2 |5 R6 T3 _8 D& t
“Draw outline list 3D text."); + U9 Q ~. T# ]& z: B( p" A. A
} |