diff --git a/include/osgText/Glyph b/include/osgText/Glyph new file mode 100644 index 000000000..c52d31ea1 --- /dev/null +++ b/include/osgText/Glyph @@ -0,0 +1,256 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSGTEXT_GLYPH +#define OSGTEXT_GLYPH 1 + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace osgText { + +class Font; +class Text; +class Glyph3D; +class GlyphTexture; + +class OSGTEXT_EXPORT Glyph : public osg::Image +{ +public: + + Glyph(Font* font, unsigned int glyphCode); + + unsigned int getGlyphCode() const { return _glyphCode; } + + void setHorizontalBearing(const osg::Vec2& bearing); + const osg::Vec2& getHorizontalBearing() const; + + void setHorizontalAdvance(float advance); + float getHorizontalAdvance() const; + + void setVerticalBearing(const osg::Vec2& bearing); + const osg::Vec2& getVerticalBearing() const; + + void setVerticalAdvance(float advance); + float getVerticalAdvance() const; + + void setTexture(GlyphTexture* texture); + GlyphTexture* getTexture(); + const GlyphTexture* getTexture() const; + + void setTexturePosition(int posX,int posY); + int getTexturePositionX() const; + int getTexturePositionY() const; + + void setMinTexCoord(const osg::Vec2& coord); + const osg::Vec2& getMinTexCoord() const; + + void setMaxTexCoord(const osg::Vec2& coord); + const osg::Vec2& getMaxTexCoord() const; + + void subload() const; + +protected: + + virtual ~Glyph(); + + Font* _font; + unsigned int _glyphCode; + + osg::Vec2 _horizontalBearing; + float _horizontalAdvance; + + osg::Vec2 _verticalBearing; + float _verticalAdvance; + + GlyphTexture* _texture; + int _texturePosX; + int _texturePosY; + osg::Vec2 _minTexCoord; + osg::Vec2 _maxTexCoord; + + typedef osg::buffered_value GLObjectList; + mutable GLObjectList _globjList; + +}; + +class OSGTEXT_EXPORT Glyph3D : public osg::Referenced +{ +public: + + Glyph3D(Font* font, unsigned int glyphCode); + + unsigned int getGlyphCode() const { return _glyphCode; } + + void setHorizontalBearing(const osg::Vec2& bearing) { _horizontalBearing=bearing; } + const osg::Vec2 & getHorizontalBearing() const { return _horizontalBearing; } + + void setHorizontalAdvance(float advance) { _horizontalAdvance=advance; } + float getHorizontalAdvance() const { return _horizontalAdvance; } + + void setVerticalBearing(const osg::Vec2& bearing) { _verticalBearing=bearing; } + const osg::Vec2& getVerticalBearing() const { return _verticalBearing; } + + void setVerticalAdvance(float advance) { _verticalAdvance=advance; } + float getVerticalAdvance() const { return _verticalAdvance; } + + void setBoundingBox(osg::BoundingBox & bb) { _bb=bb; } + const osg::BoundingBox & getBoundingBox() const { return _bb; } + + + /** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/ + virtual void setThreadSafeRefUnref(bool threadSafe); + + + void setRawVertexArray(osg::Vec3Array* vertices) { _rawVertexArray = vertices; } + osg::Vec3Array* getRawVertexArray() { return _rawVertexArray.get(); } + + /** Get the PrimitiveSetList for the raw face which hasn't been tessellated. */ + osg::Geometry::PrimitiveSetList & getRawFacePrimitiveSetList() { return _rawFacePrimitiveSetList; } + + + /** deprecated feature.*/ + void computeText3DGeometryData(); + + /** Get the PrimitiveSetList for the front face. */ + osg::Geometry::PrimitiveSetList & getFrontPrimitiveSetList() { return _frontPrimitiveSetList; } + /** Get the PrimitiveSetList for the wall face. */ + osg::Geometry::PrimitiveSetList & getWallPrimitiveSetList() { return _wallPrimitiveSetList; } + /** Get et the PrimitiveSetList for the back face. */ + osg::Geometry::PrimitiveSetList & getBackPrimitiveSetList() { return _backPrimitiveSetList; } + + /** Set the VertexArray of the glyph. */ + void setVertexArray(osg::Vec3Array * va) { _vertexArray = va; } + /** Get the VertexArray of the glyph. */ + osg::Vec3Array * getVertexArray() { return _vertexArray.get(); } + /** Set the VertexArray of the glyph. */ + void setNormalArray(osg::Vec3Array * na) { _normalArray = na; } + /** Get the NormalArray for the wall face. */ + osg::Vec3Array * getNormalArray() { return _normalArray.get(); } + + + float getHorizontalWidth() { return (-_horizontalBearing.x() + _horizontalAdvance); } + float getHorizontalHeight() { return (-_horizontalBearing.y() + _bb.yMax()); } + float getVerticalWidth() { return (-_verticalBearing.x() + _bb.xMax()); } + float getVerticalHeight() { return (-_verticalBearing.y() + _verticalAdvance); } + + void setWidth(float width) { _width = width; } + float getWidth() { return _width; } + + void setHeight(float height) { _height = height; } + float getHeight() { return _height; } + + +protected: + + virtual ~Glyph3D() {} + + Font* _font; + unsigned int _glyphCode; + + osg::Vec2 _horizontalBearing; + float _horizontalAdvance; + + osg::Vec2 _verticalBearing; + float _verticalAdvance; + + osg::BoundingBox _bb; +// osg::Vec2 _advance; + + float _width; + float _height; + + + osg::ref_ptr _vertexArray; + osg::ref_ptr _normalArray; + + + osg::Geometry::PrimitiveSetList _frontPrimitiveSetList; + osg::Geometry::PrimitiveSetList _wallPrimitiveSetList; + osg::Geometry::PrimitiveSetList _backPrimitiveSetList; + + osg::ref_ptr _rawVertexArray; + osg::Geometry::PrimitiveSetList _rawFacePrimitiveSetList; + +}; + + +class OSGTEXT_EXPORT GlyphTexture : public osg::Texture2D +{ +public: + + GlyphTexture(); + + const char* className() const { return "GlyphTexture"; } + + /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/ + virtual int compare(const osg::StateAttribute& rhs) const; + + /** Set the margin around each glyph, to ensure that texture filtering doesn't bleed adjacent glyph's into each other.*/ + void setGlyphImageMargin(unsigned int margin) { _margin = margin; } + unsigned int getGlyphImageMargin() const { return _margin; } + + void setGlyphImageMarginRatio(float margin) { _marginRatio = margin; } + float getGlyphImageMarginRatio() const { return _marginRatio; } + + bool getSpaceForGlyph(Glyph* glyph, int& posX, int& posY); + + void addGlyph(Glyph* glyph,int posX, int posY); + + virtual void apply(osg::State& state) const; + + /** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/ + virtual void setThreadSafeRefUnref(bool threadSafe); + + /** Resize any per context GLObject buffers to specified size. */ + virtual void resizeGLObjectBuffers(unsigned int maxSize); + +protected: + + virtual ~GlyphTexture(); + + + // parameter used to compute the size and position of empty space + // in the texture which could accommodate new glyphs. + int _margin; + float _marginRatio; + int _usedY; + int _partUsedX; + int _partUsedY; + + typedef std::vector< osg::ref_ptr > GlyphRefList; + typedef std::vector< const Glyph* > GlyphPtrList; + typedef osg::buffered_object< GlyphPtrList > GlyphBuffer; + + GlyphRefList _glyphs; + mutable GlyphBuffer _glyphsToSubload; + + mutable OpenThreads::Mutex _mutex; + +}; + +} + + +#endif diff --git a/src/osgText/Glyph.cpp b/src/osgText/Glyph.cpp new file mode 100644 index 000000000..75b188be7 --- /dev/null +++ b/src/osgText/Glyph.cpp @@ -0,0 +1,534 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#include +#include + +#include +#include +#include + +#include + +#include + +#include "GlyphGeometry.h" + +using namespace osgText; +using namespace std; + +GlyphTexture::GlyphTexture(): + _margin(1), + _marginRatio(0.02f), + _usedY(0), + _partUsedX(0), + _partUsedY(0) +{ + setWrap(WRAP_S, CLAMP_TO_EDGE); + setWrap(WRAP_T, CLAMP_TO_EDGE); +} + +GlyphTexture::~GlyphTexture() +{ +} + +// return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. +int GlyphTexture::compare(const osg::StateAttribute& rhs) const +{ + if (this<&rhs) return -1; + else if (this>&rhs) return 1; + return 0; +} + + +bool GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY) +{ + int maxAxis = std::max(glyph->s(), glyph->t()); + int margin = _margin + (int)((float)maxAxis * _marginRatio); + + int width = glyph->s()+2*margin; + int height = glyph->t()+2*margin; + + // first check box (_partUsedX,_usedY) to (width,height) + if (width <= (getTextureWidth()-_partUsedX) && + height <= (getTextureHeight()-_usedY)) + { + // can fit in existing row. + + // record the position in which the texture will be stored. + posX = _partUsedX+margin; + posY = _usedY+margin; + + // move used markers on. + _partUsedX += width; + if (_usedY+height>_partUsedY) _partUsedY = _usedY+height; + + return true; + } + + // start an new row. + if (width <= getTextureWidth() && + height <= (getTextureHeight()-_partUsedY)) + { + // can fit next row. + _partUsedX = 0; + _usedY = _partUsedY; + + posX = _partUsedX+margin; + posY = _usedY+margin; + + // move used markers on. + _partUsedX += width; + if (_usedY+height>_partUsedY) _partUsedY = _usedY+height; + + return true; + } + + // doesn't fit into glyph. + return false; +} + +void GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY) +{ + OpenThreads::ScopedLock lock(_mutex); + + _glyphs.push_back(glyph); + for(unsigned int i=0;i<_glyphsToSubload.size();++i) + { + _glyphsToSubload[i].push_back(glyph); + } + + // set up the details of where to place glyph's image in the texture. + glyph->setTexture(this); + glyph->setTexturePosition(posX,posY); + + glyph->setMinTexCoord( osg::Vec2( static_cast(posX)/static_cast(getTextureWidth()), + static_cast(posY)/static_cast(getTextureHeight()) ) ); + glyph->setMaxTexCoord( osg::Vec2( static_cast(posX+glyph->s())/static_cast(getTextureWidth()), + static_cast(posY+glyph->t())/static_cast(getTextureHeight()) ) ); +} + +void GlyphTexture::apply(osg::State& state) const +{ + // get the contextID (user defined ID of 0 upwards) for the + // current OpenGL context. + const unsigned int contextID = state.getContextID(); + + if (contextID>=_glyphsToSubload.size()) + { + OpenThreads::ScopedLock lock(_mutex); + + // graphics context is beyond the number of glyphsToSubloads, so + // we must now copy the glyph list across, this is a potential + // threading issue though is multiple applies are happening the + // same time on this object - to avoid this condition number of + // graphics contexts should be set before create text. + for(unsigned int i=_glyphsToSubload.size();i<=contextID;++i) + { + GlyphPtrList& glyphPtrs = _glyphsToSubload[i]; + for(GlyphRefList::const_iterator itr=_glyphs.begin(); + itr!=_glyphs.end(); + ++itr) + { + glyphPtrs.push_back(itr->get()); + } + } + } + + + const Extensions* extensions = getExtensions(contextID,true); + bool generateMipMapSupported = extensions->isGenerateMipMapSupported(); + + // get the texture object for the current contextID. + TextureObject* textureObject = getTextureObject(contextID); + + bool newTextureObject = (textureObject == 0); + + if (newTextureObject) + { + GLint maxTextureSize = 256; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + if (maxTextureSize < getTextureWidth() || maxTextureSize < getTextureHeight()) + { + OSG_WARN<<"Warning: osgText::Font texture size of ("<bind(); + + + applyTexParameters(GL_TEXTURE_2D,state); + + + // need to look at generate mip map extension if mip mapping required. + switch(_min_filter) + { + case NEAREST_MIPMAP_NEAREST: + case NEAREST_MIPMAP_LINEAR: + case LINEAR_MIPMAP_NEAREST: + case LINEAR_MIPMAP_LINEAR: + if (generateMipMapSupported) + { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE); + } + else glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, LINEAR); + break; + default: + // not mip mapping so no problems. + break; + } + + unsigned int imageDataSize = getTextureHeight()*getTextureWidth(); + unsigned char* imageData = new unsigned char[imageDataSize]; + for(unsigned int i=0; i lock(_mutex); + + if (!s_subloadAllGlyphsTogether) + { + if (newTextureObject) + { + for(GlyphRefList::const_iterator itr=_glyphs.begin(); + itr!=_glyphs.end(); + ++itr) + { + (*itr)->subload(); + } + } + else // just subload the new entries. + { + // default way of subloading as required. + //std::cout<<"subloading"<subload(); + } + } + + // clear the list since we have now subloaded them. + glyphsWereSubloading.clear(); + + } + else + { + OSG_INFO<<"osgText::Font loading all glyphs as a single subload."<subload(); + + // Rather than subloading to graphics, we'll write the values + // of the glyphs into some intermediate data and subload the + // whole thing at the end + for( int t = 0; t < (*itr)->t(); t++ ) + { + for( int s = 0; s < (*itr)->s(); s++ ) + { + int sindex = (t*(*itr)->s()+s); + int dindex = + ((((*itr)->getTexturePositionY()+t) * getTextureWidth()) + + ((*itr)->getTexturePositionX()+s)); + + const unsigned char *sptr = &(*itr)->data()[sindex]; + unsigned char *dptr = &local_data[dindex]; + + (*dptr) = (*sptr); + } + } + } + + // clear the list since we have now subloaded them. + glyphsWereSubloading.clear(); + + // Subload the image once + glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, + getTextureWidth(), + getTextureHeight(), + GL_ALPHA, GL_UNSIGNED_BYTE, local_data ); + + delete [] local_data; + + } + } + else + { +// OSG_INFO << "no need to subload "<setThreadSafeRefUnref(threadSafe); + if (_normalArray.valid()) _normalArray->setThreadSafeRefUnref(threadSafe); +} + +void Glyph3D::computeText3DGeometryData() +{ + OSG_NOTICE<<"Glyph3D::computeText3DGeometryData()"<getFontDepth(); + bool smooth = true; + + osg::ref_ptr textGeometry = osgText::computeTextGeometry(this, width); + if (!textGeometry) return; + + // create the normals + if (smooth && textGeometry.valid()) + { + osgUtil::SmoothingVisitor::smooth(*textGeometry); + } + + _vertexArray = dynamic_cast(textGeometry->getVertexArray()); + _normalArray = dynamic_cast(textGeometry->getNormalArray()); + + for(osg::Geometry::PrimitiveSetList::iterator itr = textGeometry->getPrimitiveSetList().begin(); + itr != textGeometry->getPrimitiveSetList().end(); + ++itr) + { + osg::PrimitiveSet* prim = itr->get(); + if (prim->getName()=="front") _frontPrimitiveSetList.push_back(prim); + else if (prim->getName()=="back") _backPrimitiveSetList.push_back(prim); + else if (prim->getName()=="wall") _wallPrimitiveSetList.push_back(prim); + } + + OSG_NOTICE<<" _frontPrimitiveSetList.size()=="<<_frontPrimitiveSetList.size()<