Nibiru经测试,用了OpenGL的wglUseFontBitmaps,glCallLists函数来显示文字。
" u9 v6 v% m6 o+ _( _! F- `也许是一个汉化的突破口?
( ?7 a# r; C3 H' [6 _# m3 J, ^! l$ \& Y* W1 P1 l% Z
---- 本文详细讨论了在OpenGL中显示文本的几种方法。
3 H# Y+ \* z# O8 r9 U* ]) V: A' c2 [
----也许大多数程序员使用OpenGL更多的是将精力集中于动态三维图形应用,因此,OpenGL中的文本显示往往被忽视,使人有不见积薪之感。本文介绍了几种文本显示的方法,希望能对使用OpenGL的编程者有所帮助。
/ ^2 Q7 q0 a! h4 N, {
4 j% G# L" E5 J7 H) s# d4 c2 @: o建立并修改程序
+ e% A8 ?( O: w$ g4 Q----建立一个MFC SDI Windows应用工程Text,除单文档属性外,使用其他的所有默认选择。在菜单Project打开Settings对话框,在Link属性页的 object/library modules编辑框中加入opengl32.lib glu32.lib glaux.lib三个GL库。我们利用这些库函数完成图形编辑工作。
- A9 g$ j7 L7 J% g8 }8 @7 W4 v----为使VC++的AppWizard产生的SDI应用程序能使用 OpenGL绘图,还需要作一些修改,说明如下。
+ v4 f3 R( A% Z3 t& p! x3 l" e9 m/ |; o5 f- P% M
----1.介绍PreCreateWindow函数 ! Z1 _" B/ f8 k S
J; Z2 q5 u" F& \---- OpenGL窗口必须具有WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时剪裁子窗口所覆盖的区域)和WS_CLIPIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)两种风格。此外,窗口类属性不能包括CS_PARENTDC风格。具体程序实现如下: + Y- R6 w H/ g
) N" u$ @1 C$ X( l/ |
BOOL CTextView::PreCreateWindow " w% l& p% S' H! B
(CREATESTRUCT& cs)
, ~8 y8 K$ g+ |; Y8 G{
% G0 J: Z/ {6 x9 D* K6 e, W" ^/ G// TODO: Modify the Window class or styles here by modifying
+ U: L+ f; [ A5 n4 N, b/ f; j' v$ r// the CREATESTRUCT cs 7 ?$ O2 o5 B1 A2 C/ K( {. H
5 f+ P* n; E" Y) b
//An OpenGL window must be created with the following flag
7 O6 z, w: o, f n6 n// and must not include CS_PARENTIDC for the class style. + V0 U5 K0 B; g
cs.style|=WS_CLIPSIBLINGS|WS_CLIPCHILDREN; * Q# v% w- j# c8 X$ a
9 Z. K0 x, `7 V I; Dreturn CView::PreCreateWindow(cs);
( \1 X9 L# q# F9 p+ D; O9 }: Y1 d5 m: L2 O} - d l' o' G4 R3 H1 z& a0 J
* a- A% f3 `* S' V
----2.OnCreate函数中定义像素格式PIXELFORMAT和创建 RC
" Q; c& }- `7 n& K8 Q" r; s. o4 E: {$ X \5 c8 {& `+ k! L: R
----要使窗口支持OpenGL绘图,必须对窗口进行初始化。其中包括定义像素格式PIXELFORMAT和创建RC,为OpenGL指定一个合适的像素格式,创建着色上下文并将它和窗口的设备上下文关联起来。着色上下文保存着当前着色环境的信息。可在OnCreate中调用一个自建视口成员函数SetupPixelFormat(),具体函数如下:
% C) d/ N: f9 J- T6 ?, H& z7 C. P/ F1 P5 f @& I
BOOL CTextView::SetupPixelFormat() 4 H& Z g# @: [$ B/ O/ i- E$ q( O
{ " m2 |# _! L6 H9 @# @6 t
//Create a rendering context
+ ]+ j$ O4 S! b) }CDC* m_pDC=GetDC();
. }! k! L% s7 {' l% D# iif(m_pDC==NULL) //failure to get DC 7 b, I/ U6 X# ~8 p0 f- j* d& F( u% Q
{ : a( Y* a4 d; N! v" I. F {
MessageBox(“Could't get a valid DC.");
' _2 \& J0 [ G& O$ Ireturn FALSE;
! r6 O% b$ f# ~* Z} . {; ?0 k8 h7 D2 W2 F {2 ?4 l$ }1 y
9 N! S* a% S5 h: c) Q, g' Q
//Default pixel format is a single-buffered, 2 B( S- l1 f7 k0 P
//OpenGL support hardware-accelerated,RGBA mode format # \3 k+ t' f9 C2 Q! H! n0 I
PIXELFORMATDESCRIPTOR pfd = 3 ^& \& T( U5 x' a' D
{
! T- Z# D t8 F$ {9 V) ^! H1 J Ysizeof(PIXELFORMATDESCRIPTOR),//Structure size. ( ~3 h! b( [) A2 |) l
1,
7 t# r/ L4 c; H- O( i// Structure version number.Property flags(特性标志):
0 p. R: X, B1 x- u- hPFD_DRAW_TO_WINDOW | // support window 6 D) B$ o; u4 f1 F/ L
PFD_SUPPORT_OPENGL | // support OpenGL
5 k& J8 C/ ]1 Z1 T8 C1 x/ J% qPFD_DOUBLEBUFFER,
/ E& f+ s( D9 g, j3 }PFD_TYPE_RGBA, // RGBA type ( G, z: }8 ]# {) q" @8 u
24, // 32-bit color. I' t. ~5 J3 \. K; p
0, 0, 0, 0, 0, 0, // Not concerned with these:不涉及的属性 # m& o' Z4 O. T6 ~% R# T
0, // No alpha :无alpha缓存
4 }* W6 u, P: f' R' m$ D M0, // Shift bit ignored:忽略转换位 9 Z, E5 ]. D! f9 x" O6 D
0, 0, 0, 0, 0,// No accum buffer:没有累积缓存
2 e6 Q4 s" }- k# E4 l+ i- }32, // 32-bit depth buffer. # B2 j! A' B1 w7 Y9 d
0, // No stencil:无模板缓存
1 z5 C3 p8 G! e$ P4 c0, // No auxliliary buffers:无辅助缓存 ; s7 k9 n) e( z% g9 ]
PFD_MAIN_PLANE, // Main layer type.:主层类型 3 F- t6 v" p: Z7 \6 D7 S6 A4 }, f
0, // Reserved.:保留结构数 8 ]. | A# o' \
0, 0, 0 // Unsupported.:不支持结构数
1 V& B# C8 X! I};
" `! Q0 s q' ] I% a9 Lint nPixelFormat= 4 h h; O# D. o3 b0 r, g" [
ChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd);
- G7 t D* S8 ~6 i( L' Q! `if( nPixelFormat ==0)
1 [6 A2 H- v* I1 `5 I{ . O% \ i) b5 U# `0 a
MessageBox(“ChoosePixelFormat failed.");
2 ~$ j% M% t, L7 t1 S) Qreturn FALSE;
* m1 \$ }4 Y9 t" s; |0 u/ G0 E2 V} 5 \7 \5 C5 K$ q' _, q- ]! O
( ~4 @3 R- t" \+ o/ s8 m9 {if(SetPixelFormat(m_pDC->GetSafeHdc(),
, O( A9 a/ L8 pnPixelFormat,&pfd)==0) Z$ M% V; J8 }, k* q) r
{ ! M" K. ^- P' Q8 D. t4 P7 u
MessageBox(“SetPixelFormat failed.");
5 I0 o, m. a% G. [. kreturn FALSE; / o% U2 H" [* q3 Q8 C
}
, u" k( w! w+ {( o0 B( h
/ O: N0 L$ t5 H6 W' c% K& oif( (m_hRC=wglCreateContext(m_pDC->
0 G7 f/ B( o/ W6 A# WGetSafeHdc())) ==0) 9 G8 A, I# o+ i T
{
4 e' P0 \9 L m' C* I2 }4 p4 WMessageBox(“wglCreateContext failed.");
, V: h$ \! X# freturn FALSE;
/ ?% i. W z6 L2 \5 K& a3 r} : k" n- x7 j1 x3 M0 N% _1 w
if( (wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC)) ==0)
' g* ?* `" N/ |0 a{
. G O5 _6 W' i8 ~MessageBox(“wglMakeCurrent failed."); 1 ~) d1 F( t5 c$ O j
return FALSE; ' Z. B5 R* M8 t8 B# k- G& H
}
) e3 C0 F/ L' A5 Y1 b5 N r3 M# o/ W
if(m_pDC) ReleaseDC(m_pDC); + ?: D6 s4 D0 i* B( Q) }
return TRUE; % c' D% K' i3 T8 W
} 1 p( V) [& y# F! F
3 s! j1 X% H/ m; `" w% A----3.在OnCreate()函数中调用初始化背景函数 InitializeOpenGL() / Z1 J9 F+ N' \3 v$ H. r p
5 |: o5 H! @1 ^( ~. zvoid CTextView::InitializeOpenGL()
1 ~: z8 P4 r$ u& s3 W0 e& }9 h{
3 {- r# g8 F+ u! W7 B# l3 o, bglClearColor(0.2f,0.2f,0.2f,0.0f); . o; V9 b8 g/ V% ~# B4 Q$ M
glClearDepth(1.0); - O# y* L, a% [+ I' R
glDepthFunc(GL_LESS);
1 _! F0 ?& c6 n/ |: \glEnable(GL_DEPTH_TEST); o, ]' t l- A% L% p! p. _7 K2 u+ X
glShadeModel(GL_SMOOTH);
- A2 S& e r/ b# ~4 W} 0 q% q* |3 z1 {+ v
4 Q2 l- }! Z( y9 i$ S% a
----4.在OnCreate()中启动动画定时器 / ^5 e! @, H: M2 d2 ^3 l3 l# `3 ^2 _
0 T# D; |' q5 i( A ^
SetTimer(0,40,NULL); , p: @9 |6 q7 V' Q
% p7 G* s; H& G) \
----5.在收到WM_SIZE消息时要重新计算场景尺寸,用OnSize 设置图形显示模式
& r% d& y; S2 w9 [! t3 h8 [. z8 d) Z3 s
----为了使物体能合适的显示,必须要经过投影和确定视口的工作。
' a* `, R0 o& Q T) x' Q) ~
+ A/ C) y- a) s- e3 ivoid CTextView::OnSize(UINT nType, int cx, int cy)
& C# A: D' z8 _% m0 w{
; W3 d. B' P% ] M) hCView::OnSize(nType, cx, cy);
. }' ^) ~5 v$ X% D
' i; n' u/ }% I2 ^// TODO: Add your message handler code here ' ?9 f' k( x5 ~2 t3 \; y# D
//Save the wide and height of the current window Client
7 V$ I" Z- b1 ?3 ?8 WGLsizei nWidth=(GLsizei)cx; / ? d3 b! o4 `! e! R
GLsizei nHeight=(GLsizei)cy; 4 {) z5 a6 q! y$ _5 i
ratio=(double)cx/(double)cy; # i0 W X5 @6 _
6 B! E+ L. @% N1 y6 U) I
//Coupute the aspect ratio 7 {$ C8 g4 Y' E# d/ T1 L6 L
GLdouble dAspect=(GLdouble)nWidth/(GLdouble)nHeight;
: A u% v1 E, L" _" ~
) p$ A; ]/ d j* jglViewport(0,0,nWidth,nHeight); . v: T9 O1 s. b& ~4 y4 f: C5 Z
glMatrixMode(GL_PROJECTION);
( u6 Z( q0 A0 O; K8 H9 XglLoadIdentity();
. v6 a: e6 e, j& _0 z- ?; BgluPerspective ' g$ _" H; W. _# }
(FOV,dAspect,NEARPLANE,FARPLANE);
* c( V. D2 x# H; Z/ E$ ?2 l% n; x2 K% g+ w8 R' q5 X T
glMatrixMode(GL_MODELVIEW); 5 ?) G8 R! p0 G. B
glLoadIdentity();
4 q8 Z5 } f+ A3 Y ]}
+ R* G+ S: [: \- Z$ I0 n8 Y
& @" k2 d, _5 |4 C. w----6.在OnDraw()中简单调用DrawScene()以执行OpenGL函数
9 t+ R6 D" j* G* h3 B8 u3 K
$ o3 z, C j& F b5 gvoid CTextView::OnDraw(CDC* pDC)
9 k" ?7 z1 Y: f2 X9 y0 P{ + F& d( |0 ~6 A0 K# N8 J$ N
CTextDoc* pDoc = GetDocument(); " y* r: f' q2 H* ~0 e6 Z4 G6 I
ASSERT_VALID(pDoc);
/ |% X* f$ g$ I
0 _+ z* \7 f$ [// TODO: add draw code for native data here : b* f4 _5 K5 U, [6 q" N" Y2 }
DrawScene(); * r( B: K5 `( f* a7 r6 Y
//Invalidate();
$ O- \* X, J" t: K# F8 W/ m} ! ^& I5 ^. [( k5 q7 e3 Y+ C
# a) `0 u* W$ U6 w6 s4 O# |: y! X1 ~" E----7.撤销视窗时删除上下文并撤销定时器
y, x" Y7 a2 L8 d, Y1 E; T
# h3 j9 }- e% ^/ _- n; Xvoid CTextView::OnDestroy() 4 h( S6 T( C! b& Y
{
' a9 Y! i6 F5 G& q7 K$ r: rCView::OnDestroy();
6 e2 G- f: `) ~; j& r4 h* n2 Z2 ]6 k3 r# p: e
// TODO: Add your message handler code here
0 F" y7 x1 o) s$ N$ v//This call makes the current RC not current 6 E, M( }9 v* W9 Y7 [
if(wglMakeCurrent(0,0)==FALSE)
U" k$ B) W% `' o2 jMessageBox(“wglMakeCurrent failed."); 4 G" ^2 q7 W$ T& I
/ D: N! Z0 D5 J# _/ h' B8 b//delete the RC
' S2 S' e* U' V: zif(m_hRC && (wglDeleteContext(m_hRC)==FALSE)) ' |9 b) [0 q; N8 H% F7 [# q
MessageBox(“wglDeleteContext fail."); % {( W2 @, Q) I$ f: g0 D# ~
KillTimer(1); ' ~0 G# }* S( H* V5 z
} 4 |3 Z' c4 \- ~5 ^# i+ h
) S; E# e& v9 v" A' s2 q0 e# P! d----8.当40ms定时器时间到时,简单地将整个场景的客户区置无效,使之重画
+ d6 n( ~- g9 Q: U" c
. g$ \* N$ f% uvoid CTextView::OnTimer(UINT nIDEvent) , o) q0 {; j$ a3 A0 b5 g
{ : c) t+ h* E, n* ]& S5 ]/ R2 u
// TODO: Add your message handler code here and/or call default 8 ?7 _. W4 | C) M! R s& _
Invalidate();
0 W3 R, x0 X* Q* G* h: r# O% ICView::OnTimer(nIDEvent); & Q/ V1 x& ~9 h, t/ E
}
7 D6 F% X5 c) }' U& k4 Z
+ b [6 P. C' x! i& w& U" ^* q- N修改界面 8 S9 r! O( q9 n6 M9 I
---- 删除菜单IDR_MAINFRAME中File下除去Exit菜单项外的所有选项,添加“显示GDI文字” “列表制作文字”“列表三维文字”三个菜单项,并给他们分配适当的ID。使用ClassWizard为这三个菜单项在视类中添加命令处理函数和界面更新函数,其中显示GDI文字的对应的函数如下:
8 y- g; C8 L$ C7 cvoid CTextView::OnGdiText() 3 q1 A2 H1 f% F8 k( X' T
{ ; L6 Y& U1 i4 n/ y0 ?
// TODO: Add your command handler code here 8 O7 f2 Q: c+ ]! m
m_iWhichText=0;
1 M5 q2 Z% z3 ~Invalidate(); ; |% F" ^2 a7 s
}
9 `+ N6 X% _$ U( u, Y `7 V( g. c/ X; w. u% m5 h
void CTextView::OnUpdateGdiText(CCmdUI* pCmdUI) ) j& z" x" ]2 V# N! E
{
& y+ d5 y5 s$ |- C- V8 B// TODO: Add your command update UI handler code here
7 @) l/ ?! d% b; q1 H/ dif(m_iWhichText==0) pCmdUI->SetCheck(); 7 M% g) t' l3 L) }0 J6 j, L, j
else pCmdUI->SetCheck(0); , D9 x$ T, G& c7 g
} " [, C7 S. |0 K- h8 T/ s
|! e3 z( W% [; y: @----增加Draw3DText()、DrawListText()和DrawGdiText()三个函数用于三种不同的文本绘制方法。增加DrawScene()函数,它被OnDraw函数调用,用于绘制场景。在DrawScene()函数中,当m_iWhichText为0、1、2时,分别调用 DrawGdiText()、DrawListText()和Draw3DText()显示文本。具体函数实现如下: ! g2 S, q" F2 `( u! i
. ^7 R) \$ E: W. V& M2 O& D$ M
void CTextView::OnDraw(CDC* pDC) 2 g7 D4 L5 U' s7 ]
{ 7 N/ q. O+ C. X9 c6 C6 G" ?
CTextDoc* pDoc = GetDocument(); 0 R9 B4 h- ?. ]& h8 u# s1 V' S- Q
ASSERT_VALID(pDoc); 6 i' F9 W8 E: y6 y" w# H) g A+ Z
& m. U5 a2 O. E// TODO: add draw code for native data here
7 V9 w# O! K2 p& E9 Z7 X: }! CDrawScene(); - C$ Z* u3 s! P4 _( R# y A
//Invalidate();
8 ^0 B; {' F! r& M} 2 a5 j, P/ b0 a" R ]- V
$ A8 Q; P. K, `6 s
void CTextView::DrawScene()
3 D9 n5 k* @( @- G8 R7 k{
+ r P- r' v2 L% v* H3 }3 |glClear
2 y; F. ^. y. X, R+ ^(GL_COLOR_BUFFERBIT|GL_DEPTH_BUFFER_BIT);
: e4 T* I/ `7 _' e' f1 {& B$ x4 i$ a7 B
glPushMatrix();
) k1 c4 T. K; l2 R0 Q( c0 X0 PglTranslatef(0.0f,0.0f,-FARPLANE);
& n/ Q; _, G$ m; _4 i//TextureMap(); 0 N, |4 t e( L7 k( D* x
glPopMatrix(); # }8 c# V& W# Y9 M" t$ y
glPushMatrix(); 7 T' _4 Q# y7 g! M
glTranslatef
% }/ v1 D$ x# p$ x% D5 R$ {8 r(0.0f,0.0f,-(FARPLANE+NEARPLANE)/2); . @: }, C/ ^: \6 p' Y
2 |7 A0 ^8 F: E; ^" Y+ `* u
if(m_iWhichText==1) DrawListText();
v( i8 O& q {! X) F/ t9 t% Sif(m_iWhichText==2) Draw3DText(); ! ?: s! ^2 v# d5 l5 d& Y7 }4 l J
glPopMatrix(); 3 k7 ?7 S% K% W, ]) x D# j
glFinish();
; B& G7 j9 p% y2 ^! ISwapBuffers(wglGetCurrentDC());
3 w* b1 U1 | G$ ?( |. ~7 ]
4 Y. Q) k% N4 m8 bif(m_iWhichText==0) DrawGdiText();
9 l+ @7 {9 e5 V- r9 e5 J l} & K r) P. q3 ]4 h! p* A
# }4 w6 P% g6 V3 e! g8 D! j
GDI 显示文本
/ o7 B# d1 | Y$ H---- 调用wglGetCurrentDC()函数取得当前的设备上下文,使用TextOut函数显示文本,不过要注意在DoubleBuffer模式下,绘制函数要在glFinish()和 SwapBuffers(wglGetCurrentDC())函数之后调用,否则会产生闪烁,在绘制OpenGL结束之前使用GDI函数,要除去闪烁则只能使用SingleBuffer模式,具体函数如下: , j* N1 r# t5 J( k0 d
void CTextView::DrawGdiText()
2 Q8 w; _8 X+ v{
; g: n$ k: K- R3 E: |! E0 w' lHDC hdc=wglGetCurrentDC();
- H) |$ c: k9 Y1 b::SetBkMode( hdc, TRANSPARENT ); - b; d, l9 v8 J$ i2 ~
::SetTextColor( hdc, RGB(250,0,0) );
+ N' ^+ T3 U" G
- ]" N u* E' U3 X7 X; S! BCString sState(“显示GDI文本。"); _- a! U. H( r, J! _( {/ f
::TextOut(hdc,5,5,sState,sState.GetLength()); % o. k+ {! h. q3 I# U3 R' ^
} ' R1 E- c; B J7 Y$ r; ^
- }5 R# K1 m5 `
wglUseFontBitmaps
( g* K2 n5 K+ h- @8 B1 D函数显示文字 $ \9 Z$ M5 G7 x6 s( l5 e
----使用wglUseFontBitmaps()将ASCII字符装入显示列表,然后使用glCallLists()函数利用显示列表序列显示文本。wglUseFontBitmaps有四个参数,分别是当前使用的DC、从第几个ASCII字符起始装入列表、装入列表的ASCII字符数和起始的列表序号。glListBase()指定glCallLists执行的起始列表序列号。glCallLists()含有三个参数:执行列表序列的个数、列表值的类型和所要显示的文本。注意如果所要显示的文本是字符串,它所提供的信息是相对于起始装入ASCII字符的偏移量,因此最终所显示的ASCII字符是从glListBase()所指定的列表起始号在经过glCallLists()中偏移后的列表,因此wglUseFontBitmaps的从第几个ASCII字符起始装入列表参数、glListBase()指定的 glCallLists执行的起始列表序列号和glCallLists()中的所要显示的文本参数都可以影响最终显示结果。由于显示的是ASCII 字符,因此不能显示汉字。glRasterPos3f函数决定在 OpenGL视景体坐标系下的偏移。具体函数实现如下: ( [1 C9 L7 H3 |4 C9 r
void CTextView::DrawListText() 2 Q( ~# i: L# E; y5 Y
{
, X5 @4 ~ ?; {: x W) bwglUseFontBitmaps(wglGetCurrentDC(),0,256,1000); ; {6 W! A- `. C( N8 m# A! t
glListBase(1000); ; l$ V( m; O5 m
glRasterPos3f(-5.0f,0.0f,0.0f); 9 J$ B G- S+ A) w% J( f( R" V
glCallLists(20,GL_UNSIGNED
. a' I0 i, {( V) G) {+ X6 ]( X_BYTE,“Draw with List Text.");
/ S# ^" T7 Q, L. t}
- W: I; ^& Y4 S% C! n
6 u: W6 _+ {+ D8 kwglUseFontOutlines ! n; T& M9 A7 d; p8 f
函数显示三维文字
0 a \4 m* E; w* n----wglUseFontOutlines使得OpenGL可以显示三维文字。它的用法与wglUseFontBitmaps函数大致相同,但是多了内插计算参数、字体深度、显示方式和装载字模的缓存四个参数,且只能显示TrueType字体,显示前应该先选择字体类型。具体函数实现如下:
- k1 B9 V$ ^, n/ N- Dvoid CTextView::Draw3DText()
6 x% d$ T" h+ q6 q% ?{ ! A$ G3 L# o' Q9 G8 h" @ J
GLYPHMETRICSFLOAT agmf[256];
$ t* w2 O- o% L; p0 p2 `8 \// create display lists for glyphs 0 through 255
/ k2 }7 b3 e5 e& t* S: g// with 0.1 extrusion and default deviation. , U; \' g/ b7 d# o) b" J+ g
//The display list numbering starts at 1000
: M6 W, S0 _: [+ _' d! `6 o5 a(it could be any number)
d5 A4 b. R" h7 M+ W3 P- SwglUseFontOutlines(wglGetCurrentDC(), 8 h& ~+ ]% \# Y, t1 _1 z- m- q9 V
0,255,1000,0.3f,0.8f, WGL_FONT_LINES ,agmf); $ T9 p& w& o) L# I0 w; d0 v( Q
8 L1 q f8 V( J! N" M2 p! |
// Set up transformation to draw the string
3 z, e, w \/ R- i7 UglTranslatef(-15.0f,0.0f,0.0f); 4 Y! O; [6 S9 d1 B7 |7 d
glScalef(4.0f, 4.0f, 4.0f);
* j! c) }/ }# \5 R' \2 b5 r// Display a string / U( [8 f- k U: X: j
glListBase(1000); 2 |' S4 u) ~1 z! s
// Indicates the start of display lists for the glyphs + D D) h' r' C* [* l: d
// Draw the characters in a string 4 z# I/ U$ v# \5 k: V
' F& A+ |3 ~; q! r: ~, Y1 K' G2 iglCallLists(26, GL_UNSIGNED_BYTE,
, y% G& E3 D7 X8 i. \! ?) H' y$ L“Draw outline list 3D text.");
/ g& M, p! i5 A. P9 q) E- Z& W} |