405 lines
18 KiB
C++
405 lines
18 KiB
C++
/* -*-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 <osg/Drawable>
|
|
#include <osg/Quat>
|
|
|
|
#include <osgText/TextBase>
|
|
#include <osgText/Font>
|
|
|
|
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<const Text*>(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>(font)); };
|
|
|
|
/** Set the Font to use to render the text.*/
|
|
virtual void setFont(osg::ref_ptr<Font> 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;
|
|
|
|
|
|
/** 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<Glyph*> Glyphs;
|
|
|
|
Glyphs _glyphs;
|
|
Primitives _primitives;
|
|
|
|
GlyphQuads();
|
|
GlyphQuads(const GlyphQuads& gq);
|
|
|
|
void setupPrimitives(Text::BackdropType backdropType);
|
|
|
|
Glyphs& getGlyphs() { return _glyphs; }
|
|
const Glyphs& getGlyphs() const { return _glyphs; }
|
|
|
|
/** 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<osg::ref_ptr<GlyphTexture>,GlyphQuads> TextureGlyphQuadMap;
|
|
|
|
/** Direct Access to GlyphQuads */
|
|
const GlyphQuads* getGlyphQuads(GlyphTexture* texture) const
|
|
{
|
|
TextureGlyphQuadMap::const_iterator itGlyphQuad = _textureGlyphQuadMap.find(texture);
|
|
if (itGlyphQuad == _textureGlyphQuadMap.end()) return NULL;
|
|
|
|
return &itGlyphQuad->second;
|
|
}
|
|
|
|
const TextureGlyphQuadMap& getTextureGlyphQuadMap() const
|
|
{
|
|
return _textureGlyphQuadMap;
|
|
}
|
|
|
|
void addGlyphQuad(Glyph* glyph, const osg::Vec2& minc, const osg::Vec2& maxc, const osg::Vec2& mintc, const osg::Vec2& maxtc);
|
|
|
|
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.
|
|
TextureGlyphQuadMap _textureGlyphQuadMap;
|
|
|
|
void computeGlyphRepresentation();
|
|
|
|
// internal caches of the positioning of the text.
|
|
|
|
bool computeAverageGlyphWidthAndHeight(float& avg_width, float& avg_height) const;
|
|
|
|
virtual void computePositionsImplementation();
|
|
|
|
void computeBackdropPositions();
|
|
void computeBackdropBoundingBox();
|
|
void computeBoundingBoxMargin();
|
|
|
|
void computeColorGradients();
|
|
void computeColorGradientsOverall();
|
|
void computeColorGradientsPerCharacter();
|
|
|
|
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
|