/* -*-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 osgText::TextBase { 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"; } virtual void setFont(Font* font=0) { setFont(osg::ref_ptr(font)); }; /** Set the Font to use to render the text.*/ virtual void setFont(osg::ref_ptr font); /** 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. */ virtual void setFont(const std::string& fontfile) { TextBase::setFont(fontfile); } /** * Turns off writing to the depth buffer when rendering text. This only affects text * with no backdrop or text using the DELAYED_DEPTH_WRITES implementation, since * the other backdrop implementations are really only useful for backwards * compatibility and are not worth updating to utilize this flag. */ void setEnableDepthWrites(bool enable) { _enableDepthWrites = enable; } bool getEnableDepthWrites() const { return _enableDepthWrites; } 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, /* DELAYED_DEPTH_WRITES * This mode renders all text with depth writes turned off, then * again with depth writes on, but with the color buffer disabled. * This should render text accurately for all graphics cards. The * only downside is the additional pass to render to the depth * buffer. But if you don't need the depth buffer updated for * your, this extra pass can be disabled by calling * enableDepthWrites(false).*/ DELAYED_DEPTH_WRITES }; /** * 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; } /** 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 internal 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 internal primitives 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; public: // internal structures, variable and methods used for rendering of characters. struct OSGTEXT_EXPORT GlyphQuads { typedef std::vector Glyphs; typedef std::vector LineNumbers; typedef osg::ref_ptr Coords2; typedef osg::ref_ptr Coords3; typedef osg::ref_ptr TexCoords; typedef osg::ref_ptr ColorCoords; Glyphs _glyphs; Coords2 _coords; osg::buffered_object _transformedCoords; TexCoords _texcoords; LineNumbers _lineNumbers; osg::buffered_object _transformedBackdropCoords[8]; ColorCoords _colorCoords; osg::ref_ptr _quadIndices; void updateQuadIndices(); GlyphQuads(); GlyphQuads(const GlyphQuads& gq); void initGlyphQuads(); void initGPUBufferObjects(); 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; } /** Resize any per context GLObject buffers to specified size. */ 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. */ void releaseGLObjects(osg::State* state=0) const; private: GlyphQuads& operator = (const GlyphQuads&) { return *this; } }; typedef std::map,GlyphQuads> TextureGlyphQuadMap; /** Direct Access to GlyphQuads */ const GlyphQuads* getGlyphQuads(GlyphTexture* texture) const { TextureGlyphQuadMap::iterator itGlyphQuad = _textureGlyphQuadMap.find(texture); if (itGlyphQuad == _textureGlyphQuadMap.end()) return NULL; return &itGlyphQuad->second; } const TextureGlyphQuadMap& getTextureGlyphQuadMap() const { return _textureGlyphQuadMap; } 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. // iternal map used for rendering. Set up by the computeGlyphRepresentation() method. mutable TextureGlyphQuadMap _textureGlyphQuadMap; void computeGlyphRepresentation(); // internal caches of the positioning of the text. bool computeAverageGlyphWidthAndHeight(float& avg_width, float& avg_height) const; virtual void computePositions(unsigned int contextID) const; void computeBackdropPositions(unsigned int contextID) const; void computeBackdropBoundingBox() const; void computeBoundingBoxMargin() 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 drawTextWithBackdrop(osg::State& state, 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; void renderWithDelayedDepthWrites(osg::State& state, const osg::Vec4& colorMultiplier) const; bool _enableDepthWrites; 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 function 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; }; } #endif