/* -*-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; // GL_ALPHA and GL_LUMINANCE_ALPHA are deprecated in GL3/GL4 core profile, use GL_RED & GL_RB in this case. #if defined(OSG_GL3_AVAILABLE) && !defined(OSG_GL2_AVAILABLE) && !defined(OSG_GL1_AVAILABLE) #define OSGTEXT_GLYPH_ALPHA_FORMAT GL_RED #define OSGTEXT_GLYPH_ALPHA_INTERNALFORMAT GL_R8 #define OSGTEXT_GLYPH_SDF_FORMAT GL_RG #define OSGTEXT_GLYPH_SDF_INTERNALFORMAT GL_RG8 #else #define OSGTEXT_GLYPH_ALPHA_FORMAT GL_ALPHA #define OSGTEXT_GLYPH_ALPHA_INTERNALFORMAT GL_ALPHA #define OSGTEXT_GLYPH_SDF_FORMAT GL_LUMINANCE_ALPHA #define OSGTEXT_GLYPH_SDF_INTERNALFORMAT GL_LUMINANCE_ALPHA #endif #if 0 #define TEXTURE_IMAGE_NUM_CHANNELS 1 #define TEXTURE_IMAGE_FORMAT OSGTEXT_GLYPH_FORMAT #else #define TEXTURE_IMAGE_NUM_CHANNELS 2 #define TEXTURE_IMAGE_FORMAT GL_RGBA #endif ////////////////////////////////////////////////////////////////////////////////////////////////////////// // // GlyphTexture // GlyphTexture::GlyphTexture(): _shaderTechnique(GREYSCALE), _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; } int GlyphTexture::getEffectMargin(const Glyph* glyph) { if (_shaderTechnique==GREYSCALE) return 0; else return osg::maximum(glyph->getFontResolution().second/6, 2u); } int GlyphTexture::getTexelMargin(const Glyph* glyph) { int width = glyph->s(); int height = glyph->t(); int effect_margin = getEffectMargin(glyph); int max_dimension = osg::maximum(width, height) + 2 * effect_margin; int margin = osg::maximum(max_dimension/4, 2) + effect_margin; return margin; } bool GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY) { int width = glyph->s(); int height = glyph->t(); int margin = getTexelMargin(glyph); width += 2*margin; height += 2*margin; int interval = 4; int partUsedX = ((_partUsedX % interval) == 0) ? _partUsedX : (((_partUsedX/interval)+1)*interval); int partUsedY = ((_partUsedY % interval) == 0) ? _partUsedY : (((_partUsedY/interval)+1)*interval); int usedY = ((_usedY % interval) == 0) ? _usedY : (((_usedY/interval)+1)*interval); // 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 = posX+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 = posX+width; _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); if (!_image.valid()) createImage(); _glyphs.push_back(glyph); osg::ref_ptr info = new Glyph::TextureInfo( this, posX, posY, osg::Vec2( static_cast(posX)/static_cast(getTextureWidth()), static_cast(posY)/static_cast(getTextureHeight()) ), // minTexCoord osg::Vec2( static_cast(posX+glyph->s())/static_cast(getTextureWidth()), static_cast(posY+glyph->t())/static_cast(getTextureHeight()) ), // maxTexCoord float(getTexelMargin(glyph))); // margin glyph->setTextureInfo(_shaderTechnique, info.get()); copyGlyphImage(glyph, info.get()); } void GlyphTexture::copyGlyphImage(Glyph* glyph, Glyph::TextureInfo* info) { _image->dirty(); if (_shaderTechnique<=GREYSCALE) { // OSG_NOTICE<<"GlyphTexture::copyGlyphImage() greyscale copying. glyphTexture="<allocateImage(getTextureWidth(), getTextureHeight(), 1, imageFormat, GL_UNSIGNED_BYTE); _image->setInternalTextureFormat(internalFormat); memset(_image->data(), 0, _image->getTotalSizeInBytes()); } return _image.get(); } ////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Glyph // // all the methods in Font::Glyph have been made non inline because VisualStudio6.0 is STUPID, STUPID, STUPID PILE OF JUNK. Glyph::Glyph(Font* font, unsigned int glyphCode): _font(font), _glyphCode(glyphCode), _width(1.0f), _height(1.0f), _horizontalBearing(0.0f,0.f), _horizontalAdvance(0.f), _verticalBearing(0.0f,0.f), _verticalAdvance(0.f) { setThreadSafeRefUnref(true); } Glyph::~Glyph() { } void Glyph::setHorizontalBearing(const osg::Vec2& bearing) { _horizontalBearing=bearing; } const osg::Vec2& Glyph::getHorizontalBearing() const { return _horizontalBearing; } void Glyph::setHorizontalAdvance(float advance) { _horizontalAdvance=advance; } float Glyph::getHorizontalAdvance() const { return _horizontalAdvance; } void Glyph::setVerticalBearing(const osg::Vec2& bearing) { _verticalBearing=bearing; } const osg::Vec2& Glyph::getVerticalBearing() const { return _verticalBearing; } void Glyph::setVerticalAdvance(float advance) { _verticalAdvance=advance; } float Glyph::getVerticalAdvance() const { return _verticalAdvance; } void Glyph::setTextureInfo(ShaderTechnique technique, TextureInfo* info) { if (technique>=_textureInfoList.size()) { _textureInfoList.resize(technique+1); } _textureInfoList[technique] = info; } const Glyph::TextureInfo* Glyph::getTextureInfo(ShaderTechnique technique) const { return (technique<_textureInfoList.size()) ? _textureInfoList[technique].get() : 0; } Glyph::TextureInfo* Glyph::getOrCreateTextureInfo(ShaderTechnique technique) { if (technique>=_textureInfoList.size()) { _textureInfoList.resize(technique+1); } if (!_textureInfoList[technique]) { _font->assignGlyphToGlyphTexture(this, technique); } return _textureInfoList[technique].get(); } ////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Glyph3D // Glyph3D::Glyph3D(Font* font, unsigned int glyphCode): osg::Referenced(true), _font(font), _glyphCode(glyphCode), _width(1.0f), _height(1.0f), _horizontalBearing(0,0), _horizontalAdvance(0), _verticalBearing(0,0), _verticalAdvance(0) {} void Glyph3D::setThreadSafeRefUnref(bool threadSafe) { for(GlyphGeometries::iterator itr = _glyphGeometries.begin(); itr != _glyphGeometries.end(); ++itr) { (*itr)->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."<getBevel(); bool outline = style->getOutlineRatio()>0.0f; float width = style->getThicknessRatio(); if (bevel) { osg::ref_ptr glyphGeometry = osgText::computeGlyphGeometry(glyph, *bevel, 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); }