/* -*-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 #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); } } GlyphGeometry* Glyph3D::getGlyphGeometry(const Style* style) { for(GlyphGeometries::iterator itr = _glyphGeometries.begin(); itr != _glyphGeometries.end(); ++itr) { GlyphGeometry* glyphGeometry = itr->get(); if (glyphGeometry->match(style)) { OSG_INFO<<"Glyph3D::getGlyphGeometry(Style* style) found matching GlyphGeometry."< glyphGeometry = new GlyphGeometry(); glyphGeometry->setup(this, style); _glyphGeometries.push_back(glyphGeometry); return glyphGeometry.get(); } GlyphGeometry::GlyphGeometry() { } void GlyphGeometry::setThreadSafeRefUnref(bool threadSafe) { if (_geode.valid()) _geode->setThreadSafeRefUnref(threadSafe); } void GlyphGeometry::setup(const Glyph3D* glyph, const Style* style) { float creaseAngle = 30.0f; bool smooth = true; osg::ref_ptr shellGeometry; if (!style) { OSG_INFO<<"GlyphGeometry::setup(const Glyph* glyph, NULL) creating default glyph geometry."<(style->clone(osg::CopyOp::DEEP_COPY_ALL)); const Bevel* bevel = style ? style->getBevel() : 0; bool outline = style ? style->getOutlineRatio()>0.0f : false; float width = style->getThicknessRatio(); if (bevel) { float thickness = bevel->getBevelThickness(); osg::ref_ptr glyphGeometry = osgText::computeGlyphGeometry(glyph, thickness, width); _geometry = osgText::computeTextGeometry(glyphGeometry.get(), *bevel, width); shellGeometry = outline ? osgText::computeShellGeometry(glyphGeometry.get(), *bevel, width) : 0; } else { _geometry = osgText::computeTextGeometry(glyph, width); } } if (!_geometry) { OSG_INFO<<"Warning: GlyphGeometry::setup(const Glyph* glyph, const Style* style) failed."<addDrawable(_geometry.get()); if (shellGeometry.valid()) _geode->addDrawable(shellGeometry.get()); // create the normals if (smooth) { osgUtil::SmoothingVisitor::smooth(*_geometry, osg::DegreesToRadians(creaseAngle)); } _vertices = dynamic_cast(_geometry->getVertexArray()); _normals = dynamic_cast(_geometry->getNormalArray()); for(osg::Geometry::PrimitiveSetList::iterator itr = _geometry->getPrimitiveSetList().begin(); itr != _geometry->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); } } bool GlyphGeometry::match(const Style* style) const { if (_style == style) return true; if (!_style || !style) return false; return (*_style==*style); }