I ran into the same problem and I agree that the appearance of the
clipped text is quite ugly. Unfortunately, the problem lies within the
design of osgText and not your usage of it.

Texels sampled within the character cell are linearly blended with one
another, giving an antialiasing effect internal to the character cell.
However, when a character cell has a texel set at its edge, no
blending takes place outside that edge because the texture isn't drawn
outside the quad.

My solution was to modify osgText to draw larger quads for the
character cells (by one texel in each direction), adjusting texture
coordinates accordingly. Side effects are that character cells now
overlap so depth testing needs to be disabled (or some sort of
stenciling operation performed), and extra margin should be allocated
for each character in the texture (using Font::setGlyphImageMargin).

Another possible "solution" is to disable texture filtering, so all
parts of each character will have the same clipped appearance. If
you're already using some sort of full screen antialiasing this might
not look too bad.

Below is my modified version of Text::computeGlyphRepresentation()
from osgText/Text.cpp. The modified code is controlled by #ifdefs.

Robert: Given the restrictions (depth writing needs to be disabled),
is this something you would want to add to osg? Perhaps configurable
as an optional method of text rendering? If so, I'll submit the
changed files to the submissions list.

void Text::computeGlyphRepresentation()
{
    Font* activefont = getActiveFont();
    if (!activefont) return;

    _textureGlyphQuadMap.clear();
    _lineCount = 0;

    if (_text.empty())
    {
        _textBB.set(0,0,0,0,0,0);//no size text
        computePositions(); //to reset the origin
        return;
    }

    OpenThreads::ScopedLock<Font::FontMutex>
lock(*(Font::getSerializeFontCallsMutex()));

    // initialize bounding box, it will be expanded during glyph
position calculation
    _textBB.init();

    osg::Vec2 startOfLine_coords(0.0f,0.0f);
    osg::Vec2 cursor(startOfLine_coords);
    osg::Vec2 local(0.0f,0.0f);

    unsigned int previous_charcode = 0;
    unsigned int linelength = 0;
    bool horizontal = _layout!=VERTICAL;
    bool kerning = true;

    unsigned int lineNumber = 0;

    activefont->setFontResolution(_fontWidth,_fontHeight);

    float hr = _characterHeight/(float)activefont->getFontHeight();
    float wr = hr/_characterAspectRatio;

    for(String::iterator itr=_text.begin();
        itr!=_text.end();
        )
    {
        // record the start of the current line
            String::iterator startOfLine_itr = itr;

            // find the end of the current line.
            osg::Vec2 endOfLine_coords(cursor);
            String::iterator endOfLine_itr =
computeLastCharacterOnLine(endOfLine_coords, itr,_text.end());

            linelength = endOfLine_itr - startOfLine_itr;

            // Set line position to correct alignment.
            switch(_layout)
            {
            case LEFT_TO_RIGHT:
            {
            switch(_alignment)
            {
              // nothing to be done for these
              //case LEFT_TOP:
              //case LEFT_CENTER:
              //case LEFT_BOTTOM:
              //case LEFT_BASE_LINE:
              //case LEFT_BOTTOM_BASE_LINE:
              //  break;
              case CENTER_TOP:
              case CENTER_CENTER:
              case CENTER_BOTTOM:
              case CENTER_BASE_LINE:
              case CENTER_BOTTOM_BASE_LINE:
                cursor.x() = (cursor.x() - endOfLine_coords.x()) *
0.5f;
                break;
              case RIGHT_TOP:
              case RIGHT_CENTER:
              case RIGHT_BOTTOM:
              case RIGHT_BASE_LINE:
              case RIGHT_BOTTOM_BASE_LINE:
                cursor.x() = cursor.x() - endOfLine_coords.x();
                break;
              default:
                break;
              }
            break;
            }
            case RIGHT_TO_LEFT:
            {
            switch(_alignment)
            {
              case LEFT_TOP:
              case LEFT_CENTER:
              case LEFT_BOTTOM:
              case LEFT_BASE_LINE:
              case LEFT_BOTTOM_BASE_LINE:
                cursor.x() = 2*cursor.x() - endOfLine_coords.x();
                break;
              case CENTER_TOP:
              case CENTER_CENTER:
              case CENTER_BOTTOM:
              case CENTER_BASE_LINE:
              case CENTER_BOTTOM_BASE_LINE:
                cursor.x() = cursor.x() + (cursor.x() -
endOfLine_coords.x()) * 0.5f;
                break;
              // nothing to be done for these
              //case RIGHT_TOP:
              //case RIGHT_CENTER:
              //case RIGHT_BOTTOM:
              //case RIGHT_BASE_LINE:
              //case RIGHT_BOTTOM_BASE_LINE:
              //  break;
              default:
                break;
            }
            break;
            }
            case VERTICAL:
            {
            switch(_alignment)
            {
              // TODO: current behaviour top baselines lined up in
both cases - need to implement
              //       top of characters aligment - Question is this
neccesary?
              // ... otherwise, nothing to be done for these 6 cases
              //case LEFT_TOP:
              //case CENTER_TOP:
              //case RIGHT_TOP:
              //  break;
              //case LEFT_BASE_LINE:
              //case CENTER_BASE_LINE:
              //case RIGHT_BASE_LINE:
              //  break;
              case LEFT_CENTER:
              case CENTER_CENTER:
              case RIGHT_CENTER:
                cursor.y() = cursor.y() + (cursor.y() -
endOfLine_coords.y()) * 0.5f;
                break;
              case LEFT_BOTTOM_BASE_LINE:
              case CENTER_BOTTOM_BASE_LINE:
              case RIGHT_BOTTOM_BASE_LINE:
                cursor.y() = cursor.y() - (linelength *
_characterHeight);
                break;
              case LEFT_BOTTOM:
              case CENTER_BOTTOM:
              case RIGHT_BOTTOM:
                cursor.y() = 2*cursor.y() - endOfLine_coords.y();
                break;
              default:
                break;
            }
            break;
          }
        }

        if (itr!=endOfLine_itr)
        {

            for(;itr!=endOfLine_itr;++itr)
            {
                unsigned int charcode = *itr;

                Font::Glyph* glyph = activefont->getGlyph(charcode);
                if (glyph)
                {
                    float width = (float)(glyph->s()) * wr;
                    float height = (float)(glyph->t()) * hr;

                    #ifdef TREES_CODE_FOR_MAKING_SPACES_EDITABLE
                    if (width == 0.0f)  width = glyph-
>getHorizontalAdvance() * wr;
                    if (height == 0.0f) height = glyph-
>getVerticalAdvance() * hr;
                    #endif

                    if (_layout==RIGHT_TO_LEFT)
                    {
                        cursor.x() -= glyph->getHorizontalAdvance() *
wr;
                    }

                    // adjust cursor position w.r.t any kerning.
                    if (kerning && previous_charcode)
                    {
                        switch(_layout)
                        {
                          case LEFT_TO_RIGHT:
                          {
                            osg::Vec2 delta(activefont-
>getKerning(previous_charcode,charcode,_kerningType));
                            cursor.x() += delta.x() * wr;
                            cursor.y() += delta.y() * hr;
                            break;
                          }
                          case RIGHT_TO_LEFT:
                          {
                            osg::Vec2 delta(activefont-
>getKerning(charcode,previous_charcode,_kerningType));
                            cursor.x() -= delta.x() * wr;
                            cursor.y() -= delta.y() * hr;
                            break;
                          }
                          case VERTICAL:
                            break; // no kerning when vertical.
                        }
                    }

                    local = cursor;
                    osg::Vec2 bearing(horizontal?glyph-
>getHorizontalBearing():glyph->getVerticalBearing());
                    local.x() += bearing.x() * wr;
                    local.y() += bearing.y() * hr;

                    GlyphQuads& glyphquad = _textureGlyphQuadMap[glyph-
>getTexture()];

                    glyphquad._glyphs.push_back(glyph);
                    glyphquad._lineNumbers.push_back(lineNumber);
#define FixClippingProblem
#if !defined(FixClippingProblem)
                    // set up the coords of the quad
                    glyphquad._coords.push_back(local
+osg::Vec2(0.0f,height));
                    glyphquad._coords.push_back(local+osg::Vec2(0.0f,
0.0f));
                    glyphquad._coords.push_back(local+osg::Vec2(width,
0.0f));
                    glyphquad._coords.push_back(local
+osg::Vec2(width,height));

                    // set up the tex coords of the quad
                    const osg::Vec2& mintc = glyph->getMinTexCoord();
                    const osg::Vec2& maxtc = glyph->getMaxTexCoord();

 
glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),maxtc.y()));
 
glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),mintc.y()));
 
glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),mintc.y()));
 
glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),maxtc.y()));

                    // move the cursor onto the next character.
                    // also expand bounding box
                    switch(_layout)
                    {
                      case LEFT_TO_RIGHT:
                          cursor.x() += glyph->getHorizontalAdvance()
* wr;
 
_textBB.expandBy(osg::Vec3(local.x(),local.y(),0.0f)); //lower left
corner
 
_textBB.expandBy(osg::Vec3(cursor.x(),local.y()+height,0.0f)); //upper
right corner
                          break;
                      case VERTICAL:
                          cursor.y() -= glyph->getVerticalAdvance()
*hr;
 
_textBB.expandBy(osg::Vec3(local.x(),local.y()+height,0.0f)); //upper
left corner
                          _textBB.expandBy(osg::Vec3(local.x()
+width,cursor.y(),0.0f)); //lower right corner
                          break;
                      case RIGHT_TO_LEFT:
                          _textBB.expandBy(osg::Vec3(local.x()
+width,local.y(),0.0f)); //lower right corner
 
_textBB.expandBy(osg::Vec3(cursor.x(),local.y()+height,0.0f)); //upper
left corner
                          break;
                    }
#else // Use modified text rendering
// Character quads are enlarged, and texture coordinates
correspondingly expanded
// to allow blending of text outside of defined character cell.
// Extra margin should be allocated in the glyph's texture since we're
using an extra texel
// of blank space. Also, depth writing should be disabled so adjacent
characters  are guaranteed
// not to clip each other.
                    // set up the tex coords of the quad
                    osg::Vec2 mintc = glyph->getMinTexCoord();
                    osg::Vec2 maxtc = glyph->getMaxTexCoord();
                    osg::Vec2 vDiff = maxtc - mintc;
                    float fHorizTCMargin = 1.0 / glyph->getTexture()-
>getTextureWidth();
                    float fVertTCMargin = 1.0 / glyph->getTexture()-
>getTextureHeight();
                    float fHorizQuadMargin = vDiff.x() == 0 ? 0 :
width * fHorizTCMargin / vDiff.x();
                    float fVertQuadMargin = vDiff.y() == 0 ? 0 :
height * fVertTCMargin / vDiff.y();
                    mintc.x() -= fHorizTCMargin;
                    mintc.y() -= fVertTCMargin;
                    maxtc.x() += fHorizTCMargin;
                    maxtc.y() += fVertTCMargin;

 
glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),maxtc.y()));
 
glyphquad._texcoords.push_back(osg::Vec2(mintc.x(),mintc.y()));
 
glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),mintc.y()));
 
glyphquad._texcoords.push_back(osg::Vec2(maxtc.x(),maxtc.y()));

                    // set up the coords of the quad
                    osg::Vec2 upLeft = local+osg::Vec2(0.0f-
fHorizQuadMargin,height+fVertQuadMargin);
                    osg::Vec2 lowLeft = local+osg::Vec2(0.0f-
fHorizQuadMargin,0.0f-fVertQuadMargin);
                    osg::Vec2 lowRight = local+osg::Vec2(width
+fHorizQuadMargin,0.0f-fVertQuadMargin);
                    osg::Vec2 upRight = local+osg::Vec2(width
+fHorizQuadMargin,height+fVertQuadMargin);
                    glyphquad._coords.push_back(upLeft);
                    glyphquad._coords.push_back(lowLeft);
                    glyphquad._coords.push_back(lowRight);
                    glyphquad._coords.push_back(upRight);

                    // move the cursor onto the next character.
                    // also expand bounding box
                    switch(_layout)
                    {
                      case LEFT_TO_RIGHT:
                          cursor.x() += glyph->getHorizontalAdvance()
* wr;
                          _textBB.expandBy(osg::Vec3(lowLeft.x(),
lowLeft.y(), 0.0f)); //lower left corner
                          _textBB.expandBy(osg::Vec3(upRight.x(),
upRight.y(), 0.0f)); //upper right corner
                          break;
                      case VERTICAL:
                          cursor.y() -= glyph->getVerticalAdvance()
*hr;
 
_textBB.expandBy(osg::Vec3(upLeft.x(),upLeft.y(),0.0f)); //upper left
corner
 
_textBB.expandBy(osg::Vec3(lowRight.x(),lowRight.y(),0.0f)); //lower
right corner
                          break;
                      case RIGHT_TO_LEFT:
 
_textBB.expandBy(osg::Vec3(lowRight.x(),lowRight.y(),0.0f)); //lower
right corner
 
_textBB.expandBy(osg::Vec3(upLeft.x(),upLeft.y(),0.0f)); //upper left
corner
                          break;
                    }
#endif // else defined(FixClippingProblem)
                    previous_charcode = charcode;

                }
            }
        }
        else
        {
            ++itr;
        }

        if (itr!=_text.end())
        {
            // skip over return.
            if (*itr=='\n') ++itr;
        }

        // move to new line.
        switch(_layout)
        {
          case LEFT_TO_RIGHT:
          {
            startOfLine_coords.y() -= _characterHeight * (1.0 +
_lineSpacing);
            cursor = startOfLine_coords;
            previous_charcode = 0;
            _lineCount++;
            break;
          }
          case RIGHT_TO_LEFT:
          {
            startOfLine_coords.y() -= _characterHeight * (1.0 +
_lineSpacing);
            cursor = startOfLine_coords;
            previous_charcode = 0;
            _lineCount++;
            break;
          }
          case VERTICAL:
          {
            startOfLine_coords.x() += _characterHeight/
_characterAspectRatio * (1.0 + _lineSpacing);
            cursor = startOfLine_coords;
            previous_charcode = 0;
            // because _lineCount is the max vertical no. of
characters....
            _lineCount = (_lineCount >linelength)?
_lineCount:linelength;
          }
          break;
        }

        ++lineNumber;

    }

    computePositions();
    computeBackdropBoundingBox();
    computeColorGradients();
}

_______________________________________________
osg-users mailing list
osg-users@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Reply via email to