root/OpenSceneGraph/trunk/src/osgText/Glyph.cpp @ 11817

Revision 11817, 19.8 kB (checked in by robert, 4 years ago)

Copied libutil and libtess implementations in form Mesa 7.9/src/glu into the src/osg/glu,
changed extensions from .c to .cpp and got compiling as C files as part of the osg core library.

Updated and cleaned up the rest of the OSG to use the new internal GLU.

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#include <osgText/Font>
15#include <osgText/Text>
16
17#include <osg/State>
18#include <osg/Notify>
19#include <osg/GLU>
20
21#include <osgUtil/SmoothingVisitor>
22
23#include <string.h>
24
25#include "GlyphGeometry.h"
26
27using namespace osgText;
28using namespace std;
29
30GlyphTexture::GlyphTexture():
31    _margin(1),
32    _marginRatio(0.02f),
33    _usedY(0),
34    _partUsedX(0),
35    _partUsedY(0)
36{
37    setWrap(WRAP_S, CLAMP_TO_EDGE);
38    setWrap(WRAP_T, CLAMP_TO_EDGE);
39}
40
41GlyphTexture::~GlyphTexture()
42{
43}
44
45// return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.
46int GlyphTexture::compare(const osg::StateAttribute& rhs) const
47{
48    if (this<&rhs) return -1;
49    else if (this>&rhs) return 1;
50    return 0;
51}
52
53
54bool GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY)
55{
56    int maxAxis = std::max(glyph->s(), glyph->t());
57    int margin = _margin + (int)((float)maxAxis * _marginRatio);
58   
59    int width = glyph->s()+2*margin;
60    int height = glyph->t()+2*margin;
61
62    // first check box (_partUsedX,_usedY) to (width,height)
63    if (width <= (getTextureWidth()-_partUsedX) &&
64        height <= (getTextureHeight()-_usedY))
65    {
66        // can fit in existing row.
67
68        // record the position in which the texture will be stored.
69        posX = _partUsedX+margin;
70        posY = _usedY+margin;       
71
72        // move used markers on.
73        _partUsedX += width;
74        if (_usedY+height>_partUsedY) _partUsedY = _usedY+height;
75       
76        return true;
77    }
78   
79    // start an new row.
80    if (width <= getTextureWidth() &&
81        height <= (getTextureHeight()-_partUsedY))
82    {
83        // can fit next row.
84        _partUsedX = 0;
85        _usedY = _partUsedY;
86
87        posX = _partUsedX+margin;
88        posY = _usedY+margin;       
89
90        // move used markers on.
91        _partUsedX += width;
92        if (_usedY+height>_partUsedY) _partUsedY = _usedY+height;
93       
94        return true;
95    }
96
97    // doesn't fit into glyph.
98    return false;
99}
100
101void GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY)
102{
103    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
104
105    _glyphs.push_back(glyph);
106    for(unsigned int i=0;i<_glyphsToSubload.size();++i)
107    {
108        _glyphsToSubload[i].push_back(glyph);
109    }
110
111    // set up the details of where to place glyph's image in the texture.
112    glyph->setTexture(this);
113    glyph->setTexturePosition(posX,posY);
114
115    glyph->setMinTexCoord( osg::Vec2( static_cast<float>(posX)/static_cast<float>(getTextureWidth()),
116                                      static_cast<float>(posY)/static_cast<float>(getTextureHeight()) ) );
117    glyph->setMaxTexCoord( osg::Vec2( static_cast<float>(posX+glyph->s())/static_cast<float>(getTextureWidth()),
118                                      static_cast<float>(posY+glyph->t())/static_cast<float>(getTextureHeight()) ) );
119}
120
121void GlyphTexture::apply(osg::State& state) const
122{
123    // get the contextID (user defined ID of 0 upwards) for the
124    // current OpenGL context.
125    const unsigned int contextID = state.getContextID();
126
127    if (contextID>=_glyphsToSubload.size())
128    {
129        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
130
131        // graphics context is beyond the number of glyphsToSubloads, so
132        // we must now copy the glyph list across, this is a potential
133        // threading issue though is multiple applies are happening the
134        // same time on this object - to avoid this condition number of
135        // graphics contexts should be set before create text.
136        for(unsigned int i=_glyphsToSubload.size();i<=contextID;++i)
137        {
138            GlyphPtrList& glyphPtrs = _glyphsToSubload[i];
139            for(GlyphRefList::const_iterator itr=_glyphs.begin();
140                itr!=_glyphs.end();
141                ++itr)
142            {
143                glyphPtrs.push_back(itr->get());
144            }
145        }
146    }
147
148
149    const Extensions* extensions = getExtensions(contextID,true);
150    bool generateMipMapSupported = extensions->isGenerateMipMapSupported();
151
152    // get the texture object for the current contextID.
153    TextureObject* textureObject = getTextureObject(contextID);
154   
155    bool newTextureObject = (textureObject == 0);
156
157    if (newTextureObject)
158    {
159        GLint maxTextureSize = 256;
160        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
161        if (maxTextureSize < getTextureWidth() || maxTextureSize < getTextureHeight())
162        {
163            OSG_WARN<<"Warning: osgText::Font texture size of ("<<getTextureWidth()<<", "<<getTextureHeight()<<") too large, unable to create font texture."<<std::endl;
164            OSG_WARN<<"         Maximum supported by hardward by native OpenGL implementation is ("<<maxTextureSize<<","<<maxTextureSize<<")."<<std::endl;
165            OSG_WARN<<"         Please set OSG_MAX_TEXTURE_SIZE lenvironment variable to "<<maxTextureSize<<" and re-run application."<<std::endl;
166            return;
167        }
168       
169        // being bound for the first time, need to allocate the texture
170
171        _textureObjectBuffer[contextID] = textureObject = osg::Texture::generateTextureObject(
172                this, contextID,GL_TEXTURE_2D,1,GL_ALPHA,getTextureWidth(), getTextureHeight(),1,0);
173
174        textureObject->bind();
175
176
177        applyTexParameters(GL_TEXTURE_2D,state);
178
179       
180        // need to look at generate mip map extension if mip mapping required.
181        switch(_min_filter)
182        {
183        case NEAREST_MIPMAP_NEAREST:
184        case NEAREST_MIPMAP_LINEAR:
185        case LINEAR_MIPMAP_NEAREST:
186        case LINEAR_MIPMAP_LINEAR:
187            if (generateMipMapSupported)
188            {
189                glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
190            }
191            else glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, LINEAR);
192            break;
193        default:
194            // not mip mapping so no problems.
195            break;
196        }
197       
198        unsigned int imageDataSize = getTextureHeight()*getTextureWidth();
199        unsigned char* imageData = new unsigned char[imageDataSize];
200        for(unsigned int i=0; i<imageDataSize; ++i)
201        {
202            imageData[i] = 0;
203        }
204       
205
206//        OSG_NOTICE<<"Texture width = "<<getTextureWidth()<<std::endl;
207//        OSG_NOTICE<<"Texture height = "<<getTextureHeight()<<std::endl;
208               
209        // allocate the texture memory.
210        glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA,
211                getTextureWidth(), getTextureHeight(), 0,
212                GL_ALPHA,
213                GL_UNSIGNED_BYTE,
214                imageData );
215               
216        delete [] imageData;
217   
218    }
219    else
220    {
221        // reuse texture by binding.
222        textureObject->bind();
223       
224        if (getTextureParameterDirty(contextID))
225        {
226            applyTexParameters(GL_TEXTURE_2D,state);
227        }
228
229
230    }
231   
232    static const GLubyte* s_renderer = 0;
233    static bool s_subloadAllGlyphsTogether = false;
234    if (!s_renderer)
235    {
236        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
237
238        s_renderer = glGetString(GL_RENDERER);
239        OSG_INFO<<"glGetString(GL_RENDERER)=="<<s_renderer<<std::endl;
240        if (s_renderer && strstr((const char*)s_renderer,"IMPACT")!=0)
241        {
242            // we're running on an Octane, so need to work around its
243            // subloading bugs by loading all at once.
244            s_subloadAllGlyphsTogether = true;
245        }
246       
247        if (s_renderer &&
248            ((strstr((const char*)s_renderer,"Radeon")!=0) ||
249            (strstr((const char*)s_renderer,"RADEON")!=0) ||
250            (strstr((const char*)s_renderer,"ALL-IN-WONDER")!=0)))
251        {
252            // we're running on an ATI, so need to work around its
253            // subloading bugs by loading all at once.
254            s_subloadAllGlyphsTogether = true;
255        }
256
257        if (s_renderer && strstr((const char*)s_renderer,"Sun")!=0)
258        {
259            // we're running on an solaris x server, so need to work around its
260            // subloading bugs by loading all at once.
261            s_subloadAllGlyphsTogether = true;
262        }
263
264        const char* str = getenv("OSG_TEXT_INCREMENTAL_SUBLOADING");
265        if (str)
266        {
267            s_subloadAllGlyphsTogether = strcmp(str,"OFF")==0 || strcmp(str,"Off")==0 || strcmp(str,"off")==0;
268        }
269    }
270
271
272    // now subload the glyphs that are outstanding for this graphics context.
273    GlyphPtrList& glyphsWereSubloading = _glyphsToSubload[contextID];
274
275    if (!glyphsWereSubloading.empty() || newTextureObject)
276    {
277        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
278
279        if (!s_subloadAllGlyphsTogether)
280        {
281            if (newTextureObject)
282            {
283                for(GlyphRefList::const_iterator itr=_glyphs.begin();
284                    itr!=_glyphs.end();
285                    ++itr)
286                {
287                    (*itr)->subload();
288                }
289            }
290            else // just subload the new entries.
291            {           
292                // default way of subloading as required.
293                //std::cout<<"subloading"<<std::endl;
294                for(GlyphPtrList::iterator itr=glyphsWereSubloading.begin();
295                    itr!=glyphsWereSubloading.end();
296                    ++itr)
297                {
298                    (*itr)->subload();
299                }
300            }
301           
302            // clear the list since we have now subloaded them.
303            glyphsWereSubloading.clear();
304           
305        }
306        else
307        {
308            OSG_INFO<<"osgText::Font loading all glyphs as a single subload."<<std::endl;
309
310            // Octane has bugs in OGL driver which mean that subloads smaller
311            // than 32x32 produce errors, and also cannot handle general alignment,
312            // so to get round this copy all glyphs into a temporary image and
313            // then subload the whole lot in one go.
314
315            int tsize = getTextureHeight() * getTextureWidth();
316            unsigned char *local_data = new unsigned char[tsize];
317            memset( local_data, 0L, tsize);
318
319            for(GlyphRefList::const_iterator itr=_glyphs.begin();
320                itr!=_glyphs.end();
321                ++itr)
322            {
323                //(*itr)->subload();
324
325                // Rather than subloading to graphics, we'll write the values
326                // of the glyphs into some intermediate data and subload the
327                // whole thing at the end
328                for( int t = 0; t < (*itr)->t(); t++ )
329                {
330                    for( int s = 0; s < (*itr)->s(); s++ )
331                    {
332                        int sindex = (t*(*itr)->s()+s);
333                        int dindex = 
334                            ((((*itr)->getTexturePositionY()+t) * getTextureWidth()) +
335                            ((*itr)->getTexturePositionX()+s));
336
337                        const unsigned char *sptr = &(*itr)->data()[sindex];
338                        unsigned char *dptr       = &local_data[dindex];
339
340                        (*dptr)   = (*sptr);
341                    }
342                }
343            }
344
345            // clear the list since we have now subloaded them.
346            glyphsWereSubloading.clear();
347
348            // Subload the image once
349            glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
350                    getTextureWidth(),
351                    getTextureHeight(),
352                    GL_ALPHA, GL_UNSIGNED_BYTE, local_data );
353
354            delete [] local_data;
355
356        }
357    }
358    else
359    {
360//        OSG_INFO << "no need to subload "<<std::endl;
361    }
362
363
364
365//     if (generateMipMapTurnedOn)
366//     {
367//         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);
368//     }
369
370
371}
372
373void GlyphTexture::setThreadSafeRefUnref(bool threadSafe)
374{
375    osg::Texture2D::setThreadSafeRefUnref(threadSafe);
376}
377
378void GlyphTexture::resizeGLObjectBuffers(unsigned int maxSize)
379{
380    osg::Texture2D::resizeGLObjectBuffers(maxSize);
381    _glyphsToSubload.resize(maxSize);
382}
383
384
385// all the methods in Font::Glyph have been made non inline because VisualStudio6.0 is STUPID, STUPID, STUPID PILE OF JUNK.
386Glyph::Glyph(Font* font, unsigned int glyphCode):
387    _font(font),
388    _glyphCode(glyphCode),
389    _horizontalBearing(0.0f,0.f),
390    _horizontalAdvance(0.f),
391    _verticalBearing(0.0f,0.f),
392    _verticalAdvance(0.f),
393    _texture(0),
394    _texturePosX(0),
395    _texturePosY(0),
396    _minTexCoord(0.0f,0.0f),
397    _maxTexCoord(0.0f,0.0f)
398{
399    setThreadSafeRefUnref(true);
400}
401
402Glyph::~Glyph()
403{
404}
405
406void Glyph::setHorizontalBearing(const osg::Vec2& bearing) {  _horizontalBearing=bearing; }
407const osg::Vec2& Glyph::getHorizontalBearing() const { return _horizontalBearing; }
408
409void Glyph::setHorizontalAdvance(float advance) { _horizontalAdvance=advance; }
410float Glyph::getHorizontalAdvance() const { return _horizontalAdvance; }
411
412void Glyph::setVerticalBearing(const osg::Vec2& bearing) {  _verticalBearing=bearing; }
413const osg::Vec2& Glyph::getVerticalBearing() const { return _verticalBearing; }
414
415void Glyph::setVerticalAdvance(float advance) {  _verticalAdvance=advance; }
416float Glyph::getVerticalAdvance() const { return _verticalAdvance; }
417
418void Glyph::setTexture(GlyphTexture* texture) { _texture = texture; }
419GlyphTexture* Glyph::getTexture() { return _texture; }
420const GlyphTexture* Glyph::getTexture() const { return _texture; }
421
422void Glyph::setTexturePosition(int posX,int posY) { _texturePosX = posX; _texturePosY = posY; }
423int Glyph::getTexturePositionX() const { return _texturePosX; }
424int Glyph::getTexturePositionY() const { return _texturePosY; }
425
426void Glyph::setMinTexCoord(const osg::Vec2& coord) { _minTexCoord=coord; }
427const osg::Vec2& Glyph::getMinTexCoord() const { return _minTexCoord; }
428
429void Glyph::setMaxTexCoord(const osg::Vec2& coord) { _maxTexCoord=coord; }
430const osg::Vec2& Glyph::getMaxTexCoord() const { return _maxTexCoord; }
431
432void Glyph::subload() const
433{
434    GLenum errorNo = glGetError();
435    if (errorNo!=GL_NO_ERROR)
436    {
437        const GLubyte* msg = gluErrorString(errorNo);
438        if (msg) { OSG_WARN<<"before Glyph::subload(): detected OpenGL error: "<<msg<<std::endl; }
439        else  { OSG_WARN<<"before Glyph::subload(): detected OpenGL error number: "<<errorNo<<std::endl; }
440    }
441
442    if(s() <= 0 || t() <= 0)
443    {
444        OSG_INFO<<"Glyph::subload(): texture sub-image width and/or height of 0, ignoring operation."<<std::endl;
445        return;
446    }
447
448    glPixelStorei(GL_UNPACK_ALIGNMENT,getPacking());
449
450    glTexSubImage2D(GL_TEXTURE_2D,0,
451                    _texturePosX,_texturePosY,
452                    s(),t(),
453                    (GLenum)getPixelFormat(),
454                    (GLenum)getDataType(),
455                    data());
456                   
457    errorNo = glGetError();
458    if (errorNo!=GL_NO_ERROR)
459    {
460
461
462        const GLubyte* msg = gluErrorString(errorNo);
463        if (msg) { OSG_WARN<<"after Glyph::subload() : detected OpenGL error: "<<msg<<std::endl; }
464        else { OSG_WARN<<"after Glyph::subload() : detected OpenGL error number: "<<errorNo<<std::endl; }
465
466        OSG_WARN<< "\tglTexSubImage2D(0x"<<hex<<GL_TEXTURE_2D<<dec<<" ,"<<0<<"\t"<<std::endl<<
467                                 "\t                "<<_texturePosX<<" ,"<<_texturePosY<<std::endl<<
468                                 "\t                "<<s()<<" ,"<<t()<<std::endl<<hex<<
469                                 "\t                0x"<<(GLenum)getPixelFormat()<<std::endl<<
470                                 "\t                0x"<<(GLenum)getDataType()<<std::endl<<
471                                 "\t                0x"<<(unsigned long)data()<<");"<<dec<<std::endl;
472    }                   
473}
474
475Glyph3D::Glyph3D(Font* font, unsigned int glyphCode):
476    osg::Referenced(true),
477    _font(font),
478    _glyphCode(glyphCode),
479    _horizontalBearing(0,0),
480    _horizontalAdvance(0),
481    _verticalBearing(0,0),
482    _verticalAdvance(0)
483    {}
484
485void Glyph3D::setThreadSafeRefUnref(bool threadSafe)
486{
487    GlyphGeometries _glyphGeometries;
488    for(GlyphGeometries::iterator itr = _glyphGeometries.begin();
489        itr != _glyphGeometries.end();
490        ++itr)
491    {
492        (*itr)->setThreadSafeRefUnref(threadSafe);
493    }
494}
495
496GlyphGeometry* Glyph3D::getGlyphGeometry(const Style* style)
497{
498
499    for(GlyphGeometries::iterator itr = _glyphGeometries.begin();
500        itr != _glyphGeometries.end();
501        ++itr)
502    {
503        GlyphGeometry* glyphGeometry = itr->get();
504        if (glyphGeometry->match(style))
505        {
506            OSG_INFO<<"Glyph3D::getGlyphGeometry(Style* style) found matching GlyphGeometry."<<std::endl;
507            return glyphGeometry;
508        }
509    }
510
511    OSG_INFO<<"Glyph3D::getGlyphGeometry(Style* style) could not find matching GlyphGeometry, creating a new one."<<std::endl;
512
513    osg::ref_ptr<GlyphGeometry> glyphGeometry = new GlyphGeometry();
514    glyphGeometry->setup(this, style);
515    _glyphGeometries.push_back(glyphGeometry);
516
517    return glyphGeometry.get();
518}
519
520
521GlyphGeometry::GlyphGeometry()
522{
523}
524
525void GlyphGeometry::setThreadSafeRefUnref(bool threadSafe)
526{
527    if (_geode.valid()) _geode->setThreadSafeRefUnref(threadSafe);
528}
529
530void GlyphGeometry::setup(const Glyph3D* glyph, const Style* style)
531{
532    float creaseAngle = 30.0f;
533    bool smooth = true;
534    osg::ref_ptr<osg::Geometry> shellGeometry;
535
536    if (!style)
537    {
538        OSG_INFO<<"GlyphGeometry::setup(const Glyph* glyph, NULL) creating default glyph geometry."<<std::endl;
539
540        float width = 0.1f;
541
542        _geometry = osgText::computeTextGeometry(glyph, width);
543    }
544    else
545    {
546        OSG_INFO<<"GlyphGeometry::setup(const Glyph* glyph, NULL) create glyph geometry with custom Style."<<std::endl;
547
548        // record the style
549        _style = dynamic_cast<Style*>(style->clone(osg::CopyOp::DEEP_COPY_ALL));
550
551        const Bevel* bevel = style ? style->getBevel() : 0;
552        bool outline = style ? style->getOutlineRatio()>0.0f : false;
553        float width = style->getThicknessRatio();
554
555        if (bevel)
556        {
557            float thickness = bevel->getBevelThickness();
558
559            osg::ref_ptr<osg::Geometry> glyphGeometry = osgText::computeGlyphGeometry(glyph, thickness, width);
560
561            _geometry = osgText::computeTextGeometry(glyphGeometry.get(), *bevel, width);
562            shellGeometry = outline ? osgText::computeShellGeometry(glyphGeometry.get(), *bevel, width) : 0;
563        }
564        else
565        {
566            _geometry = osgText::computeTextGeometry(glyph, width);
567        }
568    }
569
570    if (!_geometry)
571    {
572        OSG_INFO<<"Warning: GlyphGeometry::setup(const Glyph* glyph, const Style* style) failed."<<std::endl;
573        return;
574    }
575
576    _geode = new osg::Geode;
577    _geode->addDrawable(_geometry.get());
578    if (shellGeometry.valid()) _geode->addDrawable(shellGeometry.get());
579
580    // create the normals
581    if (smooth)
582    {
583        osgUtil::SmoothingVisitor::smooth(*_geometry, osg::DegreesToRadians(creaseAngle));
584    }
585
586    _vertices = dynamic_cast<osg::Vec3Array*>(_geometry->getVertexArray());
587    _normals = dynamic_cast<osg::Vec3Array*>(_geometry->getNormalArray());
588
589    for(osg::Geometry::PrimitiveSetList::iterator itr = _geometry->getPrimitiveSetList().begin();
590        itr != _geometry->getPrimitiveSetList().end();
591        ++itr)
592    {
593        osg::PrimitiveSet* prim = itr->get();
594        if (prim->getName()=="front") _frontPrimitiveSetList.push_back(prim);
595        else if (prim->getName()=="back") _backPrimitiveSetList.push_back(prim);
596        else if (prim->getName()=="wall") _wallPrimitiveSetList.push_back(prim);
597    }
598}
599
600bool GlyphGeometry::match(const Style* style) const
601{
602    if (_style == style) return true;
603    if (!_style || !style) return false;
604
605    return (*_style==*style);
606}
Note: See TracBrowser for help on using the browser.