Nibiru经测试,用了OpenGL的wglUseFontBitmaps,glCallLists函数来显示文字。 6 o" _3 Z: Z1 l9 X3 s) x8 H
也许是一个汉化的突破口?
! {# Z8 s9 L+ O( e: ?$ g! \* E3 ?' R9 A6 k: B' `
---- 本文详细讨论了在OpenGL中显示文本的几种方法。 ( N; M0 A7 m) r- Z7 G8 t
% H/ q" ]) w p; I: Y; u; c7 z& p
----也许大多数程序员使用OpenGL更多的是将精力集中于动态三维图形应用,因此,OpenGL中的文本显示往往被忽视,使人有不见积薪之感。本文介绍了几种文本显示的方法,希望能对使用OpenGL的编程者有所帮助。
2 |+ p9 F& f$ [" p; @$ [" k) a4 F( \9 w
建立并修改程序
9 g: R3 y* i5 z5 W" t0 O3 p( L----建立一个MFC SDI Windows应用工程Text,除单文档属性外,使用其他的所有默认选择。在菜单Project打开Settings对话框,在Link属性页的 object/library modules编辑框中加入opengl32.lib glu32.lib glaux.lib三个GL库。我们利用这些库函数完成图形编辑工作。
' i' y% D9 ]1 w1 u7 f----为使VC++的AppWizard产生的SDI应用程序能使用 OpenGL绘图,还需要作一些修改,说明如下。
7 e- L% E! V! B; J" x" y. u. B% o8 F4 c; d! v* u1 P+ ~* }
----1.介绍PreCreateWindow函数 - J, B) d: d7 Z+ d4 O3 _/ {
3 |1 A% Y6 B1 V% X; j/ e---- OpenGL窗口必须具有WS_CLIPCHILDREN(创建父窗口使用的Windows风格,用于重绘时剪裁子窗口所覆盖的区域)和WS_CLIPIBLINGS(创建子窗口使用的Windows风格,用于重绘时剪裁其他子窗口所覆盖的区域)两种风格。此外,窗口类属性不能包括CS_PARENTDC风格。具体程序实现如下: ( K$ \8 X6 \. f" B# w
! p3 y- o& ~* d3 J9 e6 v/ c# h
BOOL CTextView::PreCreateWindow
. ]2 c3 c3 V" [' a, e/ Q- \% g(CREATESTRUCT& cs) # `& j$ C, d) q
{ : e% t+ f+ b4 K; L! D/ u, s
// TODO: Modify the Window class or styles here by modifying % T8 k2 k, g2 l$ ^( J
// the CREATESTRUCT cs Z* A' K7 f6 O+ n* C& o3 L( D7 E
K U; U' k: g; W) L% b* H1 }//An OpenGL window must be created with the following flag # H: E1 i: S$ H/ k3 w
// and must not include CS_PARENTIDC for the class style. ( z2 R- f( |* u9 Z
cs.style|=WS_CLIPSIBLINGS|WS_CLIPCHILDREN;
. m5 {7 y% h, g) W+ x/ g$ D; o8 w+ B* z* }/ E
return CView::PreCreateWindow(cs);
' p2 n. q3 S3 A8 w# O; b} ) e: ?/ {- i$ B4 H7 [6 q7 |. F8 q' q
) R% L1 q: |8 F* S: z& |, c) V----2.OnCreate函数中定义像素格式PIXELFORMAT和创建 RC
3 x0 k+ j1 x1 V/ [+ G- r* |9 E: m, S$ s
----要使窗口支持OpenGL绘图,必须对窗口进行初始化。其中包括定义像素格式PIXELFORMAT和创建RC,为OpenGL指定一个合适的像素格式,创建着色上下文并将它和窗口的设备上下文关联起来。着色上下文保存着当前着色环境的信息。可在OnCreate中调用一个自建视口成员函数SetupPixelFormat(),具体函数如下: V1 C/ K/ R0 \: z* G$ D* p
' b4 R; s# U1 m! G* [
BOOL CTextView::SetupPixelFormat() 2 \' b8 \$ B( Z1 _, b1 Q- C4 {6 S. q
{
! ?2 N c( g& H( M. {. H: V//Create a rendering context 4 A- g; }9 h+ v/ ?' f- z
CDC* m_pDC=GetDC(); + D5 Q) `% I* K$ I9 F5 U" v2 I
if(m_pDC==NULL) //failure to get DC
1 C! l( D" S' c{
+ _6 ?1 \3 p2 RMessageBox(“Could't get a valid DC."); 0 K" [; J$ k3 S c- G- q
return FALSE; / _3 L3 ^: c* S) _$ I& W4 a
}
! ^, a( ]2 d% t Z2 ^7 [; w1 `6 x1 U6 C4 n1 p
//Default pixel format is a single-buffered, $ s% o/ w5 T5 f/ o, |
//OpenGL support hardware-accelerated,RGBA mode format ! K& B! P7 u7 b* V; ^0 Y# D- B0 u
PIXELFORMATDESCRIPTOR pfd =
0 H$ k) u% |7 q" k{
, H: n) [1 }) usizeof(PIXELFORMATDESCRIPTOR),//Structure size. 2 L) i( o& p; k. [( y
1, 1 D: f" L5 R* ?' u3 T
// Structure version number.Property flags(特性标志):
7 J3 Z9 h" h7 {- _7 _PFD_DRAW_TO_WINDOW | // support window
( V. J0 h _6 f6 M9 B8 d& @PFD_SUPPORT_OPENGL | // support OpenGL ( a/ y \$ h3 f" T: d& T
PFD_DOUBLEBUFFER,
) Y$ S8 I3 Y+ u' y' [PFD_TYPE_RGBA, // RGBA type
1 r, b$ ]7 o+ J- o, f' A3 D" y24, // 32-bit color.
7 D& R" G/ m1 e' a0, 0, 0, 0, 0, 0, // Not concerned with these:不涉及的属性 2 b9 |7 W( ~2 X! C4 n
0, // No alpha :无alpha缓存 # T7 V- m/ r# K4 D: z, Z
0, // Shift bit ignored:忽略转换位
! N( S. e, R; ?2 p: K* w0, 0, 0, 0, 0,// No accum buffer:没有累积缓存 ; v( N0 p U2 q+ }% m
32, // 32-bit depth buffer. , A, x% F6 |2 r3 j# I
0, // No stencil:无模板缓存 Z h8 q+ q5 A1 _
0, // No auxliliary buffers:无辅助缓存
8 y% b7 I& Z) ^$ GPFD_MAIN_PLANE, // Main layer type.:主层类型 1 n0 W8 K) S, S- Q+ I9 U4 h% m
0, // Reserved.:保留结构数 3 x$ {. n& w5 D
0, 0, 0 // Unsupported.:不支持结构数 0 c' |# b% f7 ^. J
}; + a: E' q2 E0 q, e7 P% z
int nPixelFormat=
# J' f: N/ m, BChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd); 2 l3 t0 r9 C3 Y: H. p
if( nPixelFormat ==0)
8 ]& ^ W- J" r" r% E7 s) o9 g. o{
1 L1 G0 N% L9 c) {. k; [/ Z% h4 OMessageBox(“ChoosePixelFormat failed.");
! p; W' }7 x& W+ j& nreturn FALSE; ! t( I7 Z8 Z; u8 r2 n
} 1 ~* O( l* Y3 n& a- N3 @
2 V; D/ [7 \2 u! R
if(SetPixelFormat(m_pDC->GetSafeHdc(),
; o1 w. V5 O* {: U3 ?nPixelFormat,&pfd)==0) $ x% Z- [1 M+ K) x8 W! G3 W
{ " e2 X/ ?& \' f; ?5 V5 @0 T. o, A
MessageBox(“SetPixelFormat failed."); & `- p. {2 w% v# ^+ ]
return FALSE;
: Z: z( ]3 m8 @3 a+ u: {7 @" h* q} ! g) f1 N# D& g- W. u8 j
6 }6 K4 B- C4 \1 T2 V1 ~
if( (m_hRC=wglCreateContext(m_pDC-> + g7 Y' r7 [: U" U+ D* o1 n
GetSafeHdc())) ==0) 5 O* s) _# A! A) [- f
{ * |- R9 w0 \* i5 E/ j' c
MessageBox(“wglCreateContext failed."); 8 q2 l8 ^( h, i* d8 T7 m# ?
return FALSE; ( O$ |& k" I3 W! s
} $ Q/ \3 ~* ?9 U( T$ e
if( (wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC)) ==0)
8 c/ c5 U" e" q6 m{ 9 j9 l. e$ w4 g1 q, B2 r+ k" b
MessageBox(“wglMakeCurrent failed.");
4 ~9 @. S1 O/ C! d: wreturn FALSE;
) m o2 W! w( P7 O1 D! ^}
3 ^' G; P+ V3 W+ |, i% n
6 k& ], s( X5 |: wif(m_pDC) ReleaseDC(m_pDC);
; X# x5 a* y- W& {( I* r/ I1 ~. Sreturn TRUE;
) \. W! e) K0 x}
% Y5 ]/ F" v$ i1 W& l
1 A3 x( E3 V% D3 `6 o4 w----3.在OnCreate()函数中调用初始化背景函数 InitializeOpenGL() / {4 U- u: \( ], k) }8 O0 p
: B* V1 g3 R% i N$ D. u& j$ H
void CTextView::InitializeOpenGL() 8 d( ^# ?7 L& i0 M$ e" a9 d5 g
{
; g) f' I; c( L$ n7 |5 c+ qglClearColor(0.2f,0.2f,0.2f,0.0f); 7 R$ h) w/ g7 f) G
glClearDepth(1.0); 9 X. t! }1 N" k
glDepthFunc(GL_LESS);
, c3 y/ f- A0 J4 g, ], g4 I3 GglEnable(GL_DEPTH_TEST);
; `% f4 x, E' {# H0 }0 r) ~glShadeModel(GL_SMOOTH);
; e8 D; \: r+ n$ q' o- L! k} & }/ f# B( |' J9 h e
& f$ J3 C- `' {1 \' G: ?& V# y----4.在OnCreate()中启动动画定时器
; i3 M2 X+ V7 n2 {' v7 N9 L' U
5 O5 g m/ h1 n8 j; h/ T: f7 s! [SetTimer(0,40,NULL);
; ?4 b; g: O+ P1 Y' y( f4 y) ]! I5 \& |( @8 z5 ~3 a
----5.在收到WM_SIZE消息时要重新计算场景尺寸,用OnSize 设置图形显示模式
" h* t) \& H8 ?" G$ I( i5 b. ?5 k8 c- V. K- r( p' r
----为了使物体能合适的显示,必须要经过投影和确定视口的工作。 9 Q: r0 ]9 E/ P+ E, G" ~ `2 o9 O+ m
3 g; [9 c- ^8 n5 e4 @, Tvoid CTextView::OnSize(UINT nType, int cx, int cy) ' }. a8 W# p% t5 i6 z
{
, S- _) R! `/ M! {CView::OnSize(nType, cx, cy);
! W. s& U, B, ?1 i: X+ B- a4 [$ y: v
// TODO: Add your message handler code here ! H0 n/ X. w( b5 h' z
//Save the wide and height of the current window Client 4 \- J6 c% Z% B# K+ `
GLsizei nWidth=(GLsizei)cx; 6 |5 `% Y6 U& Z% b' v
GLsizei nHeight=(GLsizei)cy;
8 Y9 J. b# {/ N& O- h# y2 Wratio=(double)cx/(double)cy; # u5 `; }* c' }4 c, \
7 i$ Z7 n" c2 o; \. \( n//Coupute the aspect ratio 5 G0 k0 P& q1 e5 |: T; N
GLdouble dAspect=(GLdouble)nWidth/(GLdouble)nHeight;
2 L% m6 u/ e7 h1 d: r7 A0 C2 J/ g6 g% r
glViewport(0,0,nWidth,nHeight); 0 b: _1 i& G! D4 | z
glMatrixMode(GL_PROJECTION); # r6 S$ R* h7 k* t" {' }/ C
glLoadIdentity();
3 B, M; |9 W3 G* a- ?/ CgluPerspective # }5 S1 h6 e& O! R) B3 R
(FOV,dAspect,NEARPLANE,FARPLANE); ) A8 _+ t, m( A7 a" o5 k
0 e; |! k3 r' {glMatrixMode(GL_MODELVIEW); $ w4 K7 j, D. V1 r. n
glLoadIdentity();
; ] G5 A' I s, a) b4 H4 d- J5 h} & v8 {. T- }9 n5 W& s4 f8 U; w. ~: J
& q8 c. Q5 F0 t* Q! Q----6.在OnDraw()中简单调用DrawScene()以执行OpenGL函数 & O% u3 H- h7 t
2 T% Z) o: {1 M) W2 `
void CTextView::OnDraw(CDC* pDC) 7 q& z4 e. \$ C) e, ~# Z. j
{
! l$ h/ b! C0 u, d0 `6 x! D+ XCTextDoc* pDoc = GetDocument(); 6 @& h' S0 k+ }" ]
ASSERT_VALID(pDoc); 8 O- F; p- X {
& \7 v- Q( r3 s- \4 |// TODO: add draw code for native data here * V2 [/ l9 s- ]0 `( d4 q" Z& b
DrawScene(); * v0 S: K z# ]* v1 L8 f0 ~
//Invalidate();
$ k+ i$ W! L3 y}
+ Z- H! e$ f" r. \8 {7 A: Z+ ^0 k) p7 D8 L; m |( [* E4 [ p* a
----7.撤销视窗时删除上下文并撤销定时器
; G- A, B' I/ h, h O( {8 A9 y" o3 v. j
void CTextView::OnDestroy()
, Y5 R4 M5 {- p# g{
$ @5 H- b& s2 W" Y, hCView::OnDestroy();
; K3 i4 ?3 C4 d
8 a. ^" z. b' C2 ?// TODO: Add your message handler code here
z1 o: R: F1 ^; E//This call makes the current RC not current " J# F( N3 B. v$ {0 i
if(wglMakeCurrent(0,0)==FALSE) ( r: e; m& W7 u! A
MessageBox(“wglMakeCurrent failed."); + M. R2 G* g5 H/ |+ @4 E
1 j3 ?0 g0 h; r. k, t; i
//delete the RC 3 ~4 G9 y% F2 s$ F$ D" C3 Q- a
if(m_hRC && (wglDeleteContext(m_hRC)==FALSE))
7 l; s% L( O' ?$ T1 ~/ g8 RMessageBox(“wglDeleteContext fail.");
* o) g3 h9 b# F% {) _KillTimer(1);
" }6 Y$ U# I! y1 [6 ?, I X/ U}
( E4 {9 x; H8 v2 p* Z' b$ ~( S% H# b3 o& ?9 {* q
----8.当40ms定时器时间到时,简单地将整个场景的客户区置无效,使之重画
& ~0 T+ \, c$ j. K) B
7 m( l- ]( N, U4 O* v% m( I1 hvoid CTextView::OnTimer(UINT nIDEvent)
, y$ ~. \# a' \" }& D5 e{ 1 C9 ~* Y* i+ I- |
// TODO: Add your message handler code here and/or call default
; W; q0 @5 b5 |) |8 Y) eInvalidate();
0 X2 L, e' B; u$ i8 `5 }CView::OnTimer(nIDEvent);
! P/ l6 J" U3 S! `" g} W1 g: P8 g3 F; B
, {4 f) a- L0 e+ ?& q修改界面 - z w8 M( [$ b
---- 删除菜单IDR_MAINFRAME中File下除去Exit菜单项外的所有选项,添加“显示GDI文字” “列表制作文字”“列表三维文字”三个菜单项,并给他们分配适当的ID。使用ClassWizard为这三个菜单项在视类中添加命令处理函数和界面更新函数,其中显示GDI文字的对应的函数如下:
/ |$ g8 h+ r' F& k3 ivoid CTextView::OnGdiText()
; D2 D2 h [4 ~: ^7 c B: p{
& H' ^7 }- {5 @# K// TODO: Add your command handler code here
5 ~' g% D4 b9 t4 C, `/ E: om_iWhichText=0; 6 M' l. K& G9 z: e
Invalidate(); & t- C( ]! m* o- w( R# P) @" U
} ! x- d. B$ _* ?7 C8 v
; R% m& z0 \. L0 S( h
void CTextView::OnUpdateGdiText(CCmdUI* pCmdUI) 3 c4 g) H8 H% K/ G! g7 h% w
{
2 E7 v$ _. K: w0 u' {+ r// TODO: Add your command update UI handler code here [. F3 p9 u+ g6 { ~- y# o j3 v W
if(m_iWhichText==0) pCmdUI->SetCheck();
' Z$ r9 p! o0 K2 P* [2 velse pCmdUI->SetCheck(0);
; ?8 F; I% N" G; G( ~} 7 R; K0 i, t% u+ b8 Z
; a: m% j, s9 l6 O( P3 F% {
----增加Draw3DText()、DrawListText()和DrawGdiText()三个函数用于三种不同的文本绘制方法。增加DrawScene()函数,它被OnDraw函数调用,用于绘制场景。在DrawScene()函数中,当m_iWhichText为0、1、2时,分别调用 DrawGdiText()、DrawListText()和Draw3DText()显示文本。具体函数实现如下:
! O8 d) x" G* ]# C J3 d1 _1 z3 Z$ O$ G& x
void CTextView::OnDraw(CDC* pDC)
- o% `$ d4 `1 b/ v5 O& |{ 1 f. a4 _9 R% ]4 w8 F# C
CTextDoc* pDoc = GetDocument();
( i2 _* R: b2 x9 ^& ]! FASSERT_VALID(pDoc);
& B7 A) k$ m4 _& v8 n
2 Q9 k8 N4 F" [) P- ^// TODO: add draw code for native data here 3 t5 s# w/ V. ? W" v
DrawScene(); ! @3 D5 ]9 Y+ k7 g
//Invalidate();
+ n$ W& o/ a8 T& C} }1 `% S! Q1 t* L- n
S- q( c5 C4 i/ S5 `8 tvoid CTextView::DrawScene()
6 ~4 B# C1 f! N* O{
0 S; z# d: {5 E/ o9 s' X( K: i0 rglClear - L1 Z4 ^% {* k1 b" Y# j
(GL_COLOR_BUFFERBIT|GL_DEPTH_BUFFER_BIT); ' b0 |; F. u5 {- l% E1 E$ X# o
E+ P$ n& H$ G& X0 e4 Y4 s
glPushMatrix(); 4 d& d3 E# Z! q+ `
glTranslatef(0.0f,0.0f,-FARPLANE);
" K4 T6 V: q( f* g: o& r9 J( `5 f/ n//TextureMap(); 4 B3 l A( n+ \( v a: z
glPopMatrix(); ' g% |. z4 V+ P; d* z
glPushMatrix();
" x5 B7 f8 @) zglTranslatef ) ]. Q7 Y0 Y2 o# q( k, W
(0.0f,0.0f,-(FARPLANE+NEARPLANE)/2);
$ a: ?% a' }) G' O: k3 J
' `+ B7 t8 P3 k0 ?( I1 ?if(m_iWhichText==1) DrawListText(); 2 P2 R7 X3 G5 O8 C
if(m_iWhichText==2) Draw3DText(); . ^# h$ o+ P$ s) O; @+ f# g9 T
glPopMatrix(); * _5 M+ Z- D% K) x2 b0 n
glFinish();
$ ~& }4 c9 I+ b' y vSwapBuffers(wglGetCurrentDC()); # ]( S9 A$ k8 } u, M' t/ J6 n
1 p2 O1 V2 E, Dif(m_iWhichText==0) DrawGdiText(); - ]2 T2 w" }, |6 o
} : d( a J: A( m5 k8 M4 E; y6 _8 }
4 \3 u4 d. e& b; h/ j8 w
GDI 显示文本 # I( V' r- X, Z
---- 调用wglGetCurrentDC()函数取得当前的设备上下文,使用TextOut函数显示文本,不过要注意在DoubleBuffer模式下,绘制函数要在glFinish()和 SwapBuffers(wglGetCurrentDC())函数之后调用,否则会产生闪烁,在绘制OpenGL结束之前使用GDI函数,要除去闪烁则只能使用SingleBuffer模式,具体函数如下: 1 }- h# x* N9 w' {8 X- z
void CTextView::DrawGdiText() # @) W4 Y" D% F0 H4 X
{
$ g1 X C: D8 _& w4 h3 U% o: LHDC hdc=wglGetCurrentDC(); 3 z5 R! k2 B8 z. l+ ~' {* g8 T: J4 e
::SetBkMode( hdc, TRANSPARENT );
7 c% |( d0 p, y! X+ c3 A/ T8 `::SetTextColor( hdc, RGB(250,0,0) );
; M/ s! G. [* C4 E7 V/ W& {; l
CString sState(“显示GDI文本。");
5 m D2 C: ^& P6 R% O::TextOut(hdc,5,5,sState,sState.GetLength()); 0 m' v$ b( y& @/ Y2 t# I w
} - ]8 K. |1 j; P9 K1 V
" E4 r% p5 Z; a1 O$ \7 S. ]' E* T3 ~wglUseFontBitmaps - {; R+ T7 |8 ^+ R6 a
函数显示文字 1 T3 f+ N% j( @9 o' V! Y
----使用wglUseFontBitmaps()将ASCII字符装入显示列表,然后使用glCallLists()函数利用显示列表序列显示文本。wglUseFontBitmaps有四个参数,分别是当前使用的DC、从第几个ASCII字符起始装入列表、装入列表的ASCII字符数和起始的列表序号。glListBase()指定glCallLists执行的起始列表序列号。glCallLists()含有三个参数:执行列表序列的个数、列表值的类型和所要显示的文本。注意如果所要显示的文本是字符串,它所提供的信息是相对于起始装入ASCII字符的偏移量,因此最终所显示的ASCII字符是从glListBase()所指定的列表起始号在经过glCallLists()中偏移后的列表,因此wglUseFontBitmaps的从第几个ASCII字符起始装入列表参数、glListBase()指定的 glCallLists执行的起始列表序列号和glCallLists()中的所要显示的文本参数都可以影响最终显示结果。由于显示的是ASCII 字符,因此不能显示汉字。glRasterPos3f函数决定在 OpenGL视景体坐标系下的偏移。具体函数实现如下: 0 S" @& R: B. b/ [1 O& V2 {- J# I
void CTextView::DrawListText()
, Y% s+ J# m2 N. K8 v" z2 n{
( m4 G: D% [8 JwglUseFontBitmaps(wglGetCurrentDC(),0,256,1000);
, G4 U' P! o! t" r2 B R3 J, I' zglListBase(1000);
9 i0 }1 R7 x& H3 }0 R. kglRasterPos3f(-5.0f,0.0f,0.0f);
7 ?& M" c2 u5 }: o; U8 a: mglCallLists(20,GL_UNSIGNED
6 D, l2 f9 M" P2 K$ c# z" p7 @: Z_BYTE,“Draw with List Text.");
9 m$ v& _( I( B0 E5 j8 `: Z} 7 W/ U0 ~ g9 E, L
) J$ E% Z) h* N/ N8 \, L
wglUseFontOutlines % y9 W, J# S2 K
函数显示三维文字
# E, {6 y6 ~2 |% Z7 X, H" i: N9 w----wglUseFontOutlines使得OpenGL可以显示三维文字。它的用法与wglUseFontBitmaps函数大致相同,但是多了内插计算参数、字体深度、显示方式和装载字模的缓存四个参数,且只能显示TrueType字体,显示前应该先选择字体类型。具体函数实现如下:
_ ~$ Z! x5 Bvoid CTextView::Draw3DText() + @0 B5 Y7 e! J; f
{ / q* C m* H4 G: u
GLYPHMETRICSFLOAT agmf[256]; 7 c8 V; ]/ e% b4 O& h( j
// create display lists for glyphs 0 through 255
: |) z6 r3 } S/ d& n/ b; e// with 0.1 extrusion and default deviation. 3 p4 k) _: x3 x; i! \3 v
//The display list numbering starts at 1000 b, y4 R. O; j6 M0 c( T9 _
(it could be any number)
/ o0 g) w) F( M8 [. pwglUseFontOutlines(wglGetCurrentDC(), 9 k6 [' Q2 m) X' g# l
0,255,1000,0.3f,0.8f, WGL_FONT_LINES ,agmf); 7 ~ l* B5 |* k c$ Z9 ]
) k# p( |# z0 x" k// Set up transformation to draw the string
* l7 s C) O( X# {3 [5 Y# }glTranslatef(-15.0f,0.0f,0.0f);
6 B4 {1 [4 d9 T; L8 Y* K" XglScalef(4.0f, 4.0f, 4.0f);
- q# n, N8 M. h4 u// Display a string ( i9 l# j8 r$ D$ u0 ]
glListBase(1000); . Z' d1 p: Y d" ]. H! U
// Indicates the start of display lists for the glyphs
d9 u1 s4 t( |+ M7 D% k// Draw the characters in a string
8 F( _& J% x' B8 j c2 V; j. ?
4 M. O3 H9 ~, F4 [" OglCallLists(26, GL_UNSIGNED_BYTE, ! i# E' ?0 T/ i" S+ c: g' [
“Draw outline list 3D text."); 2 s6 X, @9 c$ k w+ t" s
} |