Nibiru经测试,用了OpenGL的wglUseFontBitmaps,glCallLists函数来显示文字。 1 R9 I8 ~6 _, m# m
也许是一个汉化的突破口? 4 C; ^8 `5 h; m. `3 w! M; y
! |- h0 s$ C' s5 `7 d5 v1 b0 O---- 本文详细讨论了在OpenGL中显示文本的几种方法。 / C @, a4 L8 G$ R# y
$ B1 h1 C: a' V0 c# a3 n* L" r----也许大多数程序员使用OpenGL更多的是将精力集中于动态三维图形应用,因此,OpenGL中的文本显示往往被忽视,使人有不见积薪之感。本文介绍了几种文本显示的方法,希望能对使用OpenGL的编程者有所帮助。 & X8 D9 X/ ~1 i: }/ A J5 u
g4 i/ T! L! b8 O- A8 W! F建立并修改程序 1 y1 G: t$ ~ Q2 _" V3 n
----建立一个MFC SDI Windows应用工程Text,除单文档属性外,使用其他的所有默认选择。在菜单Project打开Settings对话框,在Link属性页的 object/library modules编辑框中加入opengl32.lib glu32.lib glaux.lib三个GL库。我们利用这些库函数完成图形编辑工作。 & J- R" e2 M3 G8 a1 s! t
----为使VC++的AppWizard产生的SDI应用程序能使用 OpenGL绘图,还需要作一些修改,说明如下。 - d! x" { v) @" s
- F ?7 H/ I2 y ]; K
----1.介绍PreCreateWindow函数 6 c8 f4 W3 t, V7 l# m8 s. }
! x5 \7 l1 \- h5 H8 E
---- OpenGL窗口必须具有WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时剪裁子窗口所覆盖的区域)和WS_CLIPIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)两种风格。此外,窗口类属性不能包括CS_PARENTDC风格。具体程序实现如下: 1 k0 B" X7 b: Z8 ]' d
2 A" s8 q# z1 J: i, kBOOL CTextView::PreCreateWindow
8 I- b! a7 Y/ w# Z. G6 X: D(CREATESTRUCT& cs) k3 [1 k3 x2 q: @
{ S& A' q4 s. {
// TODO: Modify the Window class or styles here by modifying 4 B3 H+ Q* }; o: A1 h% Z
// the CREATESTRUCT cs ) g! i' u. u2 r$ F% d# U8 D. [
# a6 j8 C# h3 _! w//An OpenGL window must be created with the following flag ( }4 N+ S% M: p, y0 D
// and must not include CS_PARENTIDC for the class style. 1 R# `, @# d. s& c
cs.style|=WS_CLIPSIBLINGS|WS_CLIPCHILDREN;
- R9 \9 m/ D. O0 N- @ O, s) ]" \# g8 P
return CView::PreCreateWindow(cs);
7 M0 |1 u6 s. b" D8 h, X} 0 [$ i3 q' ?6 |* \, W/ b, L" T% h
/ ^! d7 X& k. |- v/ m----2.OnCreate函数中定义像素格式PIXELFORMAT和创建 RC
1 ]; T! m. w) g1 U2 @. S1 }6 U; w7 U! b9 g* @7 F
----要使窗口支持OpenGL绘图,必须对窗口进行初始化。其中包括定义像素格式PIXELFORMAT和创建RC,为OpenGL指定一个合适的像素格式,创建着色上下文并将它和窗口的设备上下文关联起来。着色上下文保存着当前着色环境的信息。可在OnCreate中调用一个自建视口成员函数SetupPixelFormat(),具体函数如下:
4 G7 V1 |6 \: k+ ?" R) c+ H& P3 S; G* h. e: W
BOOL CTextView::SetupPixelFormat()
: o7 u9 d/ T, i3 q7 r6 @{
0 t8 T) r% \( c( f: U//Create a rendering context - P, s5 n4 z2 ]) P2 q
CDC* m_pDC=GetDC();
# o. A. s* ~* G' O) L6 rif(m_pDC==NULL) //failure to get DC
/ y9 J# O0 E5 a4 U{ ' v1 s% t' J( x- ^ K
MessageBox(“Could't get a valid DC."); " O F1 p$ R" ^3 x4 ]& Z
return FALSE; - O7 Y* L8 t0 l: s7 X- N
}
* X) O: ?6 u* O
6 d* Y1 f8 q9 v& c0 q0 j3 h; V) [//Default pixel format is a single-buffered,
9 d) ~& p0 {4 w, R$ S7 t//OpenGL support hardware-accelerated,RGBA mode format 1 j3 C* t2 u5 m) H+ l- H( R
PIXELFORMATDESCRIPTOR pfd = ! C& i+ r/ S& q# a& B% V7 i9 I4 Z
{ $ t* {5 J i; Y3 X7 k1 y0 f
sizeof(PIXELFORMATDESCRIPTOR),//Structure size.
4 G/ P" ]/ }/ }, y' v o+ ~8 o0 C1,
# m1 F9 ^0 }. o3 U- ]6 N// Structure version number.Property flags(特性标志):
7 g" q4 t4 y# JPFD_DRAW_TO_WINDOW | // support window ( ?/ h; ]" h! [5 I
PFD_SUPPORT_OPENGL | // support OpenGL 1 |6 C3 r! ]! Q |9 p
PFD_DOUBLEBUFFER,
: {, }5 k& N8 |0 s6 S! A1 w5 ]+ NPFD_TYPE_RGBA, // RGBA type ; N' H3 _0 P8 O
24, // 32-bit color.
) a' s* _/ u, v' b# H, d0, 0, 0, 0, 0, 0, // Not concerned with these:不涉及的属性 5 [0 ]% E4 K' ]. M) s M" c3 i1 n
0, // No alpha :无alpha缓存
2 ]& u. J, }( r. J: q- I0, // Shift bit ignored:忽略转换位
% X0 ]& A; n* k7 Y0, 0, 0, 0, 0,// No accum buffer:没有累积缓存
1 M& H# O) A2 e2 g0 A! w32, // 32-bit depth buffer.
, D4 x' D& Z" n- m0, // No stencil:无模板缓存
/ l7 Q7 J8 |/ p! @0, // No auxliliary buffers:无辅助缓存
7 z, A# C% f& j: uPFD_MAIN_PLANE, // Main layer type.:主层类型
- b4 ]5 m) H( D, n1 S; d0, // Reserved.:保留结构数 $ S& c0 R5 J( Y% }8 q
0, 0, 0 // Unsupported.:不支持结构数
2 ~# ?" i) ?) v9 q3 G4 B" M};
' f. g5 g" j X% B) u9 V! V7 b2 [int nPixelFormat=
A7 H; r6 |' u4 H2 bChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd);
5 P1 c% t8 T0 V- Eif( nPixelFormat ==0)
! `/ P5 R. y' {6 i$ m% T2 f{
- z+ l ^! Z P0 H b/ S, G' E' EMessageBox(“ChoosePixelFormat failed."); + @% j* [% W! Z
return FALSE;
" p" C/ ]6 \" D1 e* d}
% o* z" @9 H+ y6 e' W g
1 h s- J1 R7 [4 ~8 Bif(SetPixelFormat(m_pDC->GetSafeHdc(), , e7 a" i3 K+ E/ H+ L1 w! d
nPixelFormat,&pfd)==0) ! X% \: B* z0 [$ ]+ W
{
) ^' \2 c0 q/ X3 H+ WMessageBox(“SetPixelFormat failed."); 0 U. z9 F+ f; D5 Z9 u) k9 k
return FALSE;
5 m8 J8 t9 {2 H$ g% Y6 l} 1 K2 B7 X \4 y5 _$ N4 Y
5 \& P) Q+ ?) }7 |
if( (m_hRC=wglCreateContext(m_pDC-> 5 q2 B6 B7 O) h
GetSafeHdc())) ==0)
/ j" z- I+ S9 E6 E{
5 V3 g$ W9 \: @) |7 B, h* uMessageBox(“wglCreateContext failed.");
9 F% \1 k8 X. Z M$ Creturn FALSE;
* D- \, Z8 ~9 S* a) `1 m0 t6 ?}
( j* C9 h# R/ r3 ^1 Iif( (wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC)) ==0)
) T6 T" H) [0 J& A3 k{
5 j- _0 S% }1 J. u% } CMessageBox(“wglMakeCurrent failed.");
+ B5 N9 U2 S: jreturn FALSE; # T& b* K/ j* z* ~
} , y- ^$ s8 ?. x" Y3 K" a
6 j) A0 W7 R; t, U
if(m_pDC) ReleaseDC(m_pDC);
3 U5 p$ z6 C2 o- s: ?return TRUE; 6 b4 D0 [. u1 g' m; B
} 8 } V- |6 f0 u4 C# q
- z L! \' X3 f
----3.在OnCreate()函数中调用初始化背景函数 InitializeOpenGL() ( v9 M {9 l5 E* ?+ ?
0 L8 X: L \! `! E; bvoid CTextView::InitializeOpenGL() $ `( k6 m( ~4 W. V( ~; y4 z- k4 w! I
{
* N2 ~& G5 W+ YglClearColor(0.2f,0.2f,0.2f,0.0f); 9 h" K. ?3 W1 s5 ?7 u: u
glClearDepth(1.0);
- {, f; x7 g" R3 d! k: u9 W+ bglDepthFunc(GL_LESS);
, ?8 J% W, {( p9 W+ n: Z" _7 iglEnable(GL_DEPTH_TEST); , `! r" W! B. Q* q6 f- a# ^. @' y
glShadeModel(GL_SMOOTH);
1 v/ k7 I. d/ f; O}
* H2 l4 e2 Y( a5 ]' y
2 |6 h) I: b, q$ s6 q4 E" \----4.在OnCreate()中启动动画定时器
# p Q8 R" }1 m/ ?) S5 f$ F0 y; L a& {, b9 r1 ^. v" g
SetTimer(0,40,NULL); ; O, ~( L# g( Q. ^
) o% ?5 Z4 z0 \9 G) e----5.在收到WM_SIZE消息时要重新计算场景尺寸,用OnSize 设置图形显示模式 * G* \5 H. G% [
% x( V0 R r# u+ w2 g----为了使物体能合适的显示,必须要经过投影和确定视口的工作。
4 T0 t$ o9 A- D- a+ }- `
: l" v9 O0 l; z9 z* C$ c: vvoid CTextView::OnSize(UINT nType, int cx, int cy)
( Y0 b' Y* U3 L: k/ f$ B3 w0 h& Q0 H{ ) U3 c5 F4 p1 t# h
CView::OnSize(nType, cx, cy); 4 V8 ~4 u+ K/ w2 i
6 ^. y. f! K* [8 j4 Z" n: a
// TODO: Add your message handler code here - Q5 W! e0 ]4 D+ z
//Save the wide and height of the current window Client 5 r4 F3 v) w* B; W3 q% t
GLsizei nWidth=(GLsizei)cx;
& x% {% p: o2 K9 ?5 |9 bGLsizei nHeight=(GLsizei)cy;
0 n: D/ H" q- ]# Wratio=(double)cx/(double)cy;
: S4 r/ r4 E# x
$ Q# k% K) a# P- _9 O//Coupute the aspect ratio , A! |1 m! g2 v" H7 F
GLdouble dAspect=(GLdouble)nWidth/(GLdouble)nHeight; 3 L4 {+ u: I w. i, Y
v, s7 ~' N$ e- G9 @
glViewport(0,0,nWidth,nHeight); 4 p( t6 u7 ~2 U$ l+ G
glMatrixMode(GL_PROJECTION);
) ~3 I: W/ v& e! P4 i6 `. GglLoadIdentity();
8 G+ i/ A: F0 }) LgluPerspective
3 H/ t: z# a6 j* g(FOV,dAspect,NEARPLANE,FARPLANE);
- W! ]3 O: U" Z8 g$ }8 A* h
0 c7 s& Q4 l& \: P' o! @glMatrixMode(GL_MODELVIEW);
7 T- U& q) h4 |+ jglLoadIdentity();
4 }* _- G& T+ ?}
/ J( Z" c; F% O, o: ]5 a
$ ]2 i$ l0 A' w' ~* C0 `0 j1 H. X* n; v----6.在OnDraw()中简单调用DrawScene()以执行OpenGL函数 & r; {. H. U! v4 G/ M; T5 U5 p% w7 O
. N8 p. x* l# C% d5 ]
void CTextView::OnDraw(CDC* pDC) . W- q7 t' f; W% M
{ , l! r8 E6 [3 r0 Q$ L
CTextDoc* pDoc = GetDocument(); % g. H. |4 K0 {3 _" Q5 E
ASSERT_VALID(pDoc); - m% w; X* M$ N( p! } O2 }
1 B0 h4 i5 K1 `! y& [
// TODO: add draw code for native data here & A2 z- P) L& M, ?7 ~6 [8 i+ h
DrawScene();
5 w0 @* H f: G6 P! i7 x1 a//Invalidate();
! r6 H. f, J; l1 V. A: o$ ^( A7 e} - c8 B7 w* c0 n1 G7 J
6 T1 w6 S# u& X) u" ?/ f% _
----7.撤销视窗时删除上下文并撤销定时器 7 k+ f; j, _% z! k- p7 b, u
9 E8 x( ^5 e, @
void CTextView::OnDestroy() , f: i }1 e6 Y0 Q# u0 G
{ ) @; J. v+ a5 b1 t! S- @
CView::OnDestroy(); k$ W+ h2 ~1 m' j( i/ ^0 V/ G
" K5 H- v* s O
// TODO: Add your message handler code here 4 c8 S# I$ N5 s' n+ B3 o! ?% G% ~
//This call makes the current RC not current + i( i8 K! y, S) W$ R$ h1 a
if(wglMakeCurrent(0,0)==FALSE) ; l" B5 r4 _9 B7 L3 g' x
MessageBox(“wglMakeCurrent failed."); , V7 {4 w6 E& ~1 O
, M+ g4 E/ f1 E5 P0 v6 h
//delete the RC - ] {7 \$ y; e/ j
if(m_hRC && (wglDeleteContext(m_hRC)==FALSE))
$ l( P F# @+ z5 CMessageBox(“wglDeleteContext fail.");
; K$ o( u) L; S* [KillTimer(1);
9 z0 [+ B( }( s' i' p% a r8 [} 1 c6 Z. H) T6 p1 T: I
; y% c0 C* m, h3 V: n+ V----8.当40ms定时器时间到时,简单地将整个场景的客户区置无效,使之重画
, j1 b/ J% \' ]2 b$ ]6 D3 ?* c6 l9 ?( T2 I0 z$ [3 c- K
void CTextView::OnTimer(UINT nIDEvent)
$ G" f5 i$ \$ l* I S; x{ 7 J+ `% w. l# i
// TODO: Add your message handler code here and/or call default
5 L. S! E- |, q$ ^" ~* AInvalidate(); 8 B9 a; Y+ S0 ?! a# }& I* y# E
CView::OnTimer(nIDEvent); 7 p2 l+ o$ L2 [, [# b$ n
} 7 P: p5 e# Q7 R! @9 j
s+ d+ h+ X# }4 X修改界面
: }& b, n% B$ d- z8 x9 x---- 删除菜单IDR_MAINFRAME中File下除去Exit菜单项外的所有选项,添加“显示GDI文字” “列表制作文字”“列表三维文字”三个菜单项,并给他们分配适当的ID。使用ClassWizard为这三个菜单项在视类中添加命令处理函数和界面更新函数,其中显示GDI文字的对应的函数如下: . ]5 u$ x4 ~$ b- K: y$ q h% p
void CTextView::OnGdiText() + t# e* E9 c) m$ E8 _6 F! M+ I4 T
{ 4 `' I% p# ?& l( K. ^
// TODO: Add your command handler code here 8 O& I& h: |1 d; @# J
m_iWhichText=0; - J _0 R; d* @
Invalidate(); % }! a" D6 e) S+ y0 q: i
} $ g" o" `) L2 T" ]7 n' }9 c
4 k5 g& O+ L( s8 r" M
void CTextView::OnUpdateGdiText(CCmdUI* pCmdUI)
1 \0 u, g; k u6 M{
4 \0 u$ E% \9 k- }- d// TODO: Add your command update UI handler code here
: Y# A7 D8 a/ R* r9 `# gif(m_iWhichText==0) pCmdUI->SetCheck(); / I0 p1 G+ l# q) @% `! i% |! @
else pCmdUI->SetCheck(0);
2 [/ G; \+ Y; K% g5 H) }' E" y}
0 V4 T* _% G! D& Y! F: Y) K" ~; F7 z
----增加Draw3DText()、DrawListText()和DrawGdiText()三个函数用于三种不同的文本绘制方法。增加DrawScene()函数,它被OnDraw函数调用,用于绘制场景。在DrawScene()函数中,当m_iWhichText为0、1、2时,分别调用 DrawGdiText()、DrawListText()和Draw3DText()显示文本。具体函数实现如下:
" T4 E4 _) Q2 s( d# J0 D
. o# E+ k0 G/ W6 h$ v- nvoid CTextView::OnDraw(CDC* pDC)
: [+ {/ w& Z7 d* [5 r- C$ S5 Q{
' @1 z4 h* }6 b P0 FCTextDoc* pDoc = GetDocument();
/ B" w- }6 h, {. J; RASSERT_VALID(pDoc);
; l8 ?1 b' Y4 ^7 _
6 g( X! z& N1 H3 x5 P _// TODO: add draw code for native data here
) h2 O' i0 A8 q( }/ _' n* ADrawScene(); 2 `6 N8 l' e# K0 z8 P
//Invalidate(); / b" A8 U8 I3 u+ C X
} % F" I( q; Z2 M) z
7 W$ U" w! o8 p9 ]void CTextView::DrawScene() / D( y6 \% {# G; u. ^) g% Y- U
{ 9 W0 L/ f: J$ D8 w; J& b* k( h
glClear - o. G* ]/ p3 J- {" m, n
(GL_COLOR_BUFFERBIT|GL_DEPTH_BUFFER_BIT); : C8 F* W3 L. e& Y! ?% W! h/ l2 Y
# j+ U) m! i" d! j
glPushMatrix(); # O& z! `+ P& Q+ k! R5 {
glTranslatef(0.0f,0.0f,-FARPLANE);
7 ?+ D, [1 Y5 E( z1 e* ^3 d//TextureMap(); % D$ U/ E0 s: E+ T. x
glPopMatrix(); # }& y0 Q b' y2 Y7 d2 h% f% d0 e# M
glPushMatrix();
( w4 t- M: P6 ?) }5 Q0 aglTranslatef 6 z% H( ~. Q+ i8 _% {$ m
(0.0f,0.0f,-(FARPLANE+NEARPLANE)/2); 5 p3 g9 i/ s8 _( c- C0 g d
3 g& k- e$ r! U9 ^& m: u
if(m_iWhichText==1) DrawListText(); / G2 I- Q" A; S4 R2 d
if(m_iWhichText==2) Draw3DText(); / ^ O; u; Y$ J7 p
glPopMatrix();
: C4 c5 K+ k9 \* Q9 p- T7 WglFinish(); ( f0 @! r$ t+ U8 ~1 H# E- m
SwapBuffers(wglGetCurrentDC()); + B9 ]( l% H) Q6 K
- w6 ]# _) q6 U" ] k
if(m_iWhichText==0) DrawGdiText();
# g; z/ j! k K4 U2 H9 v}
8 P8 o! n/ h- N1 C
+ m8 U# x& i$ Q$ _0 W8 CGDI 显示文本
, \( n2 @+ Y6 A4 T, \; n---- 调用wglGetCurrentDC()函数取得当前的设备上下文,使用TextOut函数显示文本,不过要注意在DoubleBuffer模式下,绘制函数要在glFinish()和 SwapBuffers(wglGetCurrentDC())函数之后调用,否则会产生闪烁,在绘制OpenGL结束之前使用GDI函数,要除去闪烁则只能使用SingleBuffer模式,具体函数如下: 7 V+ C/ a" q- Q3 D
void CTextView::DrawGdiText() 1 t& M3 D3 ? R+ Q" s/ B
{ 0 B8 E. @: H" _9 k& k$ ~
HDC hdc=wglGetCurrentDC(); * x7 i$ H+ q# N1 g6 o6 J
::SetBkMode( hdc, TRANSPARENT ); & z, n6 H/ d7 M" m5 T
::SetTextColor( hdc, RGB(250,0,0) ); + j0 [, m# p5 N1 ?1 Q9 z8 q
/ C- _, S5 Y; n2 x% j$ ^CString sState(“显示GDI文本。"); ; T% W, l! `# A5 d# \
::TextOut(hdc,5,5,sState,sState.GetLength()); # \7 R5 z5 k7 @* Q# i: Z5 S
}
: P3 {/ K! k9 t& @7 Z x6 C
# r, H) f3 W7 T, v3 }8 R5 f: gwglUseFontBitmaps
0 O" ~( h8 e5 j' ^9 O2 i' |% f函数显示文字
1 F$ }; W* s6 n$ U, \6 m9 U; \----使用wglUseFontBitmaps()将ASCII字符装入显示列表,然后使用glCallLists()函数利用显示列表序列显示文本。wglUseFontBitmaps有四个参数,分别是当前使用的DC、从第几个ASCII字符起始装入列表、装入列表的ASCII字符数和起始的列表序号。glListBase()指定glCallLists执行的起始列表序列号。glCallLists()含有三个参数:执行列表序列的个数、列表值的类型和所要显示的文本。注意如果所要显示的文本是字符串,它所提供的信息是相对于起始装入ASCII字符的偏移量,因此最终所显示的ASCII字符是从glListBase()所指定的列表起始号在经过glCallLists()中偏移后的列表,因此wglUseFontBitmaps的从第几个ASCII字符起始装入列表参数、glListBase()指定的 glCallLists执行的起始列表序列号和glCallLists()中的所要显示的文本参数都可以影响最终显示结果。由于显示的是ASCII 字符,因此不能显示汉字。glRasterPos3f函数决定在 OpenGL视景体坐标系下的偏移。具体函数实现如下:
( B' B/ B2 l! f0 B; B, p, i: G# H+ Kvoid CTextView::DrawListText() 3 m- U# [* m o5 j" J* ?: O" t
{
, r; N/ b E* ^4 H& l twglUseFontBitmaps(wglGetCurrentDC(),0,256,1000);
% H; _& o. I: v) ^- v$ }; R+ YglListBase(1000); + Q# B( R6 s5 J7 X v6 U0 L
glRasterPos3f(-5.0f,0.0f,0.0f); 3 g1 q: s/ \2 m% @
glCallLists(20,GL_UNSIGNED
+ d& a* n9 l+ p/ A/ ^' R! _' }% n_BYTE,“Draw with List Text.");
8 L9 s( }2 x4 T, p0 D5 L% ~0 b}
+ A! X# {1 d6 A: f% n9 Z
[, y) I/ O; `& Y8 TwglUseFontOutlines 9 [2 G% S2 R$ z. V8 @# }# e: z
函数显示三维文字
8 I1 }$ C1 _, W% ?----wglUseFontOutlines使得OpenGL可以显示三维文字。它的用法与wglUseFontBitmaps函数大致相同,但是多了内插计算参数、字体深度、显示方式和装载字模的缓存四个参数,且只能显示TrueType字体,显示前应该先选择字体类型。具体函数实现如下:
- b9 J% H7 y Wvoid CTextView::Draw3DText() # T# p9 r( A# Z7 O; o3 d1 h V
{
& s+ Y' U) Q# B8 t# {* \+ ~GLYPHMETRICSFLOAT agmf[256]; # ^( p9 U6 I' z3 v$ q
// create display lists for glyphs 0 through 255 . C( n) M3 F! W0 U; z7 i
// with 0.1 extrusion and default deviation.
% M3 ^' s3 w% F1 X$ Q2 a6 l//The display list numbering starts at 1000
% j. X& c( |( O* d) c0 `(it could be any number)
, V2 K, V. r6 M. e. twglUseFontOutlines(wglGetCurrentDC(),
* J7 g& z8 p3 w8 c0 f- z0,255,1000,0.3f,0.8f, WGL_FONT_LINES ,agmf); 8 X) \; o6 m% b6 _& _9 M
$ ^) ? x x* c1 D1 Z9 x2 ]
// Set up transformation to draw the string + z" i% v0 H: b9 O8 J7 R1 F
glTranslatef(-15.0f,0.0f,0.0f);
. q" ^( m5 b' Y0 k3 v" `. DglScalef(4.0f, 4.0f, 4.0f);
0 z$ s9 O4 {. U- E2 [: Q) N; R7 E3 [7 f// Display a string " h2 r3 N( p. L2 T( ?; D$ H
glListBase(1000);
' c% [. H1 S1 v8 O9 f// Indicates the start of display lists for the glyphs
0 ?2 S4 j) d) ^& b7 _// Draw the characters in a string
# J, y# c( t2 U5 @7 C4 s* F. u' c h
/ [! ?0 H, B% a$ M4 H \: g5 bglCallLists(26, GL_UNSIGNED_BYTE,
8 @2 Y2 K- _; t7 q“Draw outline list 3D text.");
# ]- ~+ f$ G% Z9 l2 g' S} |