root/OpenSceneGraph/trunk/src/osgText/Text.cpp @ 12068

Revision 12068, 68.8 kB (checked in by robert, 3 years ago)

Unified more of the 2D and 3D text setup, fixed bugs in Text3D setup
which address the problems of black 3D text and the kerning causing problems with font positioning.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14
15#include <osgText/Text>
16
17#include <osg/Math>
18#include <osg/GL>
19#include <osg/Notify>
20#include <osg/PolygonOffset>
21#include <osg/TexEnv>
22
23#include <osgUtil/CullVisitor>
24
25#include <osgDB/ReadFile>
26
27using namespace osg;
28using namespace osgText;
29
30//#define TREES_CODE_FOR_MAKING_SPACES_EDITABLE
31
32Text::Text():
33    _enableDepthWrites(true),
34    _backdropType(NONE),
35    _backdropImplementation(DELAYED_DEPTH_WRITES),
36    _backdropHorizontalOffset(0.07f),
37    _backdropVerticalOffset(0.07f),
38    _backdropColor(0.0f, 0.0f, 0.0f, 1.0f),
39    _colorGradientMode(SOLID),
40    _colorGradientTopLeft(1.0f, 0.0f, 0.0f, 1.0f),
41    _colorGradientBottomLeft(0.0f, 1.0f, 0.0f, 1.0f),
42    _colorGradientBottomRight(0.0f, 0.0f, 1.0f, 1.0f),
43    _colorGradientTopRight(1.0f, 1.0f, 1.0f, 1.0f)
44{}
45
46Text::Text(const Text& text,const osg::CopyOp& copyop):
47    osgText::TextBase(text,copyop),
48    _enableDepthWrites(text._enableDepthWrites),
49    _backdropType(text._backdropType),
50    _backdropImplementation(text._backdropImplementation),
51    _backdropHorizontalOffset(text._backdropHorizontalOffset),
52    _backdropVerticalOffset(text._backdropVerticalOffset),
53    _backdropColor(text._backdropColor),
54    _colorGradientMode(text._colorGradientMode),
55    _colorGradientTopLeft(text._colorGradientTopLeft),
56    _colorGradientBottomLeft(text._colorGradientBottomLeft),
57    _colorGradientBottomRight(text._colorGradientBottomRight),
58    _colorGradientTopRight(text._colorGradientTopRight)
59{
60    computeGlyphRepresentation();
61}
62
63Text::~Text()
64{
65}
66
67void Text::setFont(osg::ref_ptr<Font> font)
68{
69    if (_font==font) return;
70
71    osg::StateSet* previousFontStateSet = _font.valid() ? _font->getStateSet() : Font::getDefaultFont()->getStateSet();
72    osg::StateSet* newFontStateSet = font.valid() ? font->getStateSet() : Font::getDefaultFont()->getStateSet();
73
74    if (getStateSet() == previousFontStateSet)
75    {
76        setStateSet( newFontStateSet );
77    }
78
79    TextBase::setFont(font);
80}
81
82
83Font* Text::getActiveFont()
84{
85    return _font.valid() ? _font.get() : Font::getDefaultFont().get();
86}
87
88const Font* Text::getActiveFont() const
89{
90    return _font.valid() ? _font.get() : Font::getDefaultFont().get();
91}
92
93String::iterator Text::computeLastCharacterOnLine(osg::Vec2& cursor, String::iterator first,String::iterator last)
94{
95    Font* activefont = getActiveFont();
96    if (!activefont) return last;
97
98    float hr = _characterHeight;
99    float wr = hr/getCharacterAspectRatio();
100
101    bool kerning = true;
102    unsigned int previous_charcode = 0;
103
104    String::iterator lastChar = first;
105
106    for(bool outOfSpace=false;lastChar!=last;++lastChar)
107    {
108        unsigned int charcode = *lastChar;
109       
110        if (charcode=='\n')
111        {
112            return lastChar;
113        }
114
115        Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
116        if (glyph)
117        {
118
119           float width = (float)(glyph->getWidth()) * wr;
120
121            if (_layout==RIGHT_TO_LEFT)
122            {
123                cursor.x() -= glyph->getHorizontalAdvance() * wr;
124            }
125
126            // adjust cursor position w.r.t any kerning.
127            if (kerning && previous_charcode)
128            {
129                switch(_layout)
130                {
131                  case LEFT_TO_RIGHT:
132                  {
133                    osg::Vec2 delta(activefont->getKerning(previous_charcode,charcode,_kerningType));
134                    cursor.x() += delta.x() * wr;
135                    cursor.y() += delta.y() * hr;
136                    break;
137                  }
138                  case RIGHT_TO_LEFT:
139                  {
140                    osg::Vec2 delta(activefont->getKerning(charcode,previous_charcode,_kerningType));
141                    cursor.x() -= delta.x() * wr;
142                    cursor.y() -= delta.y() * hr;
143                    break;
144                  }
145                  case VERTICAL:
146                    break; // no kerning when vertical.
147                }            // check to see if we are still within line if not move to next line.
148            }
149
150            switch(_layout)
151            {
152              case LEFT_TO_RIGHT:
153              {
154                if (_maximumWidth>0.0f && cursor.x()+width>_maximumWidth) outOfSpace=true;
155                if(_maximumHeight>0.0f && cursor.y()<-_maximumHeight) outOfSpace=true;
156                break;
157              }
158              case RIGHT_TO_LEFT:
159              {
160                if (_maximumWidth>0.0f && cursor.x()<-_maximumWidth) outOfSpace=true;
161                if(_maximumHeight>0.0f && cursor.y()<-_maximumHeight) outOfSpace=true;
162                break;
163              }
164              case VERTICAL:
165                if (_maximumHeight>0.0f && cursor.y()<-_maximumHeight) outOfSpace=true;
166                break;
167            }
168           
169            // => word boundary detection & wrapping
170            if (outOfSpace) break;
171
172            // move the cursor onto the next character.
173            switch(_layout)
174            {
175              case LEFT_TO_RIGHT: cursor.x() += glyph->getHorizontalAdvance() * wr; break;
176              case VERTICAL:      cursor.y() -= glyph->getVerticalAdvance() *hr; break;
177              case RIGHT_TO_LEFT: break; // nop.
178            }
179
180            previous_charcode = charcode;
181
182        }
183
184    }
185
186    // word boundary detection & wrapping
187    if (lastChar!=last)
188    {
189        String::iterator lastValidChar = lastChar;
190          String::iterator prevChar;
191        while (lastValidChar != first){
192            prevChar = lastValidChar - 1;
193
194            // last char is after a hyphen
195                if(*lastValidChar == '-')
196                return lastValidChar + 1;
197
198            // last char is start of whitespace
199            if((*lastValidChar == ' ' || *lastValidChar == '\n') && (*prevChar != ' ' && *prevChar != '\n'))
200                return lastValidChar;
201
202            // Subtract off glyphs from the cursor position (to correctly center text)
203                if(*prevChar != '-')
204            {
205                Glyph* glyph = activefont->getGlyph(_fontSize, *prevChar);
206                if (glyph)
207                {
208                    switch(_layout)
209                    {
210                    case LEFT_TO_RIGHT: cursor.x() -= glyph->getHorizontalAdvance() * wr; break;
211                    case VERTICAL:      cursor.y() += glyph->getVerticalAdvance() * hr; break;
212                    case RIGHT_TO_LEFT: break; // nop.
213                    }
214                }
215            }
216
217            lastValidChar = prevChar;
218          }
219    }
220
221    return lastChar;
222}
223
224
225void Text::computeGlyphRepresentation()
226{
227    Font* activefont = getActiveFont();
228    if (!activefont) return;
229   
230    _textureGlyphQuadMap.clear();
231    _lineCount = 0;
232   
233    if (_text.empty())
234    {
235        _textBB.set(0,0,0,0,0,0);//no size text
236        TextBase::computePositions(); //to reset the origin
237        return;
238    }
239   
240    //OpenThreads::ScopedLock<Font::FontMutex> lock(*(activefont->getSerializeFontCallsMutex()));
241
242    // initialize bounding box, it will be expanded during glyph position calculation
243    _textBB.init();
244
245    osg::Vec2 startOfLine_coords(0.0f,0.0f);
246    osg::Vec2 cursor(startOfLine_coords);
247    osg::Vec2 local(0.0f,0.0f);
248   
249    unsigned int previous_charcode = 0;
250    unsigned int linelength = 0;
251    bool horizontal = _layout!=VERTICAL;
252    bool kerning = true;
253   
254    unsigned int lineNumber = 0;
255
256    float hr = _characterHeight;
257    float wr = hr/getCharacterAspectRatio();
258
259    for(String::iterator itr=_text.begin();
260        itr!=_text.end();
261        )
262    {
263        // record the start of the current line
264            String::iterator startOfLine_itr = itr;
265
266            // find the end of the current line.
267            osg::Vec2 endOfLine_coords(cursor);
268            String::iterator endOfLine_itr = computeLastCharacterOnLine(endOfLine_coords, itr,_text.end());
269
270            linelength = endOfLine_itr - startOfLine_itr;
271
272            // Set line position to correct alignment.
273            switch(_layout)
274            {
275            case LEFT_TO_RIGHT:
276            {
277            switch(_alignment)
278            {
279              // nothing to be done for these
280              //case LEFT_TOP:
281              //case LEFT_CENTER:
282              //case LEFT_BOTTOM:
283              //case LEFT_BASE_LINE:
284              //case LEFT_BOTTOM_BASE_LINE:
285              //  break;
286              case CENTER_TOP:
287              case CENTER_CENTER:
288              case CENTER_BOTTOM:
289              case CENTER_BASE_LINE:
290              case CENTER_BOTTOM_BASE_LINE:
291                cursor.x() = (cursor.x() - endOfLine_coords.x()) * 0.5f;
292                break;
293              case RIGHT_TOP:
294              case RIGHT_CENTER:
295              case RIGHT_BOTTOM:
296              case RIGHT_BASE_LINE:
297              case RIGHT_BOTTOM_BASE_LINE:
298                cursor.x() = cursor.x() - endOfLine_coords.x();
299                break;
300              default:
301                break;
302              }
303            break;
304            }
305            case RIGHT_TO_LEFT:
306            {
307            switch(_alignment)
308            {
309              case LEFT_TOP:
310              case LEFT_CENTER:
311              case LEFT_BOTTOM:
312              case LEFT_BASE_LINE:
313              case LEFT_BOTTOM_BASE_LINE:
314                cursor.x() = 2*cursor.x() - endOfLine_coords.x();
315                break;
316              case CENTER_TOP:
317              case CENTER_CENTER:
318              case CENTER_BOTTOM:
319              case CENTER_BASE_LINE:
320              case CENTER_BOTTOM_BASE_LINE:
321                cursor.x() = cursor.x() + (cursor.x() - endOfLine_coords.x()) * 0.5f;
322                break;
323              // nothing to be done for these
324              //case RIGHT_TOP:
325              //case RIGHT_CENTER:
326              //case RIGHT_BOTTOM:
327              //case RIGHT_BASE_LINE:
328              //case RIGHT_BOTTOM_BASE_LINE:
329              //  break;
330              default:
331                break;
332            }
333            break;
334            }
335            case VERTICAL:
336            {
337            switch(_alignment)
338            {
339              // TODO: current behaviour top baselines lined up in both cases - need to implement
340              //       top of characters alignment - Question is this necessary?
341              // ... otherwise, nothing to be done for these 6 cases
342              //case LEFT_TOP:
343              //case CENTER_TOP:
344              //case RIGHT_TOP:
345              //  break;
346              //case LEFT_BASE_LINE:
347              //case CENTER_BASE_LINE:
348              //case RIGHT_BASE_LINE:
349              //  break;
350              case LEFT_CENTER:
351              case CENTER_CENTER:
352              case RIGHT_CENTER:
353                cursor.y() = cursor.y() + (cursor.y() - endOfLine_coords.y()) * 0.5f;
354                break;
355              case LEFT_BOTTOM_BASE_LINE:
356              case CENTER_BOTTOM_BASE_LINE:
357              case RIGHT_BOTTOM_BASE_LINE:
358                cursor.y() = cursor.y() - (linelength * _characterHeight);
359                break;
360              case LEFT_BOTTOM:
361              case CENTER_BOTTOM:
362              case RIGHT_BOTTOM:
363                cursor.y() = 2*cursor.y() - endOfLine_coords.y();
364                break;
365              default:
366                break;
367            }
368            break;
369          }
370        }
371
372        if (itr!=endOfLine_itr)
373        {
374
375            for(;itr!=endOfLine_itr;++itr)
376            {
377                unsigned int charcode = *itr;
378
379                Glyph* glyph = activefont->getGlyph(_fontSize, charcode);
380                if (glyph)
381                {
382                    float width = (float)(glyph->getWidth()) * wr;
383                    float height = (float)(glyph->getHeight()) * hr;
384
385                    if (_layout==RIGHT_TO_LEFT)
386                    {
387                        cursor.x() -= glyph->getHorizontalAdvance() * wr;
388                    }
389
390                    // adjust cursor position w.r.t any kerning.
391                    if (kerning && previous_charcode)
392                    {
393                        switch(_layout)
394                        {
395                          case LEFT_TO_RIGHT:
396                          {
397                            osg::Vec2 delta(activefont->getKerning(previous_charcode,charcode,_kerningType));
398                            cursor.x() += delta.x() * wr;
399                            cursor.y() += delta.y() * hr;
400                            break;
401                          }
402                          case RIGHT_TO_LEFT:
403                          {
404                            osg::Vec2 delta(activefont->getKerning(charcode,previous_charcode,_kerningType));
405                            cursor.x() -= delta.x() * wr;
406                            cursor.y() -= delta.y() * hr;
407                            break;
408                          }
409                          case VERTICAL:
410                            break; // no kerning when vertical.
411                        }
412                    }
413
414                    local = cursor;
415                    osg::Vec2 bearing(horizontal?glyph->getHorizontalBearing():glyph->getVerticalBearing());
416                    local.x() += bearing.x() * wr;
417                    local.y() += bearing.y() * hr;
418
419                    GlyphQuads& glyphquad = _textureGlyphQuadMap[glyph->getTexture()];
420
421                    glyphquad._glyphs.push_back(glyph);
422                    glyphquad._lineNumbers.push_back(lineNumber);
423
424                    // Adjust coordinates and texture coordinates to avoid
425                    // clipping the edges of antialiased characters.
426                    osg::Vec2 mintc = glyph->getMinTexCoord();
427                    osg::Vec2 maxtc = glyph->getMaxTexCoord();
428                    osg::Vec2 vDiff = maxtc - mintc;
429
430                    float fHorizTCMargin = 1.0f / glyph->getTexture()->getTextureWidth();
431                    float fVertTCMargin = 1.0f / glyph->getTexture()->getTextureHeight();
432                    float fHorizQuadMargin = vDiff.x() == 0.0f ? 0.0f : width * fHorizTCMargin / vDiff.x();
433                    float fVertQuadMargin = vDiff.y() == 0.0f ? 0.0f : height * fVertTCMargin / vDiff.y();
434
435                    mintc.x() -= fHorizTCMargin;
436                    mintc.y() -= fVertTCMargin;
437                    maxtc.x() += fHorizTCMargin;
438                    maxtc.y() += fVertTCMargin;
439
440                    // set up the coords of the quad
441                    osg::Vec2 upLeft = local+osg::Vec2(0.0f-fHorizQuadMargin,height+fVertQuadMargin);
442                    osg::Vec2 lowLeft = local+osg::Vec2(0.0f-fHorizQuadMargin,0.0f-fVertQuadMargin);
443                    osg::Vec2 lowRight = local+osg::Vec2(width+fHorizQuadMargin,0.0f-fVertQuadMargin);
444                    osg::Vec2 upRight = local+osg::Vec2(width+fHorizQuadMargin,height+fVertQuadMargin);
445                    glyphquad._coords.push_back(upLeft);
446                    glyphquad._coords.push_back(lowLeft);
447                    glyphquad._coords.push_back(lowRight);
448                    glyphquad._coords.push_back(upRight);
449
450                    // set up the tex coords of the quad
451                    glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),maxtc.y()));
452                    glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),mintc.y()));
453                    glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),mintc.y()));
454                    glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),maxtc.y()));
455
456                    // move the cursor onto the next character.
457                    // also expand bounding box
458                    switch(_layout)
459                    {
460                      case LEFT_TO_RIGHT:
461                          cursor.x() += glyph->getHorizontalAdvance() * wr;
462                          _textBB.expandBy(osg::Vec3(lowLeft.x(), lowLeft.y(), 0.0f)); //lower left corner
463                          _textBB.expandBy(osg::Vec3(upRight.x(), upRight.y(), 0.0f)); //upper right corner
464                          break;
465                      case VERTICAL:
466                          cursor.y() -= glyph->getVerticalAdvance() * hr;
467                          _textBB.expandBy(osg::Vec3(upLeft.x(),upLeft.y(),0.0f)); //upper left corner
468                          _textBB.expandBy(osg::Vec3(lowRight.x(),lowRight.y(),0.0f)); //lower right corner
469                          break;
470                      case RIGHT_TO_LEFT:
471                          _textBB.expandBy(osg::Vec3(lowRight.x(),lowRight.y(),0.0f)); //lower right corner
472                          _textBB.expandBy(osg::Vec3(upLeft.x(),upLeft.y(),0.0f)); //upper left corner
473                          break;
474                    }
475                    previous_charcode = charcode;
476
477                }
478            }
479
480            // skip over spaces and return.
481            while (itr != _text.end() && *itr==' ') ++itr;
482            if (itr != _text.end() && *itr=='\n') ++itr;
483        }
484        else
485        {
486            ++itr;
487        }
488                               
489               
490        // move to new line.
491        switch(_layout)
492        {
493          case LEFT_TO_RIGHT:
494          {
495            startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
496            cursor = startOfLine_coords;
497            previous_charcode = 0;
498            _lineCount++;
499            break;
500          }
501          case RIGHT_TO_LEFT:
502          {
503            startOfLine_coords.y() -= _characterHeight * (1.0 + _lineSpacing);
504            cursor = startOfLine_coords;
505            previous_charcode = 0;
506            _lineCount++;
507            break;
508          }
509          case VERTICAL:
510          {
511            startOfLine_coords.x() += _characterHeight/getCharacterAspectRatio() * (1.0 + _lineSpacing);
512            cursor = startOfLine_coords;
513            previous_charcode = 0;
514            // because _lineCount is the max vertical no. of characters....
515            _lineCount = (_lineCount >linelength)?_lineCount:linelength;
516          }
517          break;
518        }
519       
520        ++lineNumber;
521
522    }
523   
524    TextBase::computePositions();
525    computeBackdropBoundingBox();
526    computeBoundingBoxMargin();
527    computeColorGradients();
528}
529
530// Returns false if there are no glyphs and the width/height values are invalid.
531// Also sets avg_width and avg_height to 0.0f if the value is invalid.
532// This method is used several times in a loop for the same object which will produce the same values.
533// Further optimization may try saving these values instead of recomputing them.
534bool Text::computeAverageGlyphWidthAndHeight(float& avg_width, float& avg_height) const
535{
536    float width = 0.0f;
537    float height = 0.0f;
538    float running_width = 0.0f;
539    float running_height = 0.0f;
540    avg_width = 0.0f;
541    avg_height = 0.0f;
542    int counter = 0;
543    unsigned int i;
544    bool is_valid_size = true;
545    // This section is going to try to compute the average width and height
546    // for a character among the text. The reason I shift by an
547    // average amount per-character instead of shifting each character
548    // by its per-instance amount is because it may look strange to see
549    // the individual backdrop text letters not space themselves the same
550    // way the foreground text does. Using one value gives uniformity.
551    // Note: This loop is repeated for each context. I think it may produce
552    // the same values regardless of context. This code be optimized by moving
553    // this loop outside the loop.
554    for(TextureGlyphQuadMap::const_iterator const_titr=_textureGlyphQuadMap.begin();
555        const_titr!=_textureGlyphQuadMap.end();
556        ++const_titr)
557    {
558        const GlyphQuads& glyphquad = const_titr->second;
559        const GlyphQuads::Coords2& coords2 = glyphquad._coords;
560        for(i = 0; i < coords2.size(); i+=4)
561        {
562            width = coords2[i+2].x() - coords2[i].x();
563            height = coords2[i].y() - coords2[i+1].y();
564
565            running_width += width;
566            running_height += height;
567            counter++;
568        }
569    }
570    if(0 == counter)
571    {
572        is_valid_size = false;
573    }
574    else
575    {
576        avg_width = running_width/counter;
577        avg_height = running_height/counter;
578    }
579    return is_valid_size;
580}
581
582
583void Text::computePositions(unsigned int contextID) const
584{
585    switch(_alignment)
586    {
587    case LEFT_TOP:      _offset.set(_textBB.xMin(),_textBB.yMax(),_textBB.zMin()); break;
588    case LEFT_CENTER:   _offset.set(_textBB.xMin(),(_textBB.yMax()+_textBB.yMin())*0.5f,_textBB.zMin()); break;
589    case LEFT_BOTTOM:   _offset.set(_textBB.xMin(),_textBB.yMin(),_textBB.zMin()); break;
590
591    case CENTER_TOP:    _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,_textBB.yMax(),_textBB.zMin()); break;
592    case CENTER_CENTER: _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,(_textBB.yMax()+_textBB.yMin())*0.5f,_textBB.zMin()); break;
593    case CENTER_BOTTOM: _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,_textBB.yMin(),_textBB.zMin()); break;
594
595    case RIGHT_TOP:     _offset.set(_textBB.xMax(),_textBB.yMax(),_textBB.zMin()); break;
596    case RIGHT_CENTER:  _offset.set(_textBB.xMax(),(_textBB.yMax()+_textBB.yMin())*0.5f,_textBB.zMin()); break;
597    case RIGHT_BOTTOM:  _offset.set(_textBB.xMax(),_textBB.yMin(),_textBB.zMin()); break;
598
599    case LEFT_BASE_LINE:  _offset.set(0.0f,0.0f,0.0f); break;
600    case CENTER_BASE_LINE:  _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,0.0f,0.0f); break;
601    case RIGHT_BASE_LINE:  _offset.set(_textBB.xMax(),0.0f,0.0f); break;
602   
603    case LEFT_BOTTOM_BASE_LINE:  _offset.set(0.0f,-_characterHeight*(_lineCount-1),0.0f); break;
604    case CENTER_BOTTOM_BASE_LINE:  _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,-_characterHeight*(_lineCount-1),0.0f); break;
605    case RIGHT_BOTTOM_BASE_LINE:  _offset.set(_textBB.xMax(),-_characterHeight*(_lineCount-1),0.0f); break;
606    }
607   
608    AutoTransformCache& atc = _autoTransformCache[contextID];
609    osg::Matrix& matrix = atc._matrix;
610
611    if (_characterSizeMode!=OBJECT_COORDS || _autoRotateToScreen)
612    {
613
614        matrix.makeTranslate(-_offset);
615
616        osg::Matrix rotate_matrix;
617        if (_autoRotateToScreen)
618        {
619            osg::Vec3d trans(atc._modelview.getTrans());
620            atc._modelview.setTrans(0.0f,0.0f,0.0f);
621
622            rotate_matrix.invert(atc._modelview);
623
624            atc._modelview.setTrans(trans);
625        }
626
627        matrix.postMultRotate(_rotation);
628
629        if (_characterSizeMode!=OBJECT_COORDS)
630        {
631
632            osg::Matrix M(rotate_matrix);
633            M.postMultTranslate(_position);
634            M.postMult(atc._modelview);
635            osg::Matrix& P = atc._projection;
636           
637            // compute the pixel size vector.
638                       
639            // pre adjust P00,P20,P23,P33 by multiplying them by the viewport window matrix.
640            // here we do it in short hand with the knowledge of how the window matrix is formed
641            // note P23,P33 are multiplied by an implicit 1 which would come from the window matrix.
642            // Robert Osfield, June 2002.
643
644            // scaling for horizontal pixels
645            float P00 = P(0,0)*atc._width*0.5f;
646            float P20_00 = P(2,0)*atc._width*0.5f + P(2,3)*atc._width*0.5f;
647            osg::Vec3 scale_00(M(0,0)*P00 + M(0,2)*P20_00,
648                               M(1,0)*P00 + M(1,2)*P20_00,
649                               M(2,0)*P00 + M(2,2)*P20_00);
650
651            // scaling for vertical pixels
652            float P10 = P(1,1)*atc._height*0.5f;
653            float P20_10 = P(2,1)*atc._height*0.5f + P(2,3)*atc._height*0.5f;
654            osg::Vec3 scale_10(M(0,1)*P10 + M(0,2)*P20_10,
655                               M(1,1)*P10 + M(1,2)*P20_10,
656                               M(2,1)*P10 + M(2,2)*P20_10);
657
658            float P23 = P(2,3);
659            float P33 = P(3,3);
660
661            float pixelSizeVector_w = M(3,2)*P23 + M(3,3)*P33;
662
663            float pixelSizeVert=(_characterHeight*sqrtf(scale_10.length2()))/(pixelSizeVector_w*0.701f);
664            float pixelSizeHori=(_characterHeight/getCharacterAspectRatio()*sqrtf(scale_00.length2()))/(pixelSizeVector_w*0.701f);
665
666            // avoid nasty math by preventing a divide by zero
667            if (pixelSizeVert == 0.0f)
668               pixelSizeVert= 1.0f;
669            if (pixelSizeHori == 0.0f)
670               pixelSizeHori= 1.0f;
671
672            if (_characterSizeMode==SCREEN_COORDS)
673            {
674                float scale_font_vert=_characterHeight/pixelSizeVert;
675                float scale_font_hori=_characterHeight/getCharacterAspectRatio()/pixelSizeHori;
676
677                if (P10<0)
678                   scale_font_vert=-scale_font_vert;
679                matrix.postMultScale(osg::Vec3f(scale_font_hori, scale_font_vert,1.0f));
680            }
681            else if (pixelSizeVert>getFontHeight())
682            {
683                float scale_font = getFontHeight()/pixelSizeVert;
684                matrix.postMultScale(osg::Vec3f(scale_font, scale_font,1.0f));
685            }
686
687        }
688
689        if (_autoRotateToScreen)
690        {
691            matrix.postMult(rotate_matrix);
692        }
693
694        matrix.postMultTranslate(_position);
695    }
696    else if (!_rotation.zeroRotation())
697    {
698        matrix.makeRotate(_rotation);
699        matrix.preMultTranslate(-_offset);
700        matrix.postMultTranslate(_position);
701    }
702    else
703    {
704        matrix.makeTranslate(_position-_offset);
705    }
706
707    // now apply matrix to the glyphs.
708    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
709        titr!=_textureGlyphQuadMap.end();
710        ++titr)
711    {
712        GlyphQuads& glyphquad = titr->second;
713        GlyphQuads::Coords2& coords2 = glyphquad._coords;
714        GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
715       
716        unsigned int numCoords = coords2.size();
717        if (numCoords!=transformedCoords.size())
718        {
719            transformedCoords.resize(numCoords);
720        }
721       
722        for(unsigned int i=0;i<numCoords;++i)
723        {
724            transformedCoords[i] = osg::Vec3(coords2[i].x(),coords2[i].y(),0.0f)*matrix;
725        }
726    }
727
728    computeBackdropPositions(contextID);
729
730    _normal = osg::Matrix::transform3x3(osg::Vec3(0.0f,0.0f,1.0f),matrix);
731    _normal.normalize();
732
733    const_cast<Text*>(this)->dirtyBound();   
734}
735
736// Presumes the atc matrix is already up-to-date
737void Text::computeBackdropPositions(unsigned int contextID) const
738{
739    if(_backdropType == NONE)
740    {
741        return;
742    }
743
744    float avg_width = 0.0f;
745    float avg_height = 0.0f;
746    unsigned int i;
747    bool is_valid_size;
748   
749    AutoTransformCache& atc = _autoTransformCache[contextID];
750    osg::Matrix& matrix = atc._matrix;
751
752    // FIXME: OPTIMIZE: This function produces the same value regardless of contextID.
753    // Since we tend to loop over contextID, we should cache this value some how
754    // instead of recomputing it each time.
755    is_valid_size = computeAverageGlyphWidthAndHeight(avg_width, avg_height);
756   
757    // now apply matrix to the glyphs.
758    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
759        titr!=_textureGlyphQuadMap.end();
760        ++titr)
761    {
762        GlyphQuads& glyphquad = titr->second;
763        GlyphQuads::Coords2& coords2 = glyphquad._coords;
764
765        unsigned int backdrop_index;
766        unsigned int max_backdrop_index;
767        if(_backdropType == OUTLINE)
768        {
769            // For outline, we want to draw the in every direction
770            backdrop_index = 0;
771            max_backdrop_index = 8;
772        }
773        else
774        {
775            // Yes, this may seem a little strange,
776            // but since the code is using references,
777            // I would have to duplicate the following code twice
778            // for each part of the if/else because I can't
779            // declare a reference without setting it immediately
780            // and it wouldn't survive the scope.
781            // So it happens that the _backdropType value matches
782            // the index in the array I want to store the coordinates
783            // in. So I'll just setup the for-loop so it only does
784            // the one direction I'm interested in.
785            backdrop_index = _backdropType;
786            max_backdrop_index = _backdropType+1;
787        }
788        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
789        {
790            GlyphQuads::Coords3& transformedCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
791            unsigned int numCoords = coords2.size();
792            if (numCoords!=transformedCoords.size())
793            {
794                transformedCoords.resize(numCoords);
795            }
796
797            for(i=0;i<numCoords;++i)
798            {
799                float horizontal_shift_direction;
800                float vertical_shift_direction;
801                switch(backdrop_index)
802                {
803                    case DROP_SHADOW_BOTTOM_RIGHT:
804                        {
805                            horizontal_shift_direction = 1.0f;
806                            vertical_shift_direction = -1.0f;
807                            break;
808                        }
809                    case DROP_SHADOW_CENTER_RIGHT:
810                        {
811                            horizontal_shift_direction = 1.0f;
812                            vertical_shift_direction = 0.0f;
813                            break;
814                        }
815                    case DROP_SHADOW_TOP_RIGHT:
816                        {
817                            horizontal_shift_direction = 1.0f;
818                            vertical_shift_direction = 1.0f;
819                            break;
820                        }
821                    case DROP_SHADOW_BOTTOM_CENTER:
822                        {
823                            horizontal_shift_direction = 0.0f;
824                            vertical_shift_direction = -1.0f;
825                            break;
826                        }
827                    case DROP_SHADOW_TOP_CENTER:
828                        {
829                            horizontal_shift_direction = 0.0f;
830                            vertical_shift_direction = 1.0f;
831                            break;
832                        }                               
833                    case DROP_SHADOW_BOTTOM_LEFT:
834                        {
835                            horizontal_shift_direction = -1.0f;
836                            vertical_shift_direction = -1.0f;
837                            break;
838                        }
839                    case DROP_SHADOW_CENTER_LEFT:
840                        {
841                            horizontal_shift_direction = -1.0f;
842                            vertical_shift_direction = 0.0f;
843                            break;
844                        }
845                    case DROP_SHADOW_TOP_LEFT:
846                        {
847                            horizontal_shift_direction = -1.0f;
848                            vertical_shift_direction = 1.0f;
849                            break;
850                        }
851                    default: // error
852                        {
853                            horizontal_shift_direction = 1.0f;
854                            vertical_shift_direction = -1.0f;
855                        }
856                }
857                transformedCoords[i] = osg::Vec3(horizontal_shift_direction * _backdropHorizontalOffset * avg_width+coords2[i].x(),vertical_shift_direction * _backdropVerticalOffset * avg_height+coords2[i].y(),0.0f)*matrix;
858            }
859        }
860    }
861}
862
863// This method adjusts the bounding box to account for the expanded area caused by the backdrop.
864// This assumes that the bounding box has already been computed for the text without the backdrop.
865void Text::computeBackdropBoundingBox() const
866{
867    if(_backdropType == NONE)
868    {
869        return;
870    }
871
872    float avg_width = 0.0f;
873    float avg_height = 0.0f;
874    bool is_valid_size;
875   
876    // FIXME: OPTIMIZE: It is possible that this value has already been computed before
877    // from previous calls to this function. This might be worth optimizing.
878    is_valid_size = computeAverageGlyphWidthAndHeight(avg_width, avg_height);
879
880    // Finally, we have one more issue to deal with.
881    // Now that the text takes more space, we need
882    // to adjust the size of the bounding box.
883    if((!_textBB.valid() || !is_valid_size))
884    {
885        return;
886    }
887   
888    // Finally, we have one more issue to deal with.
889    // Now that the text takes more space, we need
890    // to adjust the size of the bounding box.
891    switch(_backdropType)
892    {
893        case DROP_SHADOW_BOTTOM_RIGHT:
894            {
895                _textBB.set(
896                    _textBB.xMin(),
897                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
898                    _textBB.zMin(),
899                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
900                    _textBB.yMax(),
901                    _textBB.zMax()
902                );
903                break;
904            }
905        case DROP_SHADOW_CENTER_RIGHT:
906            {
907                _textBB.set(
908                    _textBB.xMin(),
909                    _textBB.yMin(),
910                    _textBB.zMin(),
911                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
912                    _textBB.yMax(),
913                    _textBB.zMax()
914                );
915                break;
916            }
917        case DROP_SHADOW_TOP_RIGHT:
918            {
919                _textBB.set(
920                    _textBB.xMin(),
921                    _textBB.yMin(),
922                    _textBB.zMin(),
923                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
924                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
925                    _textBB.zMax()
926                );
927                break;
928            }
929        case DROP_SHADOW_BOTTOM_CENTER:
930            {
931                _textBB.set(
932                    _textBB.xMin(),
933                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
934                    _textBB.zMin(),
935                    _textBB.xMax(),
936                    _textBB.yMax(),
937                    _textBB.zMax()
938                );
939                break;
940            }
941        case DROP_SHADOW_TOP_CENTER:
942            {
943                _textBB.set(
944                    _textBB.xMin(),
945                    _textBB.yMin(),
946                    _textBB.zMin(),
947                    _textBB.xMax(),
948                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
949                    _textBB.zMax()
950                );
951                break;
952            }                               
953        case DROP_SHADOW_BOTTOM_LEFT:
954            {
955                _textBB.set(
956                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
957                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
958                    _textBB.zMin(),
959                    _textBB.xMax(),
960                    _textBB.yMax(),
961                    _textBB.zMax()
962                );
963                break;
964            }
965        case DROP_SHADOW_CENTER_LEFT:
966            {
967                _textBB.set(
968                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
969                    _textBB.yMin(),
970                    _textBB.zMin(),
971                    _textBB.xMax(),
972                    _textBB.yMax(),
973                    _textBB.zMax()
974                );            break;
975            }
976        case DROP_SHADOW_TOP_LEFT:
977            {
978                _textBB.set(
979                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
980                    _textBB.yMin(),
981                    _textBB.zMin(),
982                    _textBB.xMax(),
983                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
984                    _textBB.zMax()
985                );
986                break;
987            }
988        case OUTLINE:
989            {
990                _textBB.set(
991                    _textBB.xMin() - avg_width * _backdropHorizontalOffset,
992                    _textBB.yMin() - avg_height * _backdropVerticalOffset,
993                    _textBB.zMin(),
994                    _textBB.xMax() + avg_width * _backdropHorizontalOffset,
995                    _textBB.yMax() + avg_height * _backdropVerticalOffset,
996                    _textBB.zMax()
997                );
998                break;
999            }
1000        default: // error
1001            {
1002                break;
1003            }
1004    }
1005}
1006
1007// This method expands the bounding box to a settable margin when a bounding box drawing mode is active.
1008void Text::computeBoundingBoxMargin() const
1009{
1010    if(_drawMode & (BOUNDINGBOX | FILLEDBOUNDINGBOX)){
1011        _textBB.set(
1012            _textBB.xMin() - _textBBMargin,
1013            _textBB.yMin() - _textBBMargin,
1014            _textBB.zMin(),
1015            _textBB.xMax() + _textBBMargin,
1016            _textBB.yMax() + _textBBMargin,
1017            _textBB.zMax()
1018        );
1019    }
1020}
1021
1022void Text::computeColorGradients() const
1023{
1024    switch(_colorGradientMode)
1025    {
1026        case SOLID:
1027            return;
1028            break;
1029        case PER_CHARACTER:
1030            computeColorGradientsPerCharacter();
1031            break;
1032        case OVERALL:
1033            computeColorGradientsOverall();
1034            break;
1035        default:
1036            break;
1037    }
1038}
1039
1040void Text::computeColorGradientsOverall() const
1041{
1042
1043    float min_x = FLT_MAX;
1044    float min_y = FLT_MAX;
1045    float max_x = FLT_MIN;
1046    float max_y = FLT_MIN;
1047
1048    unsigned int i;
1049
1050    for(TextureGlyphQuadMap::const_iterator const_titr=_textureGlyphQuadMap.begin();
1051        const_titr!=_textureGlyphQuadMap.end();
1052        ++const_titr)
1053    {
1054        const GlyphQuads& glyphquad = const_titr->second;
1055        const GlyphQuads::Coords2& coords2 = glyphquad._coords;
1056
1057        for(i=0;i<coords2.size();++i)
1058        { 
1059            // Min and Max are needed for color gradients
1060            if(coords2[i].x() > max_x)
1061            {
1062                max_x = coords2[i].x();
1063            }
1064            if(coords2[i].x() < min_x)
1065            {
1066                min_x = coords2[i].x();
1067            }
1068            if(coords2[i].y() > max_y)
1069            {
1070                max_y = coords2[i].y();
1071            }
1072            if(coords2[i].y() < min_y)
1073            {
1074                min_y = coords2[i].y();
1075            }       
1076
1077        }
1078    }
1079
1080    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1081        titr!=_textureGlyphQuadMap.end();
1082        ++titr)
1083    {
1084        GlyphQuads& glyphquad = titr->second;
1085        GlyphQuads::Coords2& coords2 = glyphquad._coords;
1086        GlyphQuads::ColorCoords& colorCoords = glyphquad._colorCoords;
1087
1088        unsigned int numCoords = coords2.size();
1089        if (numCoords!=colorCoords.size())
1090        {
1091            colorCoords.resize(numCoords);
1092        }
1093
1094        for(i=0;i<numCoords;++i)
1095        {
1096            float red = bilinearInterpolate(
1097                min_x,
1098                max_x,
1099                min_y,
1100                max_y,
1101                coords2[i].x(),
1102                coords2[i].y(),
1103                _colorGradientBottomLeft[0],
1104                _colorGradientTopLeft[0],
1105                _colorGradientBottomRight[0],
1106                _colorGradientTopRight[0]
1107            );
1108
1109            float green = bilinearInterpolate(
1110                min_x,
1111                max_x,
1112                min_y,
1113                max_y,
1114                coords2[i].x(),
1115                coords2[i].y(),
1116                _colorGradientBottomLeft[1],
1117                _colorGradientTopLeft[1],
1118                _colorGradientBottomRight[1],
1119                _colorGradientTopRight[1]
1120            );
1121
1122            float blue = bilinearInterpolate(
1123                min_x,
1124                max_x,
1125                min_y,
1126                max_y,
1127                coords2[i].x(),
1128                coords2[i].y(),
1129                _colorGradientBottomLeft[2],
1130                _colorGradientTopLeft[2],
1131                _colorGradientBottomRight[2],
1132                _colorGradientTopRight[2]
1133            );
1134            // Alpha does not convert to HSV           
1135            float alpha = bilinearInterpolate(
1136                min_x,
1137                max_x,
1138                min_y,
1139                max_y,
1140                coords2[i].x(),
1141                coords2[i].y(),
1142                _colorGradientBottomLeft[3],
1143                _colorGradientTopLeft[3],
1144                _colorGradientBottomRight[3],
1145                _colorGradientTopRight[3]
1146            );                                   
1147
1148            colorCoords[i] = osg::Vec4(red,green,blue,alpha);
1149        }
1150    }
1151}
1152
1153void Text::computeColorGradientsPerCharacter() const
1154{
1155    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1156        titr!=_textureGlyphQuadMap.end();
1157        ++titr)
1158    {
1159        GlyphQuads& glyphquad = titr->second;
1160        GlyphQuads::Coords2& coords2 = glyphquad._coords;
1161        GlyphQuads::ColorCoords& colorCoords = glyphquad._colorCoords;
1162
1163        unsigned int numCoords = coords2.size();
1164        if (numCoords!=colorCoords.size())
1165        {
1166            colorCoords.resize(numCoords);
1167        }
1168
1169        for(unsigned int i=0;i<numCoords;++i)
1170        {
1171            switch(i%4)
1172            {
1173                case 0: // top-left
1174                    {
1175                        colorCoords[i] = _colorGradientTopLeft;
1176                        break;
1177                    }
1178                case 1: // bottom-left
1179                    {
1180                        colorCoords[i] = _colorGradientBottomLeft;
1181                        break;
1182                    }
1183                case 2: // bottom-right
1184                    {
1185                        colorCoords[i] = _colorGradientBottomRight;
1186                        break;
1187                    }
1188                case 3: // top-right
1189                    {
1190                        colorCoords[i] = _colorGradientTopRight;
1191                        break;
1192                    }
1193                default: // error
1194                    {
1195                        colorCoords[i] = osg::Vec4(0.0f,0.0f,0.0f,1.0f);
1196                    }
1197            }
1198        }
1199    }
1200}
1201
1202void Text::drawImplementation(osg::RenderInfo& renderInfo) const
1203{
1204    drawImplementation(*renderInfo.getState(), osg::Vec4(1.0f,1.0f,1.0f,1.0f));
1205}
1206
1207void Text::drawImplementation(osg::State& state, const osg::Vec4& colorMultiplier) const
1208{
1209    unsigned int contextID = state.getContextID();
1210
1211    state.applyMode(GL_BLEND,true);
1212#if 1
1213    state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::ON);
1214#else
1215    state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
1216#endif
1217#if defined(OSG_GL_FIXED_FUNCTION_AVAILABLE)
1218    state.applyTextureAttribute(0,getActiveFont()->getTexEnv());
1219#endif
1220    if (_characterSizeMode!=OBJECT_COORDS || _autoRotateToScreen)
1221    {
1222        unsigned int frameNumber = state.getFrameStamp()?state.getFrameStamp()->getFrameNumber():0;
1223        AutoTransformCache& atc = _autoTransformCache[contextID];
1224        const osg::Matrix& modelview = state.getModelViewMatrix();
1225        const osg::Matrix& projection = state.getProjectionMatrix();
1226
1227        osg::Vec3 newTransformedPosition = _position*modelview;
1228
1229        int width = atc._width;
1230        int height = atc._height;
1231
1232        const osg::Viewport* viewport = state.getCurrentViewport();
1233        if (viewport)
1234        {
1235            width = static_cast<int>(viewport->width());
1236            height = static_cast<int>(viewport->height());
1237        }
1238
1239        bool doUpdate = atc._traversalNumber==-1;
1240        if (atc._traversalNumber>=0)
1241        {
1242            if (atc._modelview!=modelview)
1243            {
1244                doUpdate = true;
1245            }
1246            else if (width!=atc._width || height!=atc._height)
1247            {
1248                doUpdate = true;
1249            }
1250            else if (atc._projection!=projection)
1251            {
1252                doUpdate = true;
1253            }
1254        }
1255       
1256        atc._traversalNumber = frameNumber;
1257        atc._width = width;
1258        atc._height = height;
1259       
1260        if (doUpdate)
1261        {   
1262            atc._transformedPosition = newTransformedPosition;
1263            atc._projection = projection;
1264            atc._modelview = modelview;
1265
1266            computePositions(contextID);
1267        }
1268       
1269    }
1270   
1271   
1272    // Ensure that the glyph coordinates have been transformed for
1273    // this context id.
1274
1275    if ( !_textureGlyphQuadMap.empty() )
1276    {
1277        const GlyphQuads& glyphquad = (_textureGlyphQuadMap.begin())->second;
1278        if ( glyphquad._transformedCoords[contextID].empty() )
1279        {
1280            computePositions(contextID);
1281        }
1282    }
1283
1284    osg::GLBeginEndAdapter& gl = (state.getGLBeginEndAdapter());
1285
1286    state.Normal(_normal.x(), _normal.y(), _normal.z());
1287
1288    if (_drawMode & FILLEDBOUNDINGBOX)
1289    {
1290        if (_textBB.valid())
1291        {
1292        #if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
1293            state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
1294
1295            const osg::Matrix& matrix = _autoTransformCache[contextID]._matrix;
1296
1297            osg::Vec3 c00(osg::Vec3(_textBB.xMin(),_textBB.yMin(),_textBB.zMin())*matrix);
1298            osg::Vec3 c10(osg::Vec3(_textBB.xMax(),_textBB.yMin(),_textBB.zMin())*matrix);
1299            osg::Vec3 c11(osg::Vec3(_textBB.xMax(),_textBB.yMax(),_textBB.zMin())*matrix);
1300            osg::Vec3 c01(osg::Vec3(_textBB.xMin(),_textBB.yMax(),_textBB.zMin())*matrix);
1301
1302            switch(_backdropImplementation)
1303            {
1304                case NO_DEPTH_BUFFER:
1305                    // Do nothing.  The bounding box will be rendered before the text and that's all that matters.
1306                    break;
1307                case DEPTH_RANGE:
1308                    glPushAttrib(GL_DEPTH_BUFFER_BIT);
1309                    //unsigned int backdrop_index = 0;
1310                    //unsigned int max_backdrop_index = 8;
1311                    //const double offset = double(max_backdrop_index - backdrop_index) * 0.003;
1312                    glDepthRange(0.001, 1.001);
1313                    break;
1314                /*case STENCIL_BUFFER:
1315                    break;*/
1316                default:
1317                    glPushAttrib(GL_POLYGON_OFFSET_FILL);
1318                    glEnable(GL_POLYGON_OFFSET_FILL);
1319                    glPolygonOffset(0.1f * osg::PolygonOffset::getFactorMultiplier(), 10.0f * osg::PolygonOffset::getUnitsMultiplier() );
1320            }
1321
1322            gl.Color4f(colorMultiplier.r()*_textBBColor.r(),colorMultiplier.g()*_textBBColor.g(),colorMultiplier.b()*_textBBColor.b(),colorMultiplier.a()*_textBBColor.a());
1323            gl.Begin(GL_QUADS);
1324                gl.Vertex3fv(c00.ptr());
1325                gl.Vertex3fv(c10.ptr());
1326                gl.Vertex3fv(c11.ptr());
1327                gl.Vertex3fv(c01.ptr());
1328            gl.End();
1329
1330            switch(_backdropImplementation)
1331            {
1332                case NO_DEPTH_BUFFER:
1333                    // Do nothing.
1334                    break;
1335                case DEPTH_RANGE:
1336                    glDepthRange(0.0, 1.0);
1337                    glPopAttrib();
1338                    break;
1339                /*case STENCIL_BUFFER:
1340                    break;*/
1341                default:
1342                    glDisable(GL_POLYGON_OFFSET_FILL);
1343                    glPopAttrib();
1344            }
1345        #else
1346            OSG_NOTICE<<"Warning: Text::drawImplementation() fillMode FILLEDBOUNDINGBOX not supported"<<std::endl;
1347        #endif
1348        }
1349    }   
1350
1351#if 1
1352    state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::ON);
1353#else
1354    state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
1355#endif
1356#if defined(OSG_GL_FIXED_FUNCTION_AVAILABLE)
1357    state.applyTextureAttribute(0,getActiveFont()->getTexEnv());
1358#endif
1359
1360    if (_drawMode & TEXT)
1361    {
1362
1363        state.disableAllVertexArrays();
1364
1365        // Okay, since ATI's cards/drivers are not working correctly,
1366        // we need alternative solutions to glPolygonOffset.
1367        // So this is a pick your poison approach. Each alternative
1368        // backend has trade-offs associated with it, but with luck,
1369        // the user may find that works for them.
1370        if(_backdropType != NONE && _backdropImplementation != DELAYED_DEPTH_WRITES)
1371        {
1372            switch(_backdropImplementation)
1373            {
1374                case POLYGON_OFFSET:
1375                    renderWithPolygonOffset(state,colorMultiplier);
1376                    break;
1377                case NO_DEPTH_BUFFER:
1378                    renderWithNoDepthBuffer(state,colorMultiplier);
1379                    break;
1380                case DEPTH_RANGE:
1381                    renderWithDepthRange(state,colorMultiplier);
1382                    break;
1383                case STENCIL_BUFFER:
1384                    renderWithStencilBuffer(state,colorMultiplier);
1385                    break;
1386                default:
1387                    renderWithPolygonOffset(state,colorMultiplier);
1388            }
1389        }
1390        else
1391        {
1392            renderWithDelayedDepthWrites(state,colorMultiplier);
1393        }
1394    }
1395
1396    if (_drawMode & BOUNDINGBOX)
1397    {
1398
1399        if (_textBB.valid())
1400        {
1401            state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
1402
1403            const osg::Matrix& matrix = _autoTransformCache[contextID]._matrix;
1404
1405            osg::Vec3 c00(osg::Vec3(_textBB.xMin(),_textBB.yMin(),_textBB.zMin())*matrix);
1406            osg::Vec3 c10(osg::Vec3(_textBB.xMax(),_textBB.yMin(),_textBB.zMin())*matrix);
1407            osg::Vec3 c11(osg::Vec3(_textBB.xMax(),_textBB.yMax(),_textBB.zMin())*matrix);
1408            osg::Vec3 c01(osg::Vec3(_textBB.xMin(),_textBB.yMax(),_textBB.zMin())*matrix);
1409
1410       
1411            gl.Color4f(colorMultiplier.r()*_textBBColor.r(),colorMultiplier.g()*_textBBColor.g(),colorMultiplier.b()*_textBBColor.b(),colorMultiplier.a()*_textBBColor.a());
1412            gl.Begin(GL_LINE_LOOP);
1413                gl.Vertex3fv(c00.ptr());
1414                gl.Vertex3fv(c10.ptr());
1415                gl.Vertex3fv(c11.ptr());
1416                gl.Vertex3fv(c01.ptr());
1417            gl.End();
1418        }
1419    }
1420
1421    if (_drawMode & ALIGNMENT)
1422    {
1423        gl.Color4fv(colorMultiplier.ptr());
1424
1425        float cursorsize = _characterHeight*0.5f;
1426
1427        const osg::Matrix& matrix = _autoTransformCache[contextID]._matrix;
1428
1429        osg::Vec3 hl(osg::Vec3(_offset.x()-cursorsize,_offset.y(),_offset.z())*matrix);
1430        osg::Vec3 hr(osg::Vec3(_offset.x()+cursorsize,_offset.y(),_offset.z())*matrix);
1431        osg::Vec3 vt(osg::Vec3(_offset.x(),_offset.y()-cursorsize,_offset.z())*matrix);
1432        osg::Vec3 vb(osg::Vec3(_offset.x(),_offset.y()+cursorsize,_offset.z())*matrix);
1433
1434        state.applyTextureMode(0,GL_TEXTURE_2D,osg::StateAttribute::OFF);
1435       
1436        gl.Begin(GL_LINES);
1437            gl.Vertex3fv(hl.ptr());
1438            gl.Vertex3fv(hr.ptr());
1439            gl.Vertex3fv(vt.ptr());
1440            gl.Vertex3fv(vb.ptr());
1441        gl.End();
1442       
1443    }   
1444}
1445
1446void Text::accept(osg::Drawable::ConstAttributeFunctor& af) const
1447{
1448    for(TextureGlyphQuadMap::const_iterator titr=_textureGlyphQuadMap.begin();
1449        titr!=_textureGlyphQuadMap.end();
1450        ++titr)
1451    {
1452        const GlyphQuads& glyphquad = titr->second;
1453        af.apply(osg::Drawable::VERTICES,glyphquad._transformedCoords[0].size(),&(glyphquad._transformedCoords[0].front()));
1454        af.apply(osg::Drawable::TEXTURE_COORDS_0,glyphquad._texcoords.size(),&(glyphquad._texcoords.front()));
1455    }
1456}
1457
1458void Text::accept(osg::PrimitiveFunctor& pf) const
1459{
1460    for(TextureGlyphQuadMap::const_iterator titr=_textureGlyphQuadMap.begin();
1461        titr!=_textureGlyphQuadMap.end();
1462        ++titr)
1463    {
1464        const GlyphQuads& glyphquad = titr->second;
1465
1466        pf.setVertexArray(glyphquad._transformedCoords[0].size(),&(glyphquad._transformedCoords[0].front()));
1467        pf.drawArrays(GL_QUADS,0,glyphquad._transformedCoords[0].size());
1468           
1469    }
1470   
1471}
1472
1473
1474void Text::setThreadSafeRefUnref(bool threadSafe)
1475{
1476    TextBase::setThreadSafeRefUnref(threadSafe);
1477
1478    getActiveFont()->setThreadSafeRefUnref(threadSafe);
1479}
1480
1481void Text::resizeGLObjectBuffers(unsigned int maxSize)
1482{
1483    TextBase::resizeGLObjectBuffers(maxSize);
1484   
1485    getActiveFont()->resizeGLObjectBuffers(maxSize);
1486}
1487
1488
1489void Text::releaseGLObjects(osg::State* state) const
1490{
1491    TextBase::releaseGLObjects(state);
1492    getActiveFont()->releaseGLObjects(state);
1493}
1494
1495
1496void Text::setBackdropType(BackdropType type)
1497{
1498    if (_backdropType==type) return;
1499
1500    _backdropType = type;
1501    computeGlyphRepresentation();
1502}
1503
1504void Text::setBackdropImplementation(BackdropImplementation implementation)
1505{
1506    if (_backdropImplementation==implementation) return;
1507
1508    _backdropImplementation = implementation;
1509    computeGlyphRepresentation();
1510}
1511
1512
1513void Text::setBackdropOffset(float offset)
1514{
1515    _backdropHorizontalOffset = offset;
1516    _backdropVerticalOffset = offset;
1517    computeGlyphRepresentation();
1518}
1519
1520void Text::setBackdropOffset(float horizontal, float vertical)
1521{
1522    _backdropHorizontalOffset = horizontal;
1523    _backdropVerticalOffset = vertical;
1524    computeGlyphRepresentation();
1525}
1526
1527void Text::setBackdropColor(const osg::Vec4& color)
1528{
1529    _backdropColor = color;
1530}
1531
1532void Text::setColorGradientMode(ColorGradientMode mode)
1533{
1534    if (_colorGradientMode==mode) return;
1535
1536    _colorGradientMode = mode;
1537    computeGlyphRepresentation();
1538}
1539
1540void Text::setColorGradientCorners(const osg::Vec4& topLeft, const osg::Vec4& bottomLeft, const osg::Vec4& bottomRight, const osg::Vec4& topRight)
1541{
1542    _colorGradientTopLeft = topLeft;
1543    _colorGradientBottomLeft = bottomLeft;
1544    _colorGradientBottomRight = bottomRight;
1545    _colorGradientTopRight = topRight;
1546    computeGlyphRepresentation();
1547}
1548
1549// Formula for f(x,y) from Wikipedia "Bilinear interpolation", 2006-06-18
1550float Text::bilinearInterpolate(float x1, float x2, float y1, float y2, float x, float y, float q11, float q12, float q21, float q22) const
1551{
1552    return (
1553        ((q11 / ((x2-x1)*(y2-y1))) * (x2-x)*(y2-y))
1554        + ((q21 / ((x2-x1)*(y2-y1))) * (x-x1)*(y2-y))
1555        + ((q12 / ((x2-x1)*(y2-y1))) * (x2-x)*(y-y1))
1556        + ((q22 / ((x2-x1)*(y2-y1))) * (x-x1)*(y-y1))
1557    );
1558}
1559
1560void Text::drawForegroundText(osg::State& state, const GlyphQuads& glyphquad, const osg::Vec4& colorMultiplier) const
1561{
1562    unsigned int contextID = state.getContextID();
1563
1564    const GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
1565    if (!transformedCoords.empty())
1566    {
1567        state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedCoords.front()));
1568        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1569
1570        if(_colorGradientMode == SOLID)
1571        {
1572            state.disableColorPointer();
1573            state.Color(colorMultiplier.r()*_color.r(),colorMultiplier.g()*_color.g(),colorMultiplier.b()*_color.b(),colorMultiplier.a()*_color.a());
1574        }
1575        else
1576        {
1577            state.setColorPointer( 4, GL_FLOAT, 0, &(glyphquad._colorCoords.front()));
1578        }
1579
1580        state.drawQuads(0,transformedCoords.size());
1581
1582    }
1583}
1584
1585void Text::renderOnlyForegroundText(osg::State& state, const osg::Vec4& colorMultiplier) const
1586{
1587    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1588        titr!=_textureGlyphQuadMap.end();
1589        ++titr)
1590    {
1591        // need to set the texture here...
1592        state.applyTextureAttribute(0,titr->first.get());
1593
1594        const GlyphQuads& glyphquad = titr->second;
1595
1596        drawForegroundText(state, glyphquad, colorMultiplier);
1597    }
1598}
1599
1600void Text::renderWithDelayedDepthWrites(osg::State& state, const osg::Vec4& colorMultiplier) const
1601{
1602    //glPushAttrib( _enableDepthWrites ? (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) : GL_DEPTH_BUFFER_BIT);
1603    // Render to color buffer without writing to depth buffer.
1604    glDepthMask(GL_FALSE);
1605    drawTextWithBackdrop(state,colorMultiplier);
1606
1607    // Render to depth buffer if depth writes requested.
1608    if( _enableDepthWrites )
1609    {
1610        glDepthMask(GL_TRUE);
1611        glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1612        drawTextWithBackdrop(state,colorMultiplier);
1613    }
1614
1615    state.haveAppliedAttribute(osg::StateAttribute::DEPTH);
1616    state.haveAppliedAttribute(osg::StateAttribute::COLORMASK);
1617
1618    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1619    //glPopAttrib();
1620}
1621
1622void Text::drawTextWithBackdrop(osg::State& state, const osg::Vec4& colorMultiplier) const
1623{
1624    unsigned int contextID = state.getContextID();
1625
1626    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1627        titr!=_textureGlyphQuadMap.end();
1628        ++titr)
1629    {
1630        // need to set the texture here...
1631        state.applyTextureAttribute(0,titr->first.get());
1632
1633        const GlyphQuads& glyphquad = titr->second;
1634
1635        if(_backdropType != NONE)
1636        {
1637            unsigned int backdrop_index;
1638            unsigned int max_backdrop_index;
1639            if(_backdropType == OUTLINE)
1640            {
1641                backdrop_index = 0;
1642                max_backdrop_index = 8;
1643            }
1644            else
1645            {
1646                backdrop_index = _backdropType;
1647                max_backdrop_index = _backdropType+1;
1648            }
1649
1650            state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1651            state.disableColorPointer();
1652            state.Color(_backdropColor.r(),_backdropColor.g(),_backdropColor.b(),_backdropColor.a());
1653
1654            for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1655            {
1656                const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1657                if (!transformedBackdropCoords.empty())
1658                {
1659                    state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1660                    state.drawQuads(0,transformedBackdropCoords.size());
1661                }
1662            }
1663        }
1664
1665        drawForegroundText(state, glyphquad, colorMultiplier);
1666    }
1667}
1668
1669
1670void Text::renderWithPolygonOffset(osg::State& state, const osg::Vec4& colorMultiplier) const
1671{
1672#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
1673    unsigned int contextID = state.getContextID();
1674
1675    if (!osg::PolygonOffset::areFactorAndUnitsMultipliersSet())
1676    {
1677        osg::PolygonOffset::setFactorAndUnitsMultipliersUsingBestGuessForDriver();
1678    }
1679
1680    // Do I really need to do this for glPolygonOffset?
1681    glPushAttrib(GL_POLYGON_OFFSET_FILL);
1682    glEnable(GL_POLYGON_OFFSET_FILL);
1683
1684    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1685        titr!=_textureGlyphQuadMap.end();
1686        ++titr)
1687    {
1688        // need to set the texture here...
1689        state.applyTextureAttribute(0,titr->first.get());
1690
1691        const GlyphQuads& glyphquad = titr->second;
1692
1693        unsigned int backdrop_index;
1694        unsigned int max_backdrop_index;
1695        if(_backdropType == OUTLINE)
1696        {
1697            backdrop_index = 0;
1698            max_backdrop_index = 8;
1699        }
1700        else
1701        {
1702            backdrop_index = _backdropType;
1703            max_backdrop_index = _backdropType+1;
1704        }
1705
1706        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1707        state.disableColorPointer();
1708        state.Color(_backdropColor.r(),_backdropColor.g(),_backdropColor.b(),_backdropColor.a());
1709
1710        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1711        {
1712            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1713            if (!transformedBackdropCoords.empty())
1714            {
1715                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1716                glPolygonOffset(0.1f * osg::PolygonOffset::getFactorMultiplier(),
1717                                osg::PolygonOffset::getUnitsMultiplier() * (max_backdrop_index-backdrop_index) );
1718                state.drawQuads(0,transformedBackdropCoords.size());
1719            }
1720        }
1721
1722        // Reset the polygon offset so the foreground text is on top
1723        glPolygonOffset(0.0f,0.0f);
1724
1725        drawForegroundText(state, glyphquad, colorMultiplier);
1726    }
1727
1728    glPopAttrib();
1729#else
1730    OSG_NOTICE<<"Warning: Text::renderWithPolygonOffset(..) not implemented."<<std::endl;
1731#endif
1732}
1733   
1734
1735void Text::renderWithNoDepthBuffer(osg::State& state, const osg::Vec4& colorMultiplier) const
1736{
1737#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
1738    unsigned int contextID = state.getContextID();
1739
1740    glPushAttrib(GL_DEPTH_BUFFER_BIT);
1741    glDisable(GL_DEPTH_TEST);
1742
1743    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1744        titr!=_textureGlyphQuadMap.end();
1745        ++titr)
1746    {
1747        // need to set the texture here...
1748        state.applyTextureAttribute(0,titr->first.get());
1749
1750        const GlyphQuads& glyphquad = titr->second;
1751
1752        unsigned int backdrop_index;
1753        unsigned int max_backdrop_index;
1754        if(_backdropType == OUTLINE)
1755        {
1756            backdrop_index = 0;
1757            max_backdrop_index = 8;
1758        }
1759        else
1760        {
1761            backdrop_index = _backdropType;
1762            max_backdrop_index = _backdropType+1;
1763        }
1764
1765        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1766        state.disableColorPointer();
1767        state.Color(_backdropColor.r(),_backdropColor.g(),_backdropColor.b(),_backdropColor.a());
1768
1769        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1770        {
1771            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1772            if (!transformedBackdropCoords.empty())
1773            {
1774                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1775                state.drawQuads(0,transformedBackdropCoords.size());
1776            }
1777        }
1778
1779        drawForegroundText(state, glyphquad, colorMultiplier);
1780    }
1781
1782    glPopAttrib();
1783#else
1784    OSG_NOTICE<<"Warning: Text::renderWithNoDepthBuffer(..) not implemented."<<std::endl;
1785#endif
1786}
1787
1788// This idea comes from Paul Martz's OpenGL FAQ: 13.050
1789void Text::renderWithDepthRange(osg::State& state, const osg::Vec4& colorMultiplier) const
1790{
1791#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
1792    unsigned int contextID = state.getContextID();
1793
1794    // Hmmm, the man page says GL_VIEWPORT_BIT for Depth range (near and far)
1795    // but experimentally, GL_DEPTH_BUFFER_BIT for glDepthRange.
1796//    glPushAttrib(GL_VIEWPORT_BIT);
1797    glPushAttrib(GL_DEPTH_BUFFER_BIT);
1798
1799    for(TextureGlyphQuadMap::iterator titr=_textureGlyphQuadMap.begin();
1800        titr!=_textureGlyphQuadMap.end();
1801        ++titr)
1802    {
1803        // need to set the texture here...
1804        state.applyTextureAttribute(0,titr->first.get());
1805
1806        const GlyphQuads& glyphquad = titr->second;
1807
1808        unsigned int backdrop_index;
1809        unsigned int max_backdrop_index;
1810        if(_backdropType == OUTLINE)
1811        {
1812            backdrop_index = 0;
1813            max_backdrop_index = 8;
1814        }
1815        else
1816        {
1817            backdrop_index = _backdropType;
1818            max_backdrop_index = _backdropType+1;
1819        }
1820
1821        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1822        state.disableColorPointer();
1823        state.Color(_backdropColor.r(),_backdropColor.g(),_backdropColor.b(),_backdropColor.a());
1824
1825        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1826        {
1827            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1828            if (!transformedBackdropCoords.empty())
1829            {
1830                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1831                double offset = double(max_backdrop_index-backdrop_index)*0.0001;
1832                glDepthRange( offset, 1.0+offset);
1833
1834                state.drawQuads(0,transformedBackdropCoords.size());
1835            }
1836        }
1837
1838        glDepthRange(0.0, 1.0);
1839
1840        drawForegroundText(state, glyphquad, colorMultiplier);
1841    }
1842
1843    glPopAttrib();
1844#else
1845    OSG_NOTICE<<"Warning: Text::renderWithDepthRange(..) not implemented."<<std::endl;
1846#endif
1847}
1848
1849void Text::renderWithStencilBuffer(osg::State& state, const osg::Vec4& colorMultiplier) const
1850{
1851#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GL3_AVAILABLE)
1852    /* Here are the steps:
1853     * 1) Disable drawing color
1854     * 2) Enable the stencil buffer
1855     * 3) Draw all the text to the stencil buffer
1856     * 4) Disable the stencil buffer
1857     * 5) Enable color
1858     * 6) Disable the depth buffer
1859     * 7) Draw all the text again.
1860     * 7b) Make sure the foreground text is drawn last if priority levels
1861     * are the same OR
1862     * 7c) If priority levels are different, then make sure the foreground
1863     * text has the higher priority.
1864     */
1865    unsigned int contextID = state.getContextID();
1866    TextureGlyphQuadMap::iterator titr; // Moved up here for VC6
1867   
1868    glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_STENCIL_TEST);
1869
1870    // It seems I can get away without calling this here
1871    //glClear(GL_STENCIL_BUFFER_BIT);
1872
1873    // enable stencil buffer
1874    glEnable(GL_STENCIL_TEST);
1875
1876    // write a one to the stencil buffer everywhere we are about to draw
1877    glStencilFunc(GL_ALWAYS, 1, 1);
1878
1879    // write only to the stencil buffer if we pass the depth test
1880    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
1881
1882    // Disable writing to the color buffer so we only write to the stencil
1883    // buffer and the depth buffer
1884    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
1885
1886    // make sure the depth buffer is enabled
1887//    glEnable(GL_DEPTH_TEST);
1888//    glDepthMask(GL_TRUE);
1889//    glDepthFunc(GL_LESS);
1890
1891    // Arrrgh! Why does the code only seem to work correctly if I call this?
1892    glDepthMask(GL_FALSE);
1893   
1894
1895    // Draw all the text to the stencil buffer to mark out the region
1896    // that we can write too.
1897   
1898    for(titr=_textureGlyphQuadMap.begin();
1899        titr!=_textureGlyphQuadMap.end();
1900        ++titr)
1901    {
1902        // need to set the texture here...
1903        state.applyTextureAttribute(0,titr->first.get());
1904
1905        const GlyphQuads& glyphquad = titr->second;
1906
1907        unsigned int backdrop_index;
1908        unsigned int max_backdrop_index;
1909        if(_backdropType == OUTLINE)
1910        {
1911            backdrop_index = 0;
1912            max_backdrop_index = 8;
1913        }
1914        else
1915        {
1916            backdrop_index = _backdropType;
1917            max_backdrop_index = _backdropType+1;
1918        }
1919
1920        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1921        state.disableColorPointer();
1922
1923        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1924        {
1925            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1926            if (!transformedBackdropCoords.empty())
1927            {
1928                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1929                state.drawQuads(0,transformedBackdropCoords.size());
1930            }
1931        }
1932
1933        // Draw the foreground text
1934        const GlyphQuads::Coords3& transformedCoords = glyphquad._transformedCoords[contextID];
1935        if (!transformedCoords.empty())
1936        {
1937            state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedCoords.front()));
1938            state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1939            state.drawQuads(0,transformedCoords.size());
1940        }
1941    }
1942
1943
1944    // disable the depth buffer
1945//    glDisable(GL_DEPTH_TEST);
1946//    glDepthMask(GL_FALSE);
1947//    glDepthMask(GL_TRUE);
1948//    glDepthFunc(GL_ALWAYS);
1949
1950    // Set the stencil function to pass when the stencil is 1
1951    // Bug: This call seems to have no effect. Try changing to NOTEQUAL
1952    // and see the exact same results.
1953    glStencilFunc(GL_EQUAL, 1, 1);
1954
1955    // disable writing to the stencil buffer
1956    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1957    glStencilMask(GL_FALSE);
1958
1959    // Re-enable writing to the color buffer so we can see the results
1960    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
1961
1962
1963    // Draw all the text again
1964
1965    for(titr=_textureGlyphQuadMap.begin();
1966        titr!=_textureGlyphQuadMap.end();
1967        ++titr)
1968    {
1969        // need to set the texture here...
1970        state.applyTextureAttribute(0,titr->first.get());
1971
1972        const GlyphQuads& glyphquad = titr->second;
1973
1974        unsigned int backdrop_index;
1975        unsigned int max_backdrop_index;
1976        if(_backdropType == OUTLINE)
1977        {
1978            backdrop_index = 0;
1979            max_backdrop_index = 8;
1980        }
1981        else
1982        {
1983            backdrop_index = _backdropType;
1984            max_backdrop_index = _backdropType+1;
1985        }
1986
1987        state.setTexCoordPointer( 0, 2, GL_FLOAT, 0, &(glyphquad._texcoords.front()));
1988        state.disableColorPointer();
1989        state.Color(_backdropColor.r(),_backdropColor.g(),_backdropColor.b(),_backdropColor.a());
1990
1991        for( ; backdrop_index < max_backdrop_index; backdrop_index++)
1992        {
1993            const GlyphQuads::Coords3& transformedBackdropCoords = glyphquad._transformedBackdropCoords[backdrop_index][contextID];
1994            if (!transformedBackdropCoords.empty())
1995            {
1996                state.setVertexPointer( 3, GL_FLOAT, 0, &(transformedBackdropCoords.front()));
1997                state.drawQuads(0,transformedBackdropCoords.size());
1998            }
1999        }
2000
2001        drawForegroundText(state, glyphquad, colorMultiplier);
2002    }
2003
2004    glPopAttrib();
2005#else
2006    OSG_NOTICE<<"Warning: Text::renderWithStencilBuffer(..) not implemented."<<std::endl;
2007#endif
2008}
Note: See TracBrowser for help on using the browser.