/* -*-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_TEXT #define OSGTEXT_TEXT 1 #include #include #include #include namespace osgText { class OSGTEXT_EXPORT Text : public osg::Drawable { public: Text(); Text(const Text& text,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY); virtual osg::Object* cloneType() const { return new Text(); } virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new Text(*this,copyop); } virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } virtual const char* className() const { return "Text"; } virtual const char* libraryName() const { return "osgText"; } /** Set the Font to use to render the text. * setFont(0) sets the use of the default font.*/ void setFont(Font* font=0); /** Set the font, loaded from the specified front file, to use to render the text, * setFont("") sets the use of the default font. * See the osgText::readFontFile function for how the font file will be located. */ void setFont(const std::string& fontfile); /** Get the font. Return 0 if default is being used.*/ const Font* getFont() const { return _font.get(); } /** Set the Font reference width and height resolution in texels. * Note, the size may not be supported by current font, * the closest supported font size will be selected.*/ void setFontResolution(unsigned int width, unsigned int height); unsigned int getFontWidth() const { return _fontWidth; } unsigned int getFontHeight() const { return _fontHeight; } /** Set the text using a osgText::String.*/ void setText(const String& text); /** Set the text using a std::string, * which is converted to an internal TextString.*/ void setText(const std::string& text); /** Set the text using a Unicode encoded std::string, which is converted to an internal TextString. * The encoding parameter specificies which Unicode encodeding is used in the std::string. */ void setText(const std::string& text,String::Encoding encoding); /** Set the text using a wchar_t string, * which is converted to an internal TextString.*/ void setText(const wchar_t* text); /** Get the text string. * Note, if you modify the string you must call Text::update() for * the internal glyph reprentation to be updated.*/ String& getText() { return _text; } /** Get the const text string.*/ const String& getText() const { return _text; } /** update internal glyph respresnetation used for rendering, * and bounding volume.*/ void update() { computeGlyphRepresentation(); } /** Set the rendered character size in object coordinates.*/ void setCharacterSize(float height,float aspectRatio=1.0f); float getCharacterHeight() const { return _characterHeight; } float getCharacterAspectRatio() const { return _characterAspectRatio; } enum CharacterSizeMode { OBJECT_COORDS, /// default SCREEN_COORDS, /// internally scale the characters to be constant screen size. OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT /// text that behavaves like OBJECT_COORDS sized text when a long distance way, but has its screen sized capped automatically when the viewer gets near. }; /** Set how the CharacterSize value relates to the final rendered character.*/ void setCharacterSizeMode(CharacterSizeMode mode) { _characterSizeMode = mode; } /** Get the CharacterSizeMode.*/ CharacterSizeMode getCharacterSizeMode() const { return _characterSizeMode; } /** Set the maximum width of the text box. * With horizontal layouts any characters which do not fit are wrapped around. * 0 or negative values indicate that no maximum width is set, lines can be as long as * they need be to fit thre required text*/ void setMaximumWidth(float maximumWidth); /** Get the maximim width of the text box.*/ float getMaximumWidth() const { return _maximumWidth; } /** Set the maximum height of the text box. * With horizontal layouts any characters which do not fit are wrapped around. * 0 or negative values indicate that no maximum height is set, lines can be as long as * they need be to fit the required text*/ void setMaximumHeight(float maximumHeight); /** Get the maximum height of the text box.*/ float getMaximumHeight() const { return _maximumHeight; } /** Set the line spacing of the text box, given as a percentage of * the character height. The default value is 0 for backward * compatibility. For longer paragraphs of text, a value of at * least 25% (i.e. set line spacing to 0.25) is recommended. */ void setLineSpacing(float lineSpacing); /** Get the line spacing of the text box. */ float getLineSpacing() const { return _lineSpacing; } /** Set the position of text.*/ void setPosition(const osg::Vec3& pos); /** Get the position of text.*/ const osg::Vec3& getPosition() const { return _position; } enum AlignmentType { LEFT_TOP, LEFT_CENTER, LEFT_BOTTOM, CENTER_TOP, CENTER_CENTER, CENTER_BOTTOM, RIGHT_TOP, RIGHT_CENTER, RIGHT_BOTTOM, LEFT_BASE_LINE, CENTER_BASE_LINE, RIGHT_BASE_LINE, LEFT_BOTTOM_BASE_LINE, CENTER_BOTTOM_BASE_LINE, RIGHT_BOTTOM_BASE_LINE, BASE_LINE = LEFT_BASE_LINE /// default. }; void setAlignment(AlignmentType alignment); AlignmentType getAlignment() const { return _alignment; } enum AxisAlignment { XY_PLANE, REVERSED_XY_PLANE, XZ_PLANE, REVERSED_XZ_PLANE, YZ_PLANE, REVERSED_YZ_PLANE, SCREEN }; void setAxisAlignment(AxisAlignment axis); void setRotation(const osg::Quat& quat); const osg::Quat& getRotation() const { return _rotation; } void setAutoRotateToScreen(bool autoRotateToScreen); bool getAutoRotateToScreen() const { return _autoRotateToScreen; } enum Layout { LEFT_TO_RIGHT, /// default RIGHT_TO_LEFT, VERTICAL }; void setLayout(Layout layout); Layout getLayout() const { return _layout; } void setColor(const osg::Vec4& color); const osg::Vec4& getColor() const { return _color; } enum DrawModeMask { TEXT = 1, /// default BOUNDINGBOX = 2, ALIGNMENT = 4 }; void setDrawMode(unsigned int mode); unsigned int getDrawMode() const { return _drawMode; } enum BackdropType { DROP_SHADOW_BOTTOM_RIGHT = 0, // usually the type of shadow you see DROP_SHADOW_CENTER_RIGHT, DROP_SHADOW_TOP_RIGHT, DROP_SHADOW_BOTTOM_CENTER, DROP_SHADOW_TOP_CENTER, DROP_SHADOW_BOTTOM_LEFT, DROP_SHADOW_CENTER_LEFT, DROP_SHADOW_TOP_LEFT, OUTLINE, NONE }; enum BackdropImplementation { /* POLYGON_OFFSET: * This uses glPolygonOffset to draw the text multiple times to * create the drop-shadow and outline effects. glPolygonOffset * is used to prevent z-fighting of the overlapping text. * This probably should have been the best option, but all the ATI * cards we have encountered so far have serious problems with this. * We see little white holes/artifacts in the rendered glyph textures * which move around depending on the viewing angle. For moving text, * the moving holes give an extremely unpleasant flickering effect. * Pumping up the "units" parameter in glPolygonOffset can minimize * this problem, but two other bad side-effects occur if you do this. * First, high values will cause problems with clipping, particularly * when there are objects behind the text. The drop-shadows or outline * may be culled because their computed offset is behind the object or * z-far plane. Second, there is an additional problem associated with * the Z-slope. High values can make large chunks of the backdrop * suddenly disappear. This can be reduced by the "factor" parameter. * Making the "factor" value small, can help, but experimentally, we've * found that it creates a new, different kind of z-fighting problem. * So there is no perfect solution. With units, you trade off the 'holes' * for the large-section clipping. * Experimentally, we have found units values from 150-512 to be tolerable * to acceptable with respect to the 'holes'. A factor of .1 seems to * bring down the large clipping problem without creating a new z-fighting * problem. * (You can experiment with these numbers by playing with the * osg:PolygonOffset multipliers which this backend tries to respect.) * * If ATI ever fixes their cards/drivers, then this might become the * best option.*/ POLYGON_OFFSET = 0, /* NO_DEPTH_BUFFER * Instead of using glPolygonOffset to prevent z-fighting, this mode * just disables the depth buffer when rendering the text. This allows * the text to be rendered without any z-fighting. The downside to this * mode is that render order begins to matter and the text will not * necessarily correctly appear above or behind other objects in the * scene based on depth values. * This mode is best for text that only needs to be ontop and * not obscured by any objects.*/ NO_DEPTH_BUFFER, /* DEPTH_RANGE * This mode is inspired by Paul Martz's OpenGL FAQ, item 13.050. * This uses glDepthRange as a substitute for glPolygonOffset. * Strangely, experiments on ATI cards seem to produce cleaner results * than when using glPolygonOffset. The trade-off for this is that the * backdrop still may be placed too far back and might be culled by objects * directly behind the object or by the far z-plane. If ATI ever fixes * the glPolygonOffset problem, polygon offset is probably a slightly * better solution because you can use smaller offsets. But with the * current ATI problem, this option may be preferable.*/ DEPTH_RANGE, /* STENCIL_BUFFER * (Assuming the backend is written correctly,) the Stencil Buffer is * the most "correct" and reliable way of producing backdrop text. * The stencil buffer is a multipass system that allows writing to the * same z-values without needing to resort to offsets. This implementation * should not have any of the problems associated with the 3 previous * implementations. But the trade-off for this mode is that without * hardware acceleration for the stencil buffer, rendering will be * extremely slow. (There is also potentially more overhead for this * algorithm so it could be slower than the other implementations. * Benchmarking would be required to determine if the speed differences * are significant on your particular hardware.) This mode is best for * when quality is important and stencil buffer hardware acceleration * is available.*/ STENCIL_BUFFER }; /** * BackdropType gives you a background shadow text behind your regular * text. This helps give text extra contrast which can be useful when * placing text against noisy backgrounds. * The color of the background shadow text is specified by setBackdropColor(). * DROP_SHADOW_BOTTOM_RIGHT will draw backdrop text to the right and down of * the normal text. Other DROW_SHADOW_* modes do the same for their repective directions. * OUTLINE will draw backdrop text so that it appears the text has an outline * or border around the normal text. This mode is particularly useful against * really noisy backgrounds that may put text on top of things that have * all types of colors which you don't have control over. * Some real world examples of this general technique in use that I know of * are Google Earth, Sid Meier's Pirates (2004 Remake), and Star Control 2 (PC 1993). * The default is NONE. */ void setBackdropType(BackdropType type); BackdropType getBackdropType() const { return _backdropType; } /** * Sets the amount text is offset to create the backdrop/shadow effect. * Set the value too high and for example, in OUTLINE mode you will get a "Brady Bunch" * effect where you see duplicates of the text in a 3x3 grid. * Set the value too small and you won't see anything. * The values represent percentages. 1.0 means 100% so a value of 1.0 * in DROW_SHADOW_LEFT_CENTER mode would cause each glyph to be echoed * next to it self. So the letter 'e' might look like 'ee'. * Good values tend to be in the 0.03 to 0.10 range (but will be subject * to your specific font and display characteristics). * Note that the text bounding boxes are updated to include backdrop offsets. * However, other metric information such as getCharacterHeight() are unaffected * by this. This means that individual glyph spacing (kerning?) are unchanged * even when this mode is used. * The default is 0.07 (7% offset). */ void setBackdropOffset(float offset = 0.07f); /** * This overloaded version lets you specify the offset for the horizontal * and vertical components separately. */ void setBackdropOffset(float horizontal, float vertical); float getBackdropHorizontalOffset() const { return _backdropHorizontalOffset; } float getBackdropVerticalOffset() const { return _backdropVerticalOffset; } /** * This specifies the color of the backdrop text. * The default is black. */ void setBackdropColor(const osg::Vec4& color); const osg::Vec4& getBackdropColor() const { return _backdropColor; } /** * This specifies the underlying backdrop rendering implementation. * Unfortunately, at this time, there is no "perfect" rendering solution * so this function is provided to let you 'pick your poison'. Each * implementation has trade-offs. See BackdropImplementation enum * docs for details.*/ void setBackdropImplementation(BackdropImplementation implementation); BackdropImplementation getBackdropImplementation() const { return _backdropImplementation; } enum ColorGradientMode { SOLID = 0, // a.k.a. ColorGradients off PER_CHARACTER, OVERALL }; /** * This sets different types of text coloring modes. * When the coloring mode is not set to SOLID, the * colors specified in setColorGradientCorners() determine * the colors for the text. * When the gradient mode is OVERALL, the coloring scheme * attempts to approximate the effect as if the entire text box/region * were a single polygon and you had applied colors to each of the four * corners with GL_SMOOTH enabled. In this mode, OpenGL interpolates * the colors across the polygon, and this is what OVERALL tries to * emulate. This can be used to give nice embellishments on things * like logos and names. * PER_CHARACTER is similar to OVERALL except that it applies the * color interpolation to the four corners of each character instead * of across the overall text box. * The default is SOLID (a.k.a. off). */ void setColorGradientMode(ColorGradientMode mode); ColorGradientMode getColorGradientMode() const { return _colorGradientMode; } /** * Used only for gradient mode, let's you specify the colors of the 4 corners. * If ColorGradients are off, these values are ignored (and the value from setColor() * is the only one that is relevant. */ void setColorGradientCorners(const osg::Vec4& topLeft, const osg::Vec4& bottomLeft, const osg::Vec4& bottomRight, const osg::Vec4& topRight); const osg::Vec4& getColorGradientTopLeft() const { return _colorGradientTopLeft; } const osg::Vec4& getColorGradientBottomLeft() const { return _colorGradientBottomLeft; } const osg::Vec4& getColorGradientBottomRight() const { return _colorGradientBottomRight; } const osg::Vec4& getColorGradientTopRight() const { return _colorGradientTopRight; } void setKerningType(KerningType kerningType) { _kerningType = kerningType; } KerningType getKerningType() const { return _kerningType; } /** Get the number of wrapped lines - only valid after computeGlyphRepresentation() has been called, returns 0 otherwise */ unsigned int getLineCount() const { return _lineCount; } /** Draw the text.*/ virtual void drawImplementation(osg::RenderInfo& renderInfo) const; /** return false, osgText::Text does not support accept(AttributeFunctor&).*/ virtual bool supports(const osg::Drawable::AttributeFunctor&) const { return false; } /** return true, osgText::Text does support accept(ConstAttributeFunctor&).*/ virtual bool supports(const osg::Drawable::ConstAttributeFunctor&) const { return true; } /** accept an ConstAttributeFunctor and call its methods to tell it about the interal attributes that this Drawable has.*/ virtual void accept(osg::Drawable::ConstAttributeFunctor& af) const; /** return true, osgText::Text does support accept(PrimitiveFunctor&) .*/ virtual bool supports(const osg::PrimitiveFunctor&) const { return true; } /** accept a PrimtiveFunctor and call its methods to tell it about the interal primtives that this Drawable has.*/ virtual void accept(osg::PrimitiveFunctor& pf) 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); /** If State is non-zero, this function releases OpenGL objects for * the specified graphics context. Otherwise, releases OpenGL objexts * for all graphics contexts. */ virtual void releaseGLObjects(osg::State* state=0) const; // make Font a friend to allow it set the _font to 0 if the font is // forcefully unloaded. friend class Font; public: // internal structures, variable and methods used for rendering of characters. struct OSGTEXT_EXPORT GlyphQuads { typedef std::vector Glyphs; typedef std::vector LineNumbers; typedef std::vector Coords2; typedef std::vector Coords3; typedef std::vector TexCoords; typedef std::vector ColorCoords; Glyphs _glyphs; Coords2 _coords; osg::buffered_object _transformedCoords; TexCoords _texcoords; LineNumbers _lineNumbers; osg::buffered_object _transformedBackdropCoords[8]; ColorCoords _colorCoords; Glyphs getGlyphs() { return _glyphs; } const Glyphs getGlyphs() const { return _glyphs; } Coords2& getCoords() { return _coords; } const Coords2& getCoords() const { return _coords; } Coords3& getTransformedCoords(unsigned int contexID) { return _transformedCoords[contexID]; } const Coords3& getTransformedCoords(unsigned int contexID) const { return _transformedCoords[contexID]; } TexCoords& getTexCoords() { return _texcoords; } const TexCoords& getTexCoords() const { return _texcoords; } LineNumbers& getLineNumbers() { return _lineNumbers; } const LineNumbers& getLineNumbers() const { return _lineNumbers; } }; typedef std::map,GlyphQuads> TextureGlyphQuadMap; /** Direct Access to GlyphQuads */ const GlyphQuads* getGlyphQuads(Font::GlyphTexture* texture) const { TextureGlyphQuadMap::iterator itGlyphQuad = _textureGlyphQuadMap.find(texture); if (itGlyphQuad == _textureGlyphQuadMap.end()) return NULL; return &itGlyphQuad->second; } const TextureGlyphQuadMap& getTextureGlyphQuadMap() const { return _textureGlyphQuadMap; } virtual osg::BoundingBox computeBound() const; protected: virtual ~Text(); Font* getActiveFont(); const Font* getActiveFont() const; String::iterator computeLastCharacterOnLine(osg::Vec2& cursor, String::iterator first,String::iterator last); // members which have public access. osg::ref_ptr _font; unsigned int _fontWidth; unsigned int _fontHeight; float _characterHeight; float _characterAspectRatio; CharacterSizeMode _characterSizeMode; float _maximumWidth; float _maximumHeight; float _lineSpacing; String _text; osg::Vec3 _position; AlignmentType _alignment; osg::Quat _rotation; bool _autoRotateToScreen; Layout _layout; osg::Vec4 _color; unsigned int _drawMode; KerningType _kerningType; unsigned int _lineCount; // iternal map used for rendering. Set up by the computeGlyphRepresentation() method. mutable TextureGlyphQuadMap _textureGlyphQuadMap; void computeGlyphRepresentation(); // internal caches of the positioning of the text. struct AutoTransformCache { AutoTransformCache(): _traversalNumber(-1), _width(0), _height(0) {} int _traversalNumber; int _width; int _height; osg::Vec3 _transformedPosition; osg::Matrix _modelview; osg::Matrix _projection; osg::Matrix _matrix; }; mutable osg::buffered_object _autoTransformCache; mutable osg::Vec3 _offset; mutable osg::Vec3 _normal; mutable osg::BoundingBox _textBB; bool computeAverageGlyphWidthAndHeight(float& avg_width, float& avg_height) const; void computePositions(); void computePositions(unsigned int contextID) const; void computeBackdropPositions(unsigned int contextID) const; void computeBackdropBoundingBox() const; void computeColorGradients() const; void computeColorGradientsOverall() const; void computeColorGradientsPerCharacter() const; void drawImplementation(osg::State& state, const osg::Vec4& colorMultiplier) const; void drawForegroundText(osg::State& state, const GlyphQuads& glyphquad, const osg::Vec4& colorMultiplier) const; void renderOnlyForegroundText(osg::State& state, const osg::Vec4& colorMultiplier) const; void renderWithPolygonOffset(osg::State& state, const osg::Vec4& colorMultiplier) const; void renderWithNoDepthBuffer(osg::State& state, const osg::Vec4& colorMultiplier) const; void renderWithDepthRange(osg::State& state, const osg::Vec4& colorMultiplier) const; void renderWithStencilBuffer(osg::State& state, const osg::Vec4& colorMultiplier) const; BackdropType _backdropType; BackdropImplementation _backdropImplementation; float _backdropHorizontalOffset; float _backdropVerticalOffset; osg::Vec4 _backdropColor; ColorGradientMode _colorGradientMode; osg::Vec4 _colorGradientTopLeft; osg::Vec4 _colorGradientBottomLeft; osg::Vec4 _colorGradientBottomRight; osg::Vec4 _colorGradientTopRight; // Helper functions for color interpolation float bilinearInterpolate(float x1, float x2, float y1, float y2, float x, float y, float q11, float q12, float q21, float q22) const; void convertHsvToRgb( float hsv[], float rgb[] ) const; void convertRgbToHsv( float rgb[], float hsv[] ) const; }; } #endif