From f69a48e55210148764e7b4d4d5c7052a25c13b3b Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 10 Dec 2007 15:15:56 +0000 Subject: [PATCH] From David Callu, added support of 3D text to osgText and associated plugins. --- examples/CMakeLists.txt | 1 + examples/osgtext3D/CMakeLists.txt | 7 + examples/osgtext3D/osgtext3D.cpp | 137 ++++ include/osgText/Font | 10 +- include/osgText/Font3D | 284 +++++++ include/osgText/KerningType | 26 + include/osgText/Text | 232 +----- include/osgText/TextBase | 292 +++++++ src/osgPlugins/freetype/CMakeLists.txt | 4 +- src/osgPlugins/freetype/FreeTypeFont.h | 2 +- src/osgPlugins/freetype/FreeTypeFont3D.cpp | 521 +++++++++++++ src/osgPlugins/freetype/FreeTypeFont3D.h | 64 ++ src/osgPlugins/freetype/FreeTypeFontBase.cpp | 224 ++++++ src/osgPlugins/freetype/FreeTypeFontBase.h | 46 ++ src/osgPlugins/freetype/FreeTypeLibrary.cpp | 84 +- src/osgPlugins/freetype/FreeTypeLibrary.h | 20 +- .../freetype/ReaderWriterFreeType.cpp | 5 +- src/osgPlugins/jp2/ReaderWriterJP2.cpp | 18 +- src/osgPlugins/osgText/CMakeLists.txt | 2 +- src/osgPlugins/osgText/IO_Text.cpp | 532 ++++++------- src/osgPlugins/osgText/IO_Text3D.cpp | 99 +++ src/osgText/CMakeLists.txt | 9 + src/osgText/Font.cpp | 4 +- src/osgText/Font3D.cpp | 248 ++++++ src/osgText/Text.cpp | 243 +----- src/osgText/Text3D.cpp | 719 ++++++++++++++++++ src/osgText/TextBase.cpp | 392 ++++++++++ 27 files changed, 3427 insertions(+), 798 deletions(-) create mode 100644 examples/osgtext3D/CMakeLists.txt create mode 100644 examples/osgtext3D/osgtext3D.cpp create mode 100644 include/osgText/Font3D create mode 100644 include/osgText/KerningType create mode 100644 include/osgText/TextBase create mode 100644 src/osgPlugins/freetype/FreeTypeFont3D.cpp create mode 100644 src/osgPlugins/freetype/FreeTypeFont3D.h create mode 100644 src/osgPlugins/freetype/FreeTypeFontBase.cpp create mode 100644 src/osgPlugins/freetype/FreeTypeFontBase.h create mode 100644 src/osgPlugins/osgText/IO_Text3D.cpp create mode 100644 src/osgText/Font3D.cpp create mode 100644 src/osgText/Text3D.cpp create mode 100644 src/osgText/TextBase.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index da039cebe..ef63dfb7b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -83,6 +83,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgteapot) ADD_SUBDIRECTORY(osgtessellate)#) ADD_SUBDIRECTORY(osgtext) + ADD_SUBDIRECTORY(osgtext3D) ADD_SUBDIRECTORY(osgtexture1D) ADD_SUBDIRECTORY(osgtexture2D) ADD_SUBDIRECTORY(osgtexture3D) diff --git a/examples/osgtext3D/CMakeLists.txt b/examples/osgtext3D/CMakeLists.txt new file mode 100644 index 000000000..b284032d0 --- /dev/null +++ b/examples/osgtext3D/CMakeLists.txt @@ -0,0 +1,7 @@ +#this file is automatically generated + + +SET(TARGET_SRC osgtext3D.cpp ) +SET(TARGET_ADDED_LIBRARIES osgText ) +#### end var setup ### +SETUP_EXAMPLE(osgtext3D) diff --git a/examples/osgtext3D/osgtext3D.cpp b/examples/osgtext3D/osgtext3D.cpp new file mode 100644 index 000000000..1e0ac3201 --- /dev/null +++ b/examples/osgtext3D/osgtext3D.cpp @@ -0,0 +1,137 @@ +/* OpenSceneGraph example, osgtext. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ + + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + + +// create text which sits in 3D space such as would be inserted into a normal model +osg::Group* create3DText(const osg::Vec3& center,float radius) +{ + + osg::Geode* geode = new osg::Geode; + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Examples of how to set up axis/orientation alignments +// + + float characterSize=radius*0.2f; + float characterDepth=characterSize*0.2f; + + osg::Vec3 pos(center.x()-radius*.5f,center.y()-radius*.5f,center.z()-radius*.5f); + + osgText::Text3D* text1 = new osgText::Text3D; + text1->setFont("fonts/arial.ttf"); + text1->setCharacterSize(characterSize); + text1->setCharacterDepth(characterDepth); + text1->setPosition(pos); + text1->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX); + text1->setAxisAlignment(osgText::Text3D::XY_PLANE); + text1->setText("XY_PLANE"); + geode->addDrawable(text1); + + osgText::Text3D* text2 = new osgText::Text3D; + text2->setFont("fonts/times.ttf"); + text2->setCharacterSize(characterSize); + text2->setCharacterDepth(characterDepth); + text2->setPosition(pos); + text2->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX); + text2->setAxisAlignment(osgText::Text3D::YZ_PLANE); + text2->setText("YZ_PLANE"); + geode->addDrawable(text2); + + osgText::Text3D* text3 = new osgText::Text3D; + text3->setFont("fonts/dirtydoz.ttf"); + text3->setCharacterSize(characterSize); + text3->setCharacterDepth(characterDepth); + text3->setPosition(pos); + text3->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX); + text3->setAxisAlignment(osgText::Text3D::XZ_PLANE); + text3->setText("XZ_PLANE"); + geode->addDrawable(text3); + + osgText::Text3D* text7 = new osgText::Text3D; + text7->setFont("fonts/times.ttf"); + text7->setCharacterSize(characterSize); + text7->setCharacterDepth(characterSize*0.2f); + text7->setPosition(center - osg::Vec3(0.0, 0.0, 0.6)); + text7->setDrawMode(osgText::Text3D::TEXT | osgText::Text3D::BOUNDINGBOX); + text7->setAxisAlignment(osgText::Text3D::SCREEN); + text7->setCharacterSizeMode(osgText::Text3D::OBJECT_COORDS); + text7->setText("CharacterSizeMode OBJECT_COORDS (default)"); + geode->addDrawable(text7); + + osg::ShapeDrawable* shape = new osg::ShapeDrawable(new osg::Sphere(center,characterSize*0.2f)); + shape->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::ON); + geode->addDrawable(shape); + + osg::Group* rootNode = new osg::Group; + rootNode->addChild(geode); + + osg::Material* front = new osg::Material; + front->setAlpha(osg::Material::FRONT_AND_BACK,1); + front->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.2,0.2,0.2,1.0)); + front->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(.0,.0,1.0,1.0)); + rootNode->getOrCreateStateSet()->setAttributeAndModes(front); + + + return rootNode; +} + +int main(int, char**) +{ + osgViewer::Viewer viewer; + + osg::Vec3 center(0.0f,0.0f,0.0f); + float radius = 1.0f; + + osg::Group* root = new osg::Group; + root->addChild(create3DText(center, radius)); + + viewer.setSceneData(root); + viewer.setCameraManipulator(new osgGA::TrackballManipulator()); + viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); + + viewer.addEventHandler(new osgViewer::ThreadingHandler); + viewer.addEventHandler(new osgViewer::WindowSizeHandler); + viewer.addEventHandler(new osgViewer::StatsHandler); + + + viewer.run(); + + return 0; +} + + diff --git a/include/osgText/Font b/include/osgText/Font index ae3be1be7..16e13c060 100644 --- a/include/osgText/Font +++ b/include/osgText/Font @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -32,12 +33,7 @@ namespace osgText { class Font; class Text; -enum KerningType -{ - KERNING_DEFAULT, //default locked to integer kerning values - KERNING_UNFITTED, //use floating point value for kerning - KERNING_NONE //no kerning -}; + /** Read a font from specified file. The filename may contain a path. * It will search for the font file in the following places in this order: @@ -179,8 +175,6 @@ protected: typedef std::pair< unsigned int, unsigned int > SizePair; typedef std::map< SizePair, GlyphMap > SizeGlyphMap; - mutable FontMutex _serializeFontCallsMutex; - osg::ref_ptr _texenv; osg::ref_ptr _stateset; SizeGlyphMap _sizeGlyphMap; diff --git a/include/osgText/Font3D b/include/osgText/Font3D new file mode 100644 index 000000000..f8853138e --- /dev/null +++ b/include/osgText/Font3D @@ -0,0 +1,284 @@ +/* -*-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_FONT3D +#define OSGTEXT_FONT3D 1 + +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace osgText { + +class Font3D; +class Text3D; + +/** Read a font from specified file. The filename may contain a path. + * It will search for the font file in the following places in this order: + * - In the current directory + * - All paths defined in OSG_FILE_PATH or OSGFILEPATH environment variable + * - Filename with path stripped: In the current directory + * - Filename with path stripped: All paths defined in OSG_FILE_PATH or OSGFILEPATH + * + * Then the file will be searched in OS specific directories in the following order: + * - Again in the current directory + * - Windows: In C:/winnt/fonts + * - Windows: In C:/windows/fonts + * - Windows: In the fonts directory of the windows install directory + * - Other OS: In /usr/share/fonts/ttf + * - Other OS: In /usr/share/fonts/ttf/western + * - Other OS: In /usr/share/fonts/ttf/decoratives + * + * If the given file could not be found, the path part will be stripped and + * the file will be searched again in the OS specific directories. + */ +extern OSGTEXT_EXPORT Font3D* readFont3DFile(const std::string& filename, const osgDB::ReaderWriter::Options* userOptions = 0); + +/** read a font from specified stream.*/ +extern OSGTEXT_EXPORT Font3D* readFont3DStream(std::istream& stream, const osgDB::ReaderWriter::Options* userOptions = 0); + +extern OSGTEXT_EXPORT std::string findFont3DFile(const std::string& str); + +/** Pure virtual base class for fonts. + * Concrete implementation are the DefaultFont found in src/osgText/DefaultFont.cpp + * and FreeTypeFont found in src/osgPlugins/freetype/FreeTypeFont.cpp*/ +class OSGTEXT_EXPORT Font3D : public osg::Object +{ +// declare the interface to a font. +public: + + // forward declare nested classes. + class Glyph3D; + class Font3DImplementation; + +public: + Font3D(Font3DImplementation* implementation=0); + + virtual osg::Object* cloneType() const { return 0; } // cloneType() not appropriate + virtual osg::Object* clone(const osg::CopyOp&) const { return 0; } // clone() not appropriate + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual const char* className() const { return "Font3D"; } + virtual const char* libraryName() const { return "osgText"; } + + virtual std::string getFileName() const; + + /** Set the pixel width and height hint.*/ +// virtual void setFontResolution(unsigned int width, unsigned int height, unsigned int depth); + + unsigned int getFontWidth() const { return _width; } + unsigned int getFontHeight() const { return _height; } + unsigned int getFontDepth() const { return _depth; } + + /** Get a kerning (adjustment of spacing of two adjacent character) for specified charcodes, w.r.t the current font size hint.*/ + virtual osg::Vec2 getKerning(unsigned int leftcharcode,unsigned int rightcharcode, KerningType kerningType); + + /** Get a Glyph for specified charcode, and the font size nearest to the current font size hint.*/ + virtual Glyph3D* getGlyph(unsigned int charcode); + + /** Return true if this font provides vertical alignments and spacing or glyphs.*/ + virtual bool hasVertical() const; + + /** Return the scale to apply on the glyph to have a charactere size equal to 1 */ + virtual float getScale() const { return _implementation->getScale(); }; + + // make Text a friend to allow it add and remove its entry in the Font's _textList. + friend class Font3DImplementation; + + void setImplementation(Font3DImplementation* implementation); + + Font3DImplementation* getImplementation(); + const Font3DImplementation* getImplementation() const; + + /** Set whether to use a mutex to ensure ref() and unref() */ + virtual void setThreadSafeRefUnref(bool threadSafe); + + typedef OpenThreads::Mutex Font3DMutex; + + /** Get the mutex that enables the serialization of calls to this font.*/ + static Font3DMutex* getSerializeFontCallsMutex(); + +protected: + + virtual ~Font3D(); + +// void addGlyph(unsigned int width, unsigned int height, unsigned int charcode, Glyph* glyph); + void addGlyph(unsigned int charcode, Glyph3D* glyph); + + // current active size of font + unsigned int _depth; + unsigned int _width; + unsigned int _height; +// unsigned int _margin; +// float _marginRatio; + + typedef std::map > Glyph3DMap; + Glyph3DMap _glyph3DMap; + + osg::ref_ptr _implementation; + + +// declare the nested classes. +public: + + class Font3DImplementation : public osg::Referenced + { + public: + + Font3DImplementation(): + osg::Referenced(true) {} + + virtual std::string getFileName() const = 0; + + /** Set the pixel width and height hint.*/ +// virtual void setFontResolution(unsigned int width, unsigned int height, unsigned int depth) = 0; + + /** Get a Glyph for specified charcode, and the font size nearest to the current font size hint.*/ + virtual Glyph3D* getGlyph(unsigned int charcode) = 0; + + /** Get a kerning (adjustment of spacing of two adjacent character) for specified charcodes, w.r.t the current font size hint.*/ + virtual osg::Vec2 getKerning(unsigned int leftcharcode,unsigned int rightcharcode, KerningType kerningType) = 0; + + /** Return true if this font provides vertical alignments and spacing or glyphs.*/ + virtual bool hasVertical() const = 0; + + virtual float getScale() const = 0; + + void setFontWidth(unsigned int width) { _facade->_width = width; } + + void setFontHeight(unsigned int height) { _facade->_height = height; } + + void setFontDepth(unsigned int depth) { _facade->_depth = depth; } + +// void addGlyph(unsigned int width, unsigned int height, unsigned int charcode, Glyph3D* glyph) +// { +// _facade->addGlyph(width, height, charcode, glyph); +// } +// +// void addGlyph(unsigned int charcode, Glyph3D* glyph) +// { +// _facade->addGlyph(charcode, glyph); +// } + + Font3D* _facade; + }; + + class OSGTEXT_EXPORT Glyph3D : public osg::Referenced + { + public: + + Glyph3D(unsigned int glyphCode): + _glyphCode(glyphCode), + _horizontalBearing(0,0), + _horizontalAdvance(0), + _verticalBearing(0,0), + _verticalAdvance(0) + {} + + + /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/ +// virtual int compare(const osg::StateAttribute& rhs) const; +// +// virtual void apply(osg::State& state) const; + + + + unsigned int getGlyphCode() const { return _glyphCode; } + + void setHorizontalBearing(const osg::Vec2& bearing) { _horizontalBearing=bearing; } + const osg::Vec2 & getHorizontalBearing() const { return _horizontalBearing; } + + void setHorizontalAdvance(float advance) { _horizontalAdvance=advance; } + float getHorizontalAdvance() const { return _horizontalAdvance; } + + void setVerticalBearing(const osg::Vec2& bearing) { _verticalBearing=bearing; } + const osg::Vec2& getVerticalBearing() const { return _verticalBearing; } + + void setVerticalAdvance(float advance) { _verticalAdvance=advance; } + float getVerticalAdvance() const { return _verticalAdvance; } + + void setBoundingBox(osg::BoundingBox & bb) { _bb=bb; } + const osg::BoundingBox & getBoundingBox() const { return _bb; } + + + /** Set whether to use a mutex to ensure ref() and unref() are thread safe.*/ + virtual void setThreadSafeRefUnref(bool threadSafe); + + + /** Get the PrimitiveSetList for the front face. */ + osg::Geometry::PrimitiveSetList & getFrontPrimitiveSetList() { return _frontPrimitiveSetList; } + /** Get the PrimitiveSetList for the wall face. */ + osg::Geometry::PrimitiveSetList & getWallPrimitiveSetList() { return _wallPrimitiveSetList; } + /** Get et the PrimitiveSetList for the back face. */ + osg::Geometry::PrimitiveSetList & getBackPrimitiveSetList() { return _backPrimitiveSetList; } + + /** Set the VertexArray of the glyph. */ + void setVertexArray(osg::Vec3Array * va) { _vertexArray = va; } + /** Get the VertexArray of the glyph. */ + osg::Vec3Array * getVertexArray() { return _vertexArray.get(); } + /** Get the NormalArray for the wall face. */ + osg::Vec3Array * getNormalArray() { return _normalArray.get(); } + + float getHorizontalWidth() { return (-_horizontalBearing.x() + _horizontalAdvance); } + float getHorizontalHeight() { return (-_horizontalBearing.y() + _bb.yMax()); } + float getVerticalWidth() { return (-_verticalBearing.x() + _bb.xMax()); } + float getVerticalHeight() { return (-_verticalBearing.y() + _verticalAdvance); } + + void setWidth(float width) { _width = width; } + float getWidth() { return _width; } + + void setHeight(float height) { _height = height; } + float getHeight() { return _height; } + + protected: + + virtual ~Glyph3D() {} + + unsigned int _glyphCode; + + osg::Vec2 _horizontalBearing; + float _horizontalAdvance; + + osg::Vec2 _verticalBearing; + float _verticalAdvance; + + osg::BoundingBox _bb; +// osg::Vec2 _advance; + + float _width; + float _height; + + + osg::ref_ptr _vertexArray; + osg::ref_ptr _normalArray; + + osg::Geometry::PrimitiveSetList _frontPrimitiveSetList; + osg::Geometry::PrimitiveSetList _wallPrimitiveSetList; + osg::Geometry::PrimitiveSetList _backPrimitiveSetList; + + + }; + + +}; + +} + + +#endif diff --git a/include/osgText/KerningType b/include/osgText/KerningType new file mode 100644 index 000000000..5f6d81b05 --- /dev/null +++ b/include/osgText/KerningType @@ -0,0 +1,26 @@ +/* -*-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_KERNINGTYPE +#define OSGTEXT_KERNINGTYPE + +namespace osgText +{ +enum KerningType +{ + KERNING_DEFAULT, //default locked to integer kerning values + KERNING_UNFITTED, //use floating point value for kerning + KERNING_NONE //no kerning +}; +} +#endif /*OSGTEXT_KERNINGTYPE*/ diff --git a/include/osgText/Text b/include/osgText/Text index fce2f00c0..6c0335cb5 100644 --- a/include/osgText/Text +++ b/include/osgText/Text @@ -14,16 +14,17 @@ #ifndef OSGTEXT_TEXT #define OSGTEXT_TEXT 1 + #include #include +#include #include -#include namespace osgText { -class OSGTEXT_EXPORT Text : public osg::Drawable +class OSGTEXT_EXPORT Text : public osgText::TextBase { public: @@ -50,179 +51,14 @@ public: 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, - USER_DEFINED_ROTATION - }; - - void setAxisAlignment(AxisAlignment axis); - AxisAlignment getAxisAlignment() const { return _axisAlignment; } - - 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 @@ -417,13 +253,7 @@ public: 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; @@ -454,9 +284,9 @@ public: * 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; +// // 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. @@ -510,7 +340,6 @@ public: return _textureGlyphQuadMap; } - virtual osg::BoundingBox computeBound() const; protected: @@ -523,26 +352,7 @@ protected: // 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; - AxisAlignment _axisAlignment; - 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; @@ -551,31 +361,9 @@ protected: // 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; + + virtual void computePositions(unsigned int contextID) const; void computeBackdropPositions(unsigned int contextID) const; void computeBackdropBoundingBox() const; diff --git a/include/osgText/TextBase b/include/osgText/TextBase new file mode 100644 index 000000000..1d9062359 --- /dev/null +++ b/include/osgText/TextBase @@ -0,0 +1,292 @@ +/* -*-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_TEXTBASE +#define OSGTEXT_TEXTBASE 1 + +#include + +#include +#include + +namespace osgText { + + +class OSGTEXT_EXPORT TextBase : public osg::Drawable +{ +public: + + TextBase(); + TextBase(const TextBase& 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 "TextBase"; } + virtual const char* libraryName() const { return "osgText"; } + + + /** 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 respresentation 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, + USER_DEFINED_ROTATION + }; + + void setAxisAlignment(AxisAlignment axis); + AxisAlignment getAxisAlignment() const { return _axisAlignment; } + + 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; } + + + enum DrawModeMask + { + TEXT = 1, /// default + BOUNDINGBOX = 2, + ALIGNMENT = 4 + }; + + void setDrawMode(unsigned int mode); + + unsigned int getDrawMode() const { return _drawMode; } + + + 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; } + + /** 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; + + + virtual osg::BoundingBox computeBound() const; + +protected: + + virtual ~TextBase(); + + void positionCursor(const osg::Vec2 & endOfLine_coords, osg::Vec2 & cursor, unsigned int linelength); + void computePositions(); + String::iterator computeLastCharacterOnLine(osg::Vec2& cursor, String::iterator first,String::iterator last); + + + virtual void computePositions(unsigned int contextID) const = 0; + virtual void computeGlyphRepresentation() = 0; + + + // members which have public access. + 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; + AxisAlignment _axisAlignment; + osg::Quat _rotation; + bool _autoRotateToScreen; + Layout _layout; + unsigned int _drawMode; + KerningType _kerningType; + unsigned int _lineCount; + + + + // 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; +}; + +} + + +#endif + diff --git a/src/osgPlugins/freetype/CMakeLists.txt b/src/osgPlugins/freetype/CMakeLists.txt index 298b34cec..c4f105508 100644 --- a/src/osgPlugins/freetype/CMakeLists.txt +++ b/src/osgPlugins/freetype/CMakeLists.txt @@ -7,8 +7,8 @@ INCLUDE_DIRECTORIES(${FREETYPE_INCLUDE_DIRS} ) -SET(TARGET_SRC FreeTypeFont.cpp FreeTypeLibrary.cpp ReaderWriterFreeType.cpp ) -SET(TARGET_H FreeTypeFont.h FreeTypeLibrary.h ) +SET(TARGET_SRC FreeTypeFont.cpp FreeTypeFont3D.cpp FreeTypeLibrary.cpp ReaderWriterFreeType.cpp ) +SET(TARGET_H FreeTypeFont.h FreeTypeFont3D.h FreeTypeLibrary.h ) SET(TARGET_ADDED_LIBRARIES osgText ) SET(TARGET_LIBRARIES_VARS FREETYPE_LIBRARY ) #### end var setup ### diff --git a/src/osgPlugins/freetype/FreeTypeFont.h b/src/osgPlugins/freetype/FreeTypeFont.h index b6839e217..fef2f3dac 100644 --- a/src/osgPlugins/freetype/FreeTypeFont.h +++ b/src/osgPlugins/freetype/FreeTypeFont.h @@ -34,7 +34,7 @@ public: virtual void setFontResolution(unsigned int width, unsigned int height); virtual osgText::Font::Glyph* getGlyph(unsigned int charcode); - + virtual osg::Vec2 getKerning(unsigned int leftcharcode,unsigned int rightcharcode, osgText::KerningType _kerningType); virtual bool hasVertical() const; diff --git a/src/osgPlugins/freetype/FreeTypeFont3D.cpp b/src/osgPlugins/freetype/FreeTypeFont3D.cpp new file mode 100644 index 000000000..2a08c3c5d --- /dev/null +++ b/src/osgPlugins/freetype/FreeTypeFont3D.cpp @@ -0,0 +1,521 @@ +/* -*-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 "FreeTypeFont3D.h" +#include "FreeTypeLibrary.h" + + +#include +#include + + +#include +#include +#include +#include +#include + + +#include +#include FT_FREETYPE_H + +#include +#include + + + +namespace +{ + +struct Char3DInfo +{ + Char3DInfo(int numSteps=50): + _verts( new osg::Vec3Array ), + _geometry( new osg::Geometry ), + _idx(0), + _numSteps(numSteps), + _maxY(-FLT_MAX), + _maxX(-FLT_MAX), + _minX(FLT_MAX), + _minY(FLT_MAX) + { + } + ~Char3DInfo() + { + } + + osg::Geometry* get() + { + int len = _verts->size()-_idx; + if (len) + { + _geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, _idx, len ) ); + _idx = _verts->size(); + } + + _geometry->setVertexArray(_verts.get()); + return _geometry.get(); + } + + void moveTo(osg::Vec2 pos) + { + if (_verts->size()) + { + int len = _verts->size()-_idx; + _geometry->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::POLYGON, _idx, len ) ); + } + _idx = _verts->size(); + _verts->push_back( osg::Vec3(pos.x(),pos.y(),0) ); + + setMinMax(pos); + } + void lineTo(osg::Vec2 pos) + { + _verts->push_back( osg::Vec3(pos.x(),pos.y(),0) ); + setMinMax(pos); + } + void conicTo(osg::Vec2 control, osg::Vec2 pos) + { + osg::Vec3 p0 = _verts->back(); + osg::Vec3 p1 = osg::Vec3(control.x(),control.y(),0); + osg::Vec3 p2 = osg::Vec3(pos.x(),pos.y(),0); + + double dt = 1.0/_numSteps; + double u=0; + for (int i=0; i<=_numSteps; ++i) + { + double w = 1; + double bs = 1.0/( (1-u)*(1-u)+2*(1-u)*u*w +u*u ); + osg::Vec3 p = (p0*((1-u)*(1-u)) + p1*(2*(1-u)*u*w) + p2*(u*u))*bs; + _verts->push_back( p ); + + u += dt; + } + + setMinMax(pos); + } + + void cubicTo(osg::Vec2 control1, osg::Vec2 control2, osg::Vec2 pos) + { + osg::Vec3 p0 = _verts->back(); + osg::Vec3 p1 = osg::Vec3(control1.x(),control1.y(),0); + osg::Vec3 p2 = osg::Vec3(control2.x(),control2.y(),0); + osg::Vec3 p3 = osg::Vec3(pos.x(),pos.y(),0); + + double cx = 3*(p1.x() - p0.x()); + double bx = 3*(p2.x() - p1.x()) - cx; + double ax = p3.x() - p0.x() - cx - bx; + double cy = 3*(p1.y() - p0.y()); + double by = 3*(p2.y() - p1.y()) - cy; + double ay = p3.y() - p0.y() - cy - by; + + double dt = 1.0/_numSteps; + double u=0; + for (int i=0; i<=_numSteps; ++i) + { + osg::Vec3 p = osg::Vec3( ax*u*u*u + bx*u*u + cx*u + p0.x(),ay*u*u*u + by*u*u + cy*u + p0.y(),0 ); + _verts->push_back( p ); + + u += dt; + } + + setMinMax(pos); + } + + void setMinMax(osg::Vec2 pos) + { + _maxY = std::max(_maxY, (double) pos.y()); + _minY = std::min(_minY, (double) pos.y()); + _maxX = std::max(_maxX, (double) pos.x()); + _minX = std::min(_minX, (double) pos.x()); + } + + osg::ref_ptr _verts; + osg::ref_ptr _geometry; + int _idx; + int _numSteps; + double _maxY; + double _maxX; + double _minX; + double _minY; +}; + + +#define FT_NUM(x) (x/64.0) +int moveTo( const FT_Vector* to, void* user ) +{ + Char3DInfo* char3d = (Char3DInfo*)user; + char3d->moveTo( osg::Vec2(FT_NUM(to->x),FT_NUM(to->y)) ); + return 0; +} +int lineTo( const FT_Vector* to, void* user ) +{ + Char3DInfo* char3d = (Char3DInfo*)user; + char3d->lineTo( osg::Vec2(FT_NUM(to->x),FT_NUM(to->y)) ); + return 0; +} +int conicTo( const FT_Vector* control,const FT_Vector* to, void* user ) +{ + Char3DInfo* char3d = (Char3DInfo*)user; + char3d->conicTo( osg::Vec2(FT_NUM(control->x),FT_NUM(control->y)), osg::Vec2(FT_NUM(to->x),FT_NUM(to->y)) ); + return 0; +} +int cubicTo( const FT_Vector* control1,const FT_Vector* control2,const FT_Vector* to, void* user ) +{ + Char3DInfo* char3d = (Char3DInfo*)user; + char3d->cubicTo( + osg::Vec2(FT_NUM(control1->x),FT_NUM(control1->y)), + osg::Vec2(FT_NUM(control2->x),FT_NUM(control2->y)), + osg::Vec2(FT_NUM(to->x),FT_NUM(to->y)) ); + return 0; +} +#undef FT_NUM + +} + + +FreeTypeFont3D::FreeTypeFont3D(const std::string& filename, FT_Face face, unsigned int flags): + _filename(filename), + _buffer(0), + _face(face), + _flags(flags) +{ + init(); +} + +FreeTypeFont3D::FreeTypeFont3D(FT_Byte* buffer, FT_Face face, unsigned int flags): + _filename(""), + _buffer(buffer), + _face(face), + _flags(flags) +{ + init(); +} + +void FreeTypeFont3D::init() +{ + FT_Error _error = FT_Set_Pixel_Sizes(_face, 32, 32); + if (_error) + { + osg::notify(osg::NOTICE) << "FreeTypeFont3D: set pixel sizes failed ..." << std::endl; + return; + } + + FT_Set_Char_Size( _face, 64*64, 64*64, 600, 600); + + int glyphIndex = FT_Get_Char_Index( _face, 'M' ); + _error = FT_Load_Glyph( _face, glyphIndex, FT_LOAD_DEFAULT ); + if (_error) + { + osg::notify(osg::NOTICE) << "FreeTypeFont3D: initial glyph load failed ..." << std::endl; + return; + } + + if (_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + { + osg::notify(osg::NOTICE) << "FreeTypeFont3D: not a vector font" << std::endl; + return; + } + + { + Char3DInfo char3d; + + FT_Outline outline = _face->glyph->outline; + FT_Outline_Funcs funcs; + funcs.conic_to = (FT_Outline_ConicToFunc)&conicTo; + funcs.line_to = (FT_Outline_LineToFunc)&lineTo; + funcs.cubic_to = (FT_Outline_CubicToFunc)&cubicTo; + funcs.move_to = (FT_Outline_MoveToFunc)&moveTo; + funcs.shift = 0; + funcs.delta = 0; + _error = FT_Outline_Decompose(&outline,&funcs,&char3d); + if (_error) + { + osg::notify(osg::NOTICE) << "FreeTypeFont3D: - outline decompose failed ..." << std::endl; + return; + } + + FT_BBox bb; + FT_Outline_Get_BBox(&outline,&bb); + + long xmin = ft_floor( bb.xMin ); + long xmax = ft_ceiling( bb.xMax ); + long ymin = ft_floor( bb.yMin ); + long ymax = ft_ceiling( bb.yMax ); + + double width = (xmax - xmin)/64.0; + double height = (ymax - ymin)/64.0; + + _scale = 1.0/height; + + double charHeight = char3d._maxY-char3d._minY; + double charWidth = char3d._maxX-char3d._minX; + + double dh = fabs(bb.yMin/64.0)/height; + double dw = fabs(bb.xMin/64.0)/width; + + _shiftY = char3d._minY + dh*charHeight; + _shiftX = char3d._minX + dw*charWidth; + + _charScale = 1/charHeight; + } +} + +FreeTypeFont3D::~FreeTypeFont3D() +{ + if(_face) + { + FreeTypeLibrary* freeTypeLibrary = FreeTypeLibrary::instance(); + if (freeTypeLibrary) + { + // remove myself from the local registry to ensure that + // not dangling pointers remain + freeTypeLibrary->removeFont3DImplmentation(this); + + // free the freetype font face itself + FT_Done_Face(_face); + _face = 0; + + // release memory held for FT_Face to work + if (_buffer) + { + delete [] _buffer; + _buffer = 0; + } + } + } +} + + +osgText::Font3D::Glyph3D * FreeTypeFont3D::getGlyph(unsigned int charcode) +{ + + + // + // GT: fix for symbol fonts (i.e. the Webdings font) as the wrong character are being + // returned, for symbol fonts in windows (FT_ENCONDING_MS_SYMBOL in freetype) the correct + // values are from 0xF000 to 0xF0FF not from 0x000 to 0x00FF (0 to 255) as you would expect. + // Microsoft uses a private field for its symbol fonts + // + unsigned int charindex = charcode; + if (_face->charmap != NULL) + { + if (_face->charmap->encoding == FT_ENCODING_MS_SYMBOL) + { + charindex |= 0xF000; + } + } + + FT_Error error = FT_Load_Char( _face, charindex, FT_LOAD_DEFAULT|_flags ); + if (error) + { + osg::notify(osg::WARN) << "FT_Load_Char(...) error 0x"<glyph->format != FT_GLYPH_FORMAT_OUTLINE) + { + osg::notify(osg::WARN) << "FreeTypeFont3D::getGlyph : not a vector font" << std::endl; + return 0; + } + + // ** init FreeType to describe the glyph + Char3DInfo char3d; + + FT_Outline outline = _face->glyph->outline; + FT_Outline_Funcs funcs; + funcs.conic_to = (FT_Outline_ConicToFunc)&conicTo; + funcs.line_to = (FT_Outline_LineToFunc)&lineTo; + funcs.cubic_to = (FT_Outline_CubicToFunc)&cubicTo; + funcs.move_to = (FT_Outline_MoveToFunc)&moveTo; + funcs.shift = 0; + funcs.delta = 0; + + // ** record description + FT_Error _error = FT_Outline_Decompose(&outline, &funcs, &char3d); + if (_error) + { + osg::notify(osg::WARN) << "FreeTypeFont3D::getGlyph : - outline decompose failed ..." << std::endl; + return 0; + } + + // ** create geometry for each part of the glyph + osg::ref_ptr frontGeo(new osg::Geometry); + frontGeo->setVertexArray(char3d.get()->getVertexArray()); + frontGeo->setPrimitiveSetList(char3d.get()->getPrimitiveSetList()); + + osg::ref_ptr wallGeo(new osg::Geometry); + wallGeo->setVertexArray(frontGeo->getVertexArray()); + + osg::ref_ptr backGeo(new osg::Geometry); + backGeo->setVertexArray(frontGeo->getVertexArray()); + + + + // ** for conveniance. + osg::Vec3Array * vertices = char3d._verts.get(); + + + + // ** duplicate the vertex for the back face + // ** with a depth equal to the font depth + std::size_t len = vertices->size(); + std::size_t dlen = len * 2; + + vertices->reserve(dlen); + + osg::Vec3Array::iterator begin = vertices->begin(); + osg::Vec3Array::iterator it = vertices->begin(); + + for (std::size_t i = 0; i != len; ++i, ++it) + vertices->push_back(*it); +// std::copy(begin, begin + len, begin + len + 1); TOCHECK + + + // ** and decal new vertices + unsigned int depth = _facade->getFontDepth(); + for (std::size_t i = len; i != dlen; ++i) + { + (*vertices)[i].z() -= depth; + } + + osg::Vec3Array::iterator end; + + // ** create wall and back face from the front polygon + // ** then accumulate them in the apropriate geometry wallGeo and backGeo + for (std::size_t i=0; i < frontGeo->getNumPrimitiveSets(); ++i) + { + // ** get the front polygon + osg::ref_ptr daFront(dynamic_cast(frontGeo->getPrimitiveSet(i))); + unsigned int idx = daFront->getFirst(); + unsigned int cnt = daFront->getCount(); + + // ** reverse vertices to draw the front face in the CCW + std::reverse(begin + idx, begin + idx + cnt); + + // ** create the back polygon + osg::ref_ptr daBack(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, idx + len, cnt)); + backGeo->addPrimitiveSet(daBack.get()); + + + // ** create the wall triangle strip + osg::ref_ptr deWall(new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP)); + wallGeo->addPrimitiveSet(deWall.get()); + + // ** link triangle strip + deWall->push_back(idx + len); + for (unsigned int j = 1; j < cnt; ++j) + { + deWall->push_back(idx + cnt - j); + deWall->push_back(idx + len + j); + } + deWall->push_back(idx); + deWall->push_back(idx + len); + deWall->push_back(idx + cnt - 1); + } + + // ** tesselate front and back face + { + osgUtil::Tessellator ts; + ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE); + ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY); + ts.retessellatePolygons(*frontGeo); + } + + { + osgUtil::Tessellator ts; + ts.setWindingType(osgUtil::Tessellator::TESS_WINDING_POSITIVE); + ts.setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY); + ts.retessellatePolygons(*backGeo); + } + + // ** generate normal + { + osgUtil::SmoothingVisitor sm; + osg::ref_ptr geode(new osg::Geode); + geode->addDrawable(wallGeo.get()); + geode->accept(sm); + } + + // ** save vertices and PrimitiveSetList of each face in the Glyph3D PrimitiveSet face list + osgText::Font3D::Glyph3D * glyph3D = new osgText::Font3D::Glyph3D(charcode); + + glyph3D->setVertexArray(dynamic_cast(frontGeo->getVertexArray())); + + glyph3D->getFrontPrimitiveSetList() = frontGeo->getPrimitiveSetList(); + glyph3D->getWallPrimitiveSetList() = wallGeo->getPrimitiveSetList(); + glyph3D->getBackPrimitiveSetList() = backGeo->getPrimitiveSetList(); + + + FT_Glyph_Metrics* metrics = &(_face->glyph->metrics); + + glyph3D->setHorizontalBearing(osg::Vec2((float)metrics->horiBearingX/64.0f,(float)(metrics->horiBearingY-metrics->height)/64.0f)); // bottom left. + glyph3D->setHorizontalAdvance((float)metrics->horiAdvance/64.0f); + glyph3D->setVerticalBearing(osg::Vec2((float)metrics->vertBearingX/64.0f,(float)(metrics->vertBearingY-metrics->height)/64.0f)); // top middle. + glyph3D->setVerticalAdvance((float)metrics->vertAdvance/64.0f); + + glyph3D->setWidth((float)metrics->width / 64.0f); + glyph3D->setHeight((float)metrics->height / 64.0f); + + + FT_BBox ftbb; + FT_Outline_Get_BBox(&outline, &ftbb); + + long xmin = ft_floor( ftbb.xMin ); + long xmax = ft_ceiling( ftbb.xMax ); + long ymin = ft_floor( ftbb.yMin ); + long ymax = ft_ceiling( ftbb.yMax ); + + osg::BoundingBox bb(xmin / 64.0f, ymin / 64.0f, 0.0f, xmax / 64.0f, ymax / 64.0f, 0.0f); + + glyph3D->setBoundingBox(bb); + + return glyph3D; +} + +osg::Vec2 FreeTypeFont3D::getKerning(unsigned int leftcharcode,unsigned int rightcharcode, osgText::KerningType kerningType) +{ + if (!FT_HAS_KERNING(_face) || (kerningType == osgText::KERNING_NONE)) return osg::Vec2(0.0f,0.0f); + + FT_Kerning_Mode mode = (kerningType==osgText::KERNING_DEFAULT) ? ft_kerning_default : ft_kerning_unfitted; + + // convert character code to glyph index + FT_UInt left = FT_Get_Char_Index( _face, leftcharcode ); + FT_UInt right = FT_Get_Char_Index( _face, rightcharcode ); + + // get the kerning distances. + FT_Vector kerning; + + FT_Error error = FT_Get_Kerning( _face, // handle to face object + left, // left glyph index + right, // right glyph index + mode, // kerning mode + &kerning ); // target vector + + if (error) + { + osg::notify(osg::WARN) << "FT_Get_Kerning(...) returned error code " < + +#include +#include FT_FREETYPE_H + +class FreeTypeFont3D : public osgText::Font3D::Font3DImplementation +{ +// declare the interface to a font. +public: + + FreeTypeFont3D(const std::string& filename, FT_Face face, unsigned int flags); + FreeTypeFont3D(FT_Byte* buffer, FT_Face face, unsigned int flags); + + virtual std::string getFileName() const { return _filename; } + +// virtual void setFontResolution(unsigned int width, unsigned int height, unsigned int depth); + + virtual osgText::Font3D::Glyph3D * getGlyph(unsigned int charcode); + + virtual osg::Vec2 getKerning(unsigned int leftcharcode,unsigned int rightcharcode, osgText::KerningType _kerningType); + + virtual bool hasVertical() const; + + virtual float getScale() const; + +protected: + + void init(); + + long ft_round( long x ) { return (( x + 32 ) & -64); } + long ft_floor( long x ) { return (x & -64); } + long ft_ceiling( long x ){ return (( x + 63 ) & -64); } + + virtual ~FreeTypeFont3D(); + + std::string _filename; + FT_Byte* _buffer; + FT_Face _face; + unsigned int _flags; + + + double _scale; + double _shiftY; + double _shiftX; + double _charScale; +}; + +#endif diff --git a/src/osgPlugins/freetype/FreeTypeFontBase.cpp b/src/osgPlugins/freetype/FreeTypeFontBase.cpp new file mode 100644 index 000000000..25219e858 --- /dev/null +++ b/src/osgPlugins/freetype/FreeTypeFontBase.cpp @@ -0,0 +1,224 @@ +/* -*-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 "FreeTypeFont.h" +#include "FreeTypeLibrary.h" + +#include +#include + +FreeTypeFont::FreeTypeFont(const std::string& filename, FT_Face face, unsigned int flags): + _filename(filename), + _buffer(0), + _face(face), + _flags(flags) +{ +} + +FreeTypeFont::FreeTypeFont(FT_Byte* buffer, FT_Face face, unsigned int flags): + _filename(""), + _buffer(buffer), + _face(face), + _flags(flags) +{ +} + +FreeTypeFont::~FreeTypeFont() +{ + if(_face) + { + FreeTypeLibrary* freeTypeLibrary = FreeTypeLibrary::instance(); + if (freeTypeLibrary) + { + // remove myself from the local registry to ensure that + // not dangling pointers remain + freeTypeLibrary->removeFontImplmentation(this); + + // free the freetype font face itself + FT_Done_Face(_face); + _face = 0; + + // release memory held for FT_Face to work + if (_buffer) + { + delete [] _buffer; + _buffer = 0; + } + } + } +} + +void FreeTypeFont::setFontResolution(unsigned int width, unsigned int height) +{ + int maxAxis = std::max(width, height); + int margin = _facade->getGlyphImageMargin() + (int)((float)maxAxis * _facade->getGlyphImageMarginRatio()); + + if (width+2*margin>_facade->getTextureWidthHint() || + width+2*margin>_facade->getTextureHeightHint()) + { + osg::notify(osg::WARN)<<"Warning: FreeTypeFont::setSize("<getTextureWidthHint()-2*margin; + height = _facade->getTextureHeightHint()-2*margin; + + osg::notify(osg::WARN)<<" sizes capped ("<charmap != NULL) + { + if (_face->charmap->encoding == FT_ENCODING_MS_SYMBOL) + { + charindex |= 0xF000; + } + } + + FT_Error error = FT_Load_Char( _face, charindex, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP|_flags ); + if (error) + { + osg::notify(osg::WARN) << "FT_Load_Char(...) error 0x"<glyph; + + int pitch = glyphslot->bitmap.pitch; + unsigned char* buffer = glyphslot->bitmap.buffer; + + unsigned int sourceWidth = glyphslot->bitmap.width;; + unsigned int sourceHeight = glyphslot->bitmap.rows; + + unsigned int width = sourceWidth; + unsigned int height = sourceHeight; + + osg::ref_ptr glyph = new osgText::Font::Glyph; + + unsigned int dataSize = width*height; + unsigned char* data = new unsigned char[dataSize]; + + + // clear the image to zeros. + for(unsigned char* p=data;psetImage(width,height,1, + GL_ALPHA, + GL_ALPHA,GL_UNSIGNED_BYTE, + data, + osg::Image::USE_NEW_DELETE, + 1); + + glyph->setInternalTextureFormat(GL_ALPHA); + + // copy image across to osgText::Glyph image. + switch(glyphslot->bitmap.pixel_mode) + { + case FT_PIXEL_MODE_MONO: + for(int r=sourceHeight-1;r>=0;--r) + { + unsigned char* ptr = buffer+r*pitch; + for(unsigned int c=0;c> 3] & (1 << (~c & 7))) ? 255 : 0; + } + } + break; + + + case FT_PIXEL_MODE_GRAY: + for(int r=sourceHeight-1;r>=0;--r) + { + unsigned char* ptr = buffer+r*pitch; + for(unsigned int c=0;cbitmap.pixel_mode << std::endl; + } + + + FT_Glyph_Metrics* metrics = &(glyphslot->metrics); + + glyph->setHorizontalBearing(osg::Vec2((float)metrics->horiBearingX/64.0f,(float)(metrics->horiBearingY-metrics->height)/64.0f)); // bottom left. + glyph->setHorizontalAdvance((float)metrics->horiAdvance/64.0f); + glyph->setVerticalBearing(osg::Vec2((float)metrics->vertBearingX/64.0f,(float)(metrics->vertBearingY-metrics->height)/64.0f)); // top middle. + glyph->setVerticalAdvance((float)metrics->vertAdvance/64.0f); + + addGlyph(_facade->getFontWidth(),_facade->getFontHeight(),charcode,glyph.get()); + +// cout << " in getGlyph() implementation="< +#include FT_FREETYPE_H + +class FreeTypeFontBase : public osgText::Font::FontImplementation +{ +// declare the interface to a font. +public: + + FreeTypeFont(const std::string& filename, FT_Face face, unsigned int flags); + FreeTypeFont(FT_Byte* buffer, FT_Face face, unsigned int flags); + + virtual ~FreeTypeFont(); + + virtual std::string getFileName() const { return _filename; } + + virtual void setFontResolution(unsigned int width, unsigned int height); + + virtual osg::Vec2 getKerning(unsigned int leftcharcode,unsigned int rightcharcode, osgText::KerningType _kerningType); + + virtual bool hasVertical() const; + +protected: + + std::string _filename; + FT_Byte* _buffer; + FT_Face _face; + unsigned int _flags; +}; + +#endif diff --git a/src/osgPlugins/freetype/FreeTypeLibrary.cpp b/src/osgPlugins/freetype/FreeTypeLibrary.cpp index e4361668c..60ba7991c 100644 --- a/src/osgPlugins/freetype/FreeTypeLibrary.cpp +++ b/src/osgPlugins/freetype/FreeTypeLibrary.cpp @@ -13,7 +13,7 @@ #include "FreeTypeLibrary.h" #include -#include +//#include //#define PRINT_OUT_FONT_DETAILS #ifdef PRINT_OUT_FONT_DETAILS @@ -24,6 +24,7 @@ FreeTypeLibrary::FreeTypeLibrary() { + osg::notify(osg::INFO) << "FreeTypeLibrary::FreeTypeLibrary()" << std::endl; FT_Error error = FT_Init_FreeType( &_ftlibrary ); if (error) { @@ -47,6 +48,14 @@ FreeTypeLibrary::~FreeTypeLibrary() font->setImplementation(0); } + while(!_font3DImplementationSet.empty()) + { + FreeTypeFont3D* font3DImplementation = *_font3DImplementationSet.begin(); + _font3DImplementationSet.erase(_font3DImplementationSet.begin()); + osgText::Font3D* font3D = font3DImplementation->_facade; + font3D->setImplementation(0); + } + FT_Done_FreeType( _ftlibrary); } @@ -56,22 +65,20 @@ FreeTypeLibrary* FreeTypeLibrary::instance() return s_library.get(); } -osgText::Font* FreeTypeLibrary::getFont(const std::string& fontfile,unsigned int index, unsigned int flags) +bool FreeTypeLibrary::getFace(const std::string& fontfile,unsigned int index, FT_Face & face) { - - FT_Face face; /* handle to face object */ FT_Error error = FT_New_Face( _ftlibrary, fontfile.c_str(), index, &face ); if (error == FT_Err_Unknown_File_Format) { osg::notify(osg::WARN)<<" .... the font file could be opened and read, but it appears"< +#include "FreeTypeFont3D.h" + #include #include +#include +#include + +#include + class FreeTypeLibrary : public osg::Referenced { public: @@ -32,10 +38,19 @@ public: osgText::Font* getFont(const std::string& fontfile,unsigned int index=0, unsigned int flags=0); osgText::Font* getFont(std::istream& fontstream, unsigned int index=0, unsigned int flags=0); + osgText::Font3D* getFont3D(const std::string& fontfile, unsigned int index=0, unsigned int flags=0); + osgText::Font3D* getFont3D(std::istream& fontstream, unsigned int index=0, unsigned int flags=0); + void removeFontImplmentation(FreeTypeFont* fontImpl) { _fontImplementationSet.erase(fontImpl); } + void removeFont3DImplmentation(FreeTypeFont3D* font3DImpl) { _font3DImplementationSet.erase(font3DImpl); } protected: + /** common method to load a FT_Face from a file*/ + bool getFace(const std::string& fontfile,unsigned int index, FT_Face & face); + /** common method to load a FT_Face from a stream */ + FT_Byte* getFace(std::istream& fontstream, unsigned int index, FT_Face & face); + /** Verify the correct character mapping for MS windows */ void verifyCharacterMap(FT_Face face); @@ -44,10 +59,11 @@ protected: FreeTypeLibrary(); typedef std::set< FreeTypeFont* > FontImplementationSet; - + typedef std::set< FreeTypeFont3D* > Font3DImplementationSet; FT_Library _ftlibrary; FontImplementationSet _fontImplementationSet; + Font3DImplementationSet _font3DImplementationSet; }; diff --git a/src/osgPlugins/freetype/ReaderWriterFreeType.cpp b/src/osgPlugins/freetype/ReaderWriterFreeType.cpp index b72f3165c..a67e39a91 100644 --- a/src/osgPlugins/freetype/ReaderWriterFreeType.cpp +++ b/src/osgPlugins/freetype/ReaderWriterFreeType.cpp @@ -49,7 +49,10 @@ class ReaderWriterFreeType : public osgDB::ReaderWriter return ReadResult::ERROR_IN_READING_FILE; } - return freeTypeLibrary->getFont(fileName,0,getFlags(options)); + if (options->getPluginData("3D")) + return freeTypeLibrary->getFont3D(fileName,0,getFlags(options)); + else + return freeTypeLibrary->getFont(fileName,0,getFlags(options)); } virtual ReadResult readObject(std::istream& stream, const osgDB::ReaderWriter::Options* options) const diff --git a/src/osgPlugins/jp2/ReaderWriterJP2.cpp b/src/osgPlugins/jp2/ReaderWriterJP2.cpp index 954a28590..9f2568743 100644 --- a/src/osgPlugins/jp2/ReaderWriterJP2.cpp +++ b/src/osgPlugins/jp2/ReaderWriterJP2.cpp @@ -170,6 +170,17 @@ extern "C" { class ReaderWriterJP2 : public osgDB::ReaderWriter { public: + + + ReaderWriterJP2() + { + // little dance here to get around warnings created by jas_image_strtofmt use of char* rather than const char* + // as a parameted and modern compilers deprecating "jp2" string being treated as char*. + char* jp2 = strdup("jp2"); + _fmt_jp2 = jas_image_strtofmt(jp2); + free(jp2); + } + virtual const char* className() const { return "RGB Image Reader/Writer"; } virtual bool acceptsExtension(const std::string& extension) const @@ -411,7 +422,7 @@ class ReaderWriterJP2 : public osgDB::ReaderWriter opt = new char[options->getOptionString().size() + 1]; strcpy(opt, options->getOptionString().c_str()); } - jas_image_encode(jimage, out, jas_image_strtofmt("jp2"), opt); + jas_image_encode(jimage, out, _fmt_jp2, opt); if(opt) delete[] opt; jas_stream_flush(out); @@ -491,7 +502,8 @@ class ReaderWriterJP2 : public osgDB::ReaderWriter opt = new char[options->getOptionString().size() + 1]; strcpy(opt, options->getOptionString().c_str()); } - jas_image_encode(jimage, out, jas_image_strtofmt("jp2"), opt); + + jas_image_encode(jimage, out, _fmt_jp2, opt); if(opt) delete[] opt; jas_stream_flush(out); @@ -511,6 +523,8 @@ class ReaderWriterJP2 : public osgDB::ReaderWriter return WriteResult::FILE_SAVED; } + + int _fmt_jp2; }; // now register with Registry to instantiate the above diff --git a/src/osgPlugins/osgText/CMakeLists.txt b/src/osgPlugins/osgText/CMakeLists.txt index 96583428b..adf69ac36 100644 --- a/src/osgPlugins/osgText/CMakeLists.txt +++ b/src/osgPlugins/osgText/CMakeLists.txt @@ -1,7 +1,7 @@ #this file is automatically generated -SET(TARGET_SRC IO_Text.cpp ) +SET(TARGET_SRC IO_TextBase.cpp IO_Text.cpp IO_Text3D.cpp ) SET(TARGET_ADDED_LIBRARIES osgText ) #### end var setup ### SETUP_PLUGIN(osgtext) diff --git a/src/osgPlugins/osgText/IO_Text.cpp b/src/osgPlugins/osgText/IO_Text.cpp index c1e37f7e7..cdc749fd0 100644 --- a/src/osgPlugins/osgText/IO_Text.cpp +++ b/src/osgPlugins/osgText/IO_Text.cpp @@ -20,11 +20,84 @@ osgDB::RegisterDotOsgWrapperProxy Text_Proxy ( new osgText::Text, "Text", - "Object Drawable Text", + "Object Drawable TextBase Text", Text_readLocalData, Text_writeLocalData ); + +osgText::Text::BackdropType convertBackdropTypeStringToEnum(std::string & str) +{ + if (str=="DROP_SHADOW_BOTTOM_RIGHT") return osgText::Text::DROP_SHADOW_BOTTOM_RIGHT; + else if (str=="DROP_SHADOW_CENTER_RIGHT") return osgText::Text::DROP_SHADOW_CENTER_RIGHT; + else if (str=="DROP_SHADOW_TOP_RIGHT") return osgText::Text::DROP_SHADOW_TOP_RIGHT; + else if (str=="DROP_SHADOW_BOTTOM_CENTER") return osgText::Text::DROP_SHADOW_BOTTOM_CENTER; + else if (str=="DROP_SHADOW_TOP_CENTER") return osgText::Text::DROP_SHADOW_TOP_CENTER; + else if (str=="DROP_SHADOW_BOTTOM_LEFT") return osgText::Text::DROP_SHADOW_BOTTOM_LEFT; + else if (str=="DROP_SHADOW_CENTER_LEFT") return osgText::Text::DROP_SHADOW_CENTER_LEFT; + else if (str=="DROP_SHADOW_TOP_LEFT") return osgText::Text::DROP_SHADOW_TOP_LEFT; + else if (str=="OUTLINE") return osgText::Text::OUTLINE; + else if (str=="NONE") return osgText::Text::NONE; + else return static_cast(-1); +} +std::string convertBackdropTypeEnumToString(osgText::Text::BackdropType backdropType) +{ + switch (backdropType) + { + case osgText::Text::DROP_SHADOW_BOTTOM_RIGHT: return "DROP_SHADOW_BOTTOM_RIGHT"; + case osgText::Text::DROP_SHADOW_CENTER_RIGHT: return "DROP_SHADOW_CENTER_RIGHT"; + case osgText::Text::DROP_SHADOW_TOP_RIGHT: return "DROP_SHADOW_TOP_RIGHT"; + case osgText::Text::DROP_SHADOW_BOTTOM_CENTER: return "DROP_SHADOW_BOTTOM_CENTER"; + case osgText::Text::DROP_SHADOW_TOP_CENTER: return "DROP_SHADOW_TOP_CENTER"; + case osgText::Text::DROP_SHADOW_BOTTOM_LEFT: return "DROP_SHADOW_BOTTOM_LEFT"; + case osgText::Text::DROP_SHADOW_CENTER_LEFT: return "DROP_SHADOW_CENTER_LEFT"; + case osgText::Text::DROP_SHADOW_TOP_LEFT: return "DROP_SHADOW_TOP_LEFT"; + case osgText::Text::OUTLINE: return "OUTLINE"; + case osgText::Text::NONE: return "NONE"; + default : return ""; + } +} + + +osgText::Text::BackdropImplementation convertBackdropImplementationStringToEnum(std::string & str) +{ + if (str=="POLYGON_OFFSET") return osgText::Text::POLYGON_OFFSET; + else if (str=="NO_DEPTH_BUFFER") return osgText::Text::NO_DEPTH_BUFFER; + else if (str=="DEPTH_RANGE") return osgText::Text::DEPTH_RANGE; + else if (str=="STENCIL_BUFFER") return osgText::Text::STENCIL_BUFFER; + else return static_cast(-1); +} +std::string convertBackdropImplementationEnumToString(osgText::Text::BackdropImplementation backdropImplementation) +{ + switch (backdropImplementation) + { + case osgText::Text::POLYGON_OFFSET: return "POLYGON_OFFSET"; + case osgText::Text::NO_DEPTH_BUFFER: return "NO_DEPTH_BUFFER"; + case osgText::Text::DEPTH_RANGE: return "DEPTH_RANGE"; + case osgText::Text::STENCIL_BUFFER: return "STENCIL_BUFFER"; + default : return ""; + } +} + +osgText::Text::ColorGradientMode convertColorGradientModeStringToEnum(std::string & str) +{ + if (str=="SOLID") return osgText::Text::SOLID; + else if (str=="PER_CHARACTER") return osgText::Text::PER_CHARACTER; + else if (str=="OVERALL") return osgText::Text::OVERALL; + else return static_cast(-1); +} +std::string convertColorGradientModeEnumToString(osgText::Text::ColorGradientMode colorGradientMode) +{ + switch (colorGradientMode) + { + case osgText::Text::SOLID: return "SOLID"; + case osgText::Text::PER_CHARACTER: return "PER_CHARACTER"; + case osgText::Text::OVERALL: return "OVERALL"; + default : return ""; + } +} + + bool Text_readLocalData(osg::Object &obj, osgDB::Input &fr) { osgText::Text &text = static_cast(obj); @@ -39,157 +112,6 @@ bool Text_readLocalData(osg::Object &obj, osgDB::Input &fr) } - if (fr[0].matchWord("fontResolution") || fr[0].matchWord("fontSize")) - { - unsigned int width; - unsigned int height; - if (fr[1].getUInt(width) && fr[2].getUInt(height)) - { - text.setFontResolution(width,height); - fr += 3; - itAdvanced = true; - } - } - - if (fr[0].matchWord("characterSize")) - { - float height; - float aspectRatio; - if (fr[1].getFloat(height) && fr[2].getFloat(aspectRatio)) - { - text.setCharacterSize(height,aspectRatio); - fr += 3; - itAdvanced = true; - } - } - - if (fr.matchSequence("characterSizeMode %w")) - { - std::string str = fr[1].getStr(); - if (str=="OBJECT_COORDS") text.setCharacterSizeMode(osgText::Text::OBJECT_COORDS); - else if (str=="SCREEN_COORDS") text.setCharacterSizeMode(osgText::Text::SCREEN_COORDS); - else if (str=="OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT") text.setCharacterSizeMode(osgText::Text::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT); - } - - // maximum dimentsions of text box. - if (fr[0].matchWord("maximumWidth")) - { - float width; - if (fr[1].getFloat(width)) - { - text.setMaximumWidth(width); - fr += 2; - itAdvanced = true; - } - } - - if (fr[0].matchWord("maximumHeight")) - { - float height; - if (fr[1].getFloat(height)) - { - text.setMaximumHeight(height); - fr += 2; - itAdvanced = true; - } - } - - if (fr[0].matchWord("lineSpacing")) - { - float height; - if (fr[1].getFloat(height)) - { - text.setLineSpacing(height); - fr += 2; - itAdvanced = true; - } - } - - if (fr.matchSequence("alignment %w")) - { - std::string str = fr[1].getStr(); - if (str=="LEFT_TOP") text.setAlignment(osgText::Text::LEFT_TOP); - else if (str=="LEFT_CENTER") text.setAlignment(osgText::Text::LEFT_CENTER); - else if (str=="LEFT_BOTTOM") text.setAlignment(osgText::Text::LEFT_BOTTOM); - else if (str=="CENTER_TOP") text.setAlignment(osgText::Text::CENTER_TOP); - else if (str=="CENTER_CENTER") text.setAlignment(osgText::Text::CENTER_CENTER); - else if (str=="CENTER_BOTTOM") text.setAlignment(osgText::Text::CENTER_BOTTOM); - else if (str=="RIGHT_TOP") text.setAlignment(osgText::Text::RIGHT_TOP); - else if (str=="RIGHT_CENTER") text.setAlignment(osgText::Text::RIGHT_CENTER); - else if (str=="RIGHT_BOTTOM") text.setAlignment(osgText::Text::RIGHT_BOTTOM); - else if (str=="LEFT_BASE_LINE") text.setAlignment(osgText::Text::LEFT_BASE_LINE); - else if (str=="CENTER_BASE_LINE") text.setAlignment(osgText::Text::CENTER_BASE_LINE); - else if (str=="RIGHT_BASE_LINE") text.setAlignment(osgText::Text::RIGHT_BASE_LINE); - else if (str=="LEFT_BOTTOM_BASE_LINE") text.setAlignment(osgText::Text::LEFT_BOTTOM_BASE_LINE); - else if (str=="CENTER_BOTTOM_BASE_LINE") text.setAlignment(osgText::Text::CENTER_BOTTOM_BASE_LINE); - else if (str=="RIGHT_BOTTOM_BASE_LINE") text.setAlignment(osgText::Text::RIGHT_BOTTOM_BASE_LINE); - else if (str=="BASE_LINE") text.setAlignment(osgText::Text::BASE_LINE); - fr += 2; - itAdvanced = true; - } - - if (fr.matchSequence("axisAlignment %w")) - { - std::string str = fr[1].getStr(); - if (str=="XY_PLANE") text.setAxisAlignment(osgText::Text::XY_PLANE); - else if (str=="REVERSED_XY_PLANE") text.setAxisAlignment(osgText::Text::REVERSED_XY_PLANE); - else if (str=="XZ_PLANE") text.setAxisAlignment(osgText::Text::XZ_PLANE); - else if (str=="REVERSED_XZ_PLANE") text.setAxisAlignment(osgText::Text::REVERSED_XZ_PLANE); - else if (str=="YZ_PLANE") text.setAxisAlignment(osgText::Text::YZ_PLANE); - else if (str=="REVERSED_YZ_PLANE") text.setAxisAlignment(osgText::Text::REVERSED_YZ_PLANE); - else if (str=="SCREEN") text.setAxisAlignment(osgText::Text::SCREEN); - fr += 2; - itAdvanced = true; - } - - if (fr.matchSequence("rotation")) - { - osg::Vec4 rotation; - if (fr[1].getFloat(rotation.x()) && fr[2].getFloat(rotation.y()) && fr[3].getFloat(rotation.z()) && fr[4].getFloat(rotation.w())) - { - text.setRotation(rotation); - fr += 4; - itAdvanced = true; - } - } - - if (fr.matchSequence("autoRotateToScreen TRUE")) - { - text.setAutoRotateToScreen(true); - fr += 2; - itAdvanced = true; - } - - if (fr.matchSequence("autoScaleToLimitScreenSizeToFontResolution TRUE")) - { - text.setCharacterSizeMode(osgText::Text::SCREEN_COORDS); - fr += 2; - itAdvanced = true; - } - - if (fr.matchSequence("layout %w") && fr[1].getStr()) - { - std::string str = fr[1].getStr(); - if (str=="LEFT_TO_RIGHT") text.setLayout(osgText::Text::LEFT_TO_RIGHT); - else if (str=="RIGHT_TO_LEFT") text.setLayout(osgText::Text::RIGHT_TO_LEFT); - else if (str=="VERTICAL") text.setLayout(osgText::Text::VERTICAL); - fr += 2; - itAdvanced = true; - } - - - // position - if (fr[0].matchWord("position")) - { - osg::Vec3 p; - if (fr[1].getFloat(p.x()) && fr[2].getFloat(p.y()) && fr[3].getFloat(p.z())) - { - text.setPosition(p); - fr += 4; - itAdvanced = true; - } - } - // color if (fr[0].matchWord("color")) { @@ -202,57 +124,137 @@ bool Text_readLocalData(osg::Object &obj, osgDB::Input &fr) } } - // draw mode - if (fr[0].matchWord("drawMode")) + // backdropType + if (fr[0].matchWord("backdropType")) { - int i; - if (fr[1].getInt(i)) { - text.setDrawMode(i); + std::string str = fr[1].getStr(); + osgText::Text::BackdropType backdropType = convertBackdropTypeStringToEnum(str); + + if (backdropType != static_cast(-1)) + text.setBackdropType(backdropType); + + fr += 2; + itAdvanced = true; + } + + float backdropHorizontalOffset = text.getBackdropHorizontalOffset(); + float backdropVerticalOffset = text.getBackdropVerticalOffset(); + + // backdropHorizontalOffset + if (fr[0].matchWord("backdropHorizontalOffset")) + { + if (fr[1].getFloat(backdropHorizontalOffset)) + { fr += 2; itAdvanced = true; } } - // text - if (fr.matchSequence("text %s") && fr[1].getStr()) { - text.setText(std::string(fr[1].getStr())); + // backdropVerticalOffset + if (fr[0].matchWord("backdropVerticalOffset")) + { + if (fr[1].getFloat(backdropVerticalOffset)) + { + fr += 2; + itAdvanced = true; + } + } + text.setBackdropOffset(backdropHorizontalOffset, backdropVerticalOffset); + + // backdropColor + if (fr[0].matchWord("backdropColor")) + { + osg::Vec4 c; + if (fr[1].getFloat(c.x()) && fr[2].getFloat(c.y()) && fr[3].getFloat(c.z()) && fr[4].getFloat(c.w())) + { + text.setBackdropColor(c); + fr += 4; + itAdvanced = true; + } + } + + // backdropImplementation + if (fr[0].matchWord("backdropImplementation")) + { + std::string str = fr[1].getStr(); + osgText::Text::BackdropImplementation backdropImplementation = convertBackdropImplementationStringToEnum(str); + + if (backdropImplementation != static_cast(-1)) + text.setBackdropImplementation(backdropImplementation); + fr += 2; itAdvanced = true; } - - if (fr.matchSequence("text %i {")) + + // ColorGradientMode + if (fr[0].matchWord("colorGradientMode")) { - // pre 0.9.3 releases.. - int entry = fr[0].getNoNestedBrackets(); - - int capacity; - fr[1].getInt(capacity); - - osgText::String str; - str.reserve(capacity); - - fr += 3; - - while (!fr.eof() && fr[0].getNoNestedBrackets()>entry) - { - unsigned int c; - if (fr[0].getUInt(c)) - { - ++fr; - str.push_back(c); - } - else - { - ++fr; - } - } - - text.setText(str); - + std::string str = fr[1].getStr(); + osgText::Text::ColorGradientMode colorGradientMode = convertColorGradientModeStringToEnum(str); + + if (colorGradientMode != static_cast(-1)) + text.setColorGradientMode(colorGradientMode); + + fr += 2; itAdvanced = true; - ++fr; + } + + // ** get default value; + osg::Vec4 colorGradientTopLeft = text.getColorGradientTopLeft(); + osg::Vec4 colorGradientBottomLeft = text.getColorGradientBottomLeft(); + osg::Vec4 colorGradientBottomRight = text.getColorGradientBottomRight(); + osg::Vec4 colorGradientTopRight = text.getColorGradientTopRight(); + + // colorGradientTopLeft + if (fr[0].matchWord("colorGradientTopLeft")) + { + osg::Vec4 c; + if (fr[1].getFloat(c.x()) && fr[2].getFloat(c.y()) && fr[3].getFloat(c.z()) && fr[4].getFloat(c.w())) + { + colorGradientTopLeft = c; + fr += 4; + itAdvanced = true; + } } + // colorGradientBottomLeft + if (fr[0].matchWord("colorGradientBottomLeft")) + { + osg::Vec4 c; + if (fr[1].getFloat(c.x()) && fr[2].getFloat(c.y()) && fr[3].getFloat(c.z()) && fr[4].getFloat(c.w())) + { + colorGradientBottomLeft = c; + fr += 4; + itAdvanced = true; + } + } + + // colorGradientBottomRight + if (fr[0].matchWord("colorGradientBottomRight")) + { + osg::Vec4 c; + if (fr[1].getFloat(c.x()) && fr[2].getFloat(c.y()) && fr[3].getFloat(c.z()) && fr[4].getFloat(c.w())) + { + colorGradientBottomRight = c; + fr += 4; + itAdvanced = true; + } + } + + // colorGradientTopRight + if (fr[0].matchWord("colorGradientTopRight")) + { + osg::Vec4 c; + if (fr[1].getFloat(c.x()) && fr[2].getFloat(c.y()) && fr[3].getFloat(c.z()) && fr[4].getFloat(c.w())) + { + colorGradientTopRight = c; + fr += 4; + itAdvanced = true; + } + } + + text.setColorGradientCorners(colorGradientTopLeft, colorGradientBottomLeft, colorGradientBottomRight, colorGradientTopRight); + return itAdvanced; } @@ -265,126 +267,44 @@ bool Text_writeLocalData(const osg::Object &obj, osgDB::Output &fw) fw.indent() << "font " << text.getFont()->getFileName() << std::endl; } - // font resolution - fw.indent() << "fontResolution " << text.getFontWidth() << " " << text.getFontHeight() << std::endl; - - // charater size. - fw.indent() << "characterSize " << text.getCharacterHeight() << " " << text.getCharacterAspectRatio() << std::endl; - - fw.indent() << "characterSizeMode "; - switch(text.getCharacterSizeMode()) - { - case osgText::Text::OBJECT_COORDS : fw<<"OBJECT_COORDS"<0.0f) - { - fw.indent() << "maximumWidth " << text.getMaximumWidth() << std::endl; - } - - if (text.getMaximumHeight()>0.0f) - { - fw.indent() << "maximumHeight " << text.getMaximumHeight() << std::endl; - } - - if (text.getLineSpacing()>0.0f) - { - fw.indent() << "lineSpacing " << text.getLineSpacing() << std::endl; - } - - // alignment - fw.indent() << "alignment "; - switch(text.getAlignment()) - { - case osgText::Text::LEFT_TOP: fw << "LEFT_TOP" << std::endl; break; - case osgText::Text::LEFT_CENTER : fw << "LEFT_CENTER" << std::endl; break; - case osgText::Text::LEFT_BOTTOM : fw << "LEFT_BOTTOM" << std::endl; break; - - case osgText::Text::CENTER_TOP: fw << "CENTER_TOP" << std::endl; break; - case osgText::Text::CENTER_CENTER: fw << "CENTER_CENTER" << std::endl; break; - case osgText::Text::CENTER_BOTTOM: fw << "CENTER_BOTTOM" << std::endl; break; - - case osgText::Text::RIGHT_TOP: fw << "RIGHT_TOP" << std::endl; break; - case osgText::Text::RIGHT_CENTER: fw << "RIGHT_CENTER" << std::endl; break; - case osgText::Text::RIGHT_BOTTOM: fw << "RIGHT_BOTTOM" << std::endl; break; - - case osgText::Text::LEFT_BASE_LINE: fw << "LEFT_BASE_LINE" << std::endl; break; - case osgText::Text::CENTER_BASE_LINE:fw << "CENTER_BASE_LINE" << std::endl; break; - case osgText::Text::RIGHT_BASE_LINE: fw << "RIGHT_BASE_LINE" << std::endl; break; - - case osgText::Text::LEFT_BOTTOM_BASE_LINE: fw << "LEFT_BOTTOM_BASE_LINE" << std::endl; break; - case osgText::Text::CENTER_BOTTOM_BASE_LINE:fw << "CENTER_BOTTOM_BASE_LINE" << std::endl; break; - case osgText::Text::RIGHT_BOTTOM_BASE_LINE: fw << "RIGHT_BOTTOM_BASE_LINE" << std::endl; break; - }; - - - if (!text.getRotation().zeroRotation()) - { - fw.indent() << "rotation " << text.getRotation() << std::endl; - } - - if (text.getAutoRotateToScreen()) - { - fw.indent() << "autoRotateToScreen TRUE"<< std::endl; - } - - - // layout - fw.indent() << "layout "; - switch(text.getLayout()) - { - case osgText::Text::LEFT_TO_RIGHT: fw << "LEFT_TO_RIGHT" << std::endl; break; - case osgText::Text::RIGHT_TO_LEFT: fw << "RIGHT_TO_LEFT" << std::endl; break; - case osgText::Text::VERTICAL: fw << "VERTICAL" << std::endl; break; - }; - - - // position - osg::Vec3 p = text.getPosition(); - fw.indent() << "position " << p.x() << " " << p.y() << " " << p.z() << std::endl; - // color osg::Vec4 c = text.getColor(); fw.indent() << "color " << c.x() << " " << c.y() << " " << c.z() << " " << c.w() << std::endl; - // draw mode - fw.indent() << "drawMode " << text.getDrawMode() << std::endl; - - - // text - const osgText::String& textstring = text.getText(); - bool isACString = true; - osgText::String::const_iterator itr; - for(itr=textstring.begin(); - itr!=textstring.end() && isACString; - ++itr) - { - if (*itr==0 || *itr>256) isACString=false; - } - if (isACString) - { - std::string str; - - for(itr=textstring.begin(); - itr!=textstring.end(); - ++itr) - { - str += (char)(*itr); - } - - //std::copy(textstring.begin(),textstring.end(),std::back_inserter(str)); + // backdropType + fw.indent() << "backdropType " << convertBackdropTypeEnumToString(text.getBackdropType()) << std::endl; + + // backdropHorizontalOffset + fw.indent() << "backdropHorizontalOffset " << text.getBackdropHorizontalOffset() << std::endl; + + // backdropVerticalOffset + fw.indent() << "backdropVerticalOffset " << text.getBackdropVerticalOffset() << std::endl; + + // backdropColor + c = text.getBackdropColor(); + fw.indent() << "backdropColor " << c.x() << " " << c.y() << " " << c.z() << " " << c.w() << std::endl; + + // backdropImplementation + fw.indent() << "backdropImplementation " << convertBackdropImplementationEnumToString(text.getBackdropImplementation()) << std::endl; + + // colorGradientMode + fw.indent() << "colorGradientMode " << convertColorGradientModeEnumToString(text.getColorGradientMode()) << std::endl; + + // colorGradientTopLeft + c = text.getColorGradientTopLeft(); + fw.indent() << "colorGradientTopLeft " << c.x() << " " << c.y() << " " << c.z() << " " << c.w() << std::endl; + + // colorGradientBottomLeft + c = text.getColorGradientBottomLeft(); + fw.indent() << "colorGradientBottomLeft " << c.x() << " " << c.y() << " " << c.z() << " " << c.w() << std::endl; + + // colorGradientBottomRight + c = text.getColorGradientBottomRight(); + fw.indent() << "colorGradientBottomRight " << c.x() << " " << c.y() << " " << c.z() << " " << c.w() << std::endl; - fw.indent() << "text " << fw.wrapString(str) << std::endl; - } - else - { - // do it the hardway...output each character as an int - fw.indent() << "text "< +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +bool Text3D_readLocalData(osg::Object &obj, osgDB::Input &fr); +bool Text3D_writeLocalData(const osg::Object &obj, osgDB::Output &fw); + +osgDB::RegisterDotOsgWrapperProxy Text3D_Proxy +( + new osgText::Text3D, + "Text3D", + "Object Drawable TextBase Text3D", + Text3D_readLocalData, + Text3D_writeLocalData +); + +osgText::Text3D::RenderMode convertRenderModeStringToEnum(const std::string str) +{ + if (str == "PER_GLYPH") return osgText::Text3D::PER_GLYPH; + else if (str == "PER_FACE") return osgText::Text3D::PER_FACE; + return static_cast(-1); +} +std::string convertRenderModeEnumToString(osgText::Text3D::RenderMode renderMode) +{ + switch (renderMode) + { + case osgText::Text3D::PER_GLYPH: return "PER_GLYPH"; + case osgText::Text3D::PER_FACE: return "PER_FACE"; + default: return ""; + } +} + +bool Text3D_readLocalData(osg::Object &obj, osgDB::Input &fr) +{ + osgText::Text3D &text = static_cast(obj); + bool itAdvanced = false; + + // font + if (fr.matchSequence("font %w")) + { + text.setFont(fr[1].getStr()); + fr += 2; + itAdvanced = true; + + } + + // characterDepth + if (fr[0].matchWord("characterDepth")) + { + float depth; + if (fr[1].getFloat(depth)) + { + text.setCharacterDepth(depth); + fr += 2; + itAdvanced = true; + } + } + + // RenderMode + if (fr[0].matchWord("renderMode")) + { + osgText::Text3D::RenderMode renderMode = convertRenderModeStringToEnum(fr[1].getStr()); + if (renderMode != static_cast(-1)) + { + text.setRenderMode(renderMode); + } + fr += 2; + itAdvanced = true; + } + + return itAdvanced; +} + +bool Text3D_writeLocalData(const osg::Object &obj, osgDB::Output &fw) +{ + const osgText::Text3D &text = static_cast(obj); + + if (text.getFont()) + { + fw.indent() << "font " << text.getFont()->getFileName() << std::endl; + } + + fw.indent() << "characterDepth " << text.getCharacterDepth() << std::endl; + + fw.indent() << "renderMode " << convertRenderModeEnumToString(text.getRenderMode()) << std::endl; + + return true; +} diff --git a/src/osgText/CMakeLists.txt b/src/osgText/CMakeLists.txt index 31934943b..9a3026b40 100644 --- a/src/osgText/CMakeLists.txt +++ b/src/osgText/CMakeLists.txt @@ -10,8 +10,12 @@ SET(HEADER_PATH ${OpenSceneGraph_SOURCE_DIR}/include/${LIB_NAME}) SET(LIB_PUBLIC_HEADERS ${HEADER_PATH}/Export ${HEADER_PATH}/Font + ${HEADER_PATH}/Font3D + ${HEADER_PATH}/KerningType ${HEADER_PATH}/String + ${HEADER_PATH}/TextBase ${HEADER_PATH}/Text + ${HEADER_PATH}/Text3D ${HEADER_PATH}/FadeText ${HEADER_PATH}/Version ) @@ -23,12 +27,17 @@ ADD_LIBRARY(${LIB_NAME} DefaultFont.cpp DefaultFont.h Font.cpp + Font3D.cpp String.cpp FadeText.cpp + TextBase.cpp Text.cpp + Text3D.cpp Version.cpp ) + +SET(TARGET_LIBRARIES_VARS FREETYPE_LIBRARY ) LINK_INTERNAL(${LIB_NAME} osgDB osg diff --git a/src/osgText/Font.cpp b/src/osgText/Font.cpp index 90b012a47..57bb55839 100644 --- a/src/osgText/Font.cpp +++ b/src/osgText/Font.cpp @@ -34,8 +34,8 @@ static OpenThreads::ReentrantMutex s_FontFileMutex; Font::FontMutex* osgText::Font::getSerializeFontCallsMutex() { - static OpenThreads::Mutex s_FontCallsMutex; - return &s_FontCallsMutex; + static OpenThreads::Mutex s_serializeFontCallsMutex; + return &s_serializeFontCallsMutex; } std::string osgText::findFontFile(const std::string& str) diff --git a/src/osgText/Font3D.cpp b/src/osgText/Font3D.cpp new file mode 100644 index 000000000..7aafd8031 --- /dev/null +++ b/src/osgText/Font3D.cpp @@ -0,0 +1,248 @@ +/* -*-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 + +#include + + +using namespace std; + +//static osg::ApplicationUsageProxy Font3D_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_TEXT3D_INCREMENTAL_SUBLOADING ","ON | OFF"); + +static OpenThreads::ReentrantMutex s_Font3DFileMutex; + +namespace osgText +{ + +Font::FontMutex* Font3D::getSerializeFontCallsMutex() +{ + static OpenThreads::Mutex s_serializeFontCallsMutex; + return &s_serializeFontCallsMutex; +} + +std::string findFont3DFile(const std::string& str) +{ + // try looking in OSGFILEPATH etc first for fonts. + std::string filename = osgDB::findDataFile(str); + if (!filename.empty()) return filename; + + OpenThreads::ScopedLock lock(s_Font3DFileMutex); + + static osgDB::FilePathList s_FontFilePath; + static bool initialized = false; + if (!initialized) + { + initialized = true; + #if defined(WIN32) + osgDB::convertStringPathIntoFilePathList( + ".;C:/winnt/fonts;C:/windows/fonts", + s_FontFilePath); + + char *ptr; + if ((ptr = getenv( "windir" ))) + { + std::string winFontPath = ptr; + winFontPath += "\\fonts"; + s_FontFilePath.push_back(winFontPath); + } + #else + osgDB::convertStringPathIntoFilePathList( + ".:/usr/share/fonts/ttf:/usr/share/fonts/ttf/western:/usr/share/fonts/ttf/decoratives", + s_FontFilePath); + #endif + } + + filename = osgDB::findFileInPath(str,s_FontFilePath); + if (!filename.empty()) return filename; + + // Try filename without pathname, if it has a path + filename = osgDB::getSimpleFileName(str); + if(filename!=str) + { + filename = osgDB::findFileInPath(filename,s_FontFilePath); + if (!filename.empty()) return filename; + } + else + { + filename = osgText::findFont3DFile(std::string("fonts/")+filename); + if (!filename.empty()) return filename; + } + + // Not found, return empty string + osg::notify(osg::WARN)<<"Warning: font file \""< lock(s_Font3DFileMutex); + + osg::ref_ptr localOptions; + if (!userOptions) + { + localOptions = new osgDB::ReaderWriter::Options; + localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_OBJECTS); + // ** HACK to load Font3D instead of Font + localOptions->setPluginData("3D", (void*) 1); + } + else + { + userOptions->setPluginData("3D", (void*) 1); + } + + osg::Object* object = osgDB::readObjectFile(foundFile, userOptions ? userOptions : localOptions.get()); + + // if the object is a font then return it. + osgText::Font3D* font3D = dynamic_cast(object); + if (font3D) return font3D; + + // otherwise if the object has zero references then delete it by doing another unref(). + if (object && object->referenceCount()==0) object->unref(); + return 0; +} + +osgText::Font3D* readFont3DStream(std::istream& stream, const osgDB::ReaderWriter::Options* userOptions) +{ + OpenThreads::ScopedLock lock(s_Font3DFileMutex); + + osg::ref_ptr localOptions; + if (!userOptions) + { + localOptions = new osgDB::ReaderWriter::Options; + localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_OBJECTS); + localOptions->setPluginData("3D", (void*) 1); + } + else + { + userOptions->setPluginData("3D", (void*) 1); + } + + // there should be a better way to get the FreeType ReaderWriter by name... + osgDB::ReaderWriter *reader = osgDB::Registry::instance()->getReaderWriterForExtension("ttf"); + if (reader == 0) return 0; + + osgDB::ReaderWriter::ReadResult rr = reader->readObject(stream, userOptions ? userOptions : localOptions.get()); + if (rr.error()) + { + osg::notify(osg::WARN) << rr.message() << std::endl; + return 0; + } + if (!rr.validObject()) return 0; + + osg::Object *object = rr.takeObject(); + + // if the object is a font then return it. + osgText::Font3D* font3D = dynamic_cast(object); + if (font3D) return font3D; + + // otherwise if the object has zero references then delete it by doing another unref(). + if (object && object->referenceCount()==0) object->unref(); + return 0; +} + +Font3D::Font3D(Font3DImplementation* implementation): + osg::Object(true), + _depth(1), + _width(64), + _height(64) +{ + setImplementation(implementation); +} + +Font3D::~Font3D() +{ + if (_implementation.valid()) _implementation->_facade = 0; +} + +void Font3D::setImplementation(Font3DImplementation* implementation) +{ + if (_implementation.valid()) _implementation->_facade = 0; + _implementation = implementation; + if (_implementation.valid()) _implementation->_facade = this; +} + +Font3D::Font3DImplementation* Font3D::getImplementation() +{ + return _implementation.get(); +} + +const Font3D::Font3DImplementation* Font3D::getImplementation() const +{ + return _implementation.get(); +} + +std::string Font3D::getFileName() const +{ + if (_implementation.valid()) return _implementation->getFileName(); + return ""; +} + +Font3D::Glyph3D* Font3D::getGlyph(unsigned int charcode) +{ + Glyph3D * glyph3D = NULL; + + Glyph3DMap::iterator itr = _glyph3DMap.find(charcode); + if (itr!=_glyph3DMap.end()) glyph3D = itr->second.get(); + + else if (_implementation.valid()) + { + glyph3D = _implementation->getGlyph(charcode); + if (glyph3D) _glyph3DMap[charcode] = glyph3D; + } + + return glyph3D; +} + +void Font3D::setThreadSafeRefUnref(bool threadSafe) +{ + Glyph3DMap::iterator it,end = _glyph3DMap.end(); + + for (it=_glyph3DMap.begin(); it!=end; ++it) + it->second->setThreadSafeRefUnref(threadSafe); +} + +osg::Vec2 Font3D::getKerning(unsigned int leftcharcode,unsigned int rightcharcode, KerningType kerningType) +{ + if (_implementation.valid()) return _implementation->getKerning(leftcharcode,rightcharcode,kerningType); + else return osg::Vec2(0.0f,0.0f); +} +bool Font3D::hasVertical() const +{ + if (_implementation.valid()) return _implementation->hasVertical(); + else return false; +} + +void Font3D::Glyph3D::setThreadSafeRefUnref(bool threadSafe) +{ + if (_vertexArray.valid()) _vertexArray->setThreadSafeRefUnref(threadSafe); + if (_normalArray.valid()) _normalArray->setThreadSafeRefUnref(threadSafe); +} +} diff --git a/src/osgText/Text.cpp b/src/osgText/Text.cpp index 76ea9a77b..5a8047089 100644 --- a/src/osgText/Text.cpp +++ b/src/osgText/Text.cpp @@ -32,22 +32,7 @@ using namespace osgText; //#define TREES_CODE_FOR_MAKING_SPACES_EDITABLE Text::Text(): - _fontWidth(32), - _fontHeight(32), - _characterHeight(32), - _characterAspectRatio(1.0f), - _characterSizeMode(OBJECT_COORDS), - _maximumWidth(0.0f), - _maximumHeight(0.0f), - _lineSpacing(0.0f), - _alignment(BASE_LINE), - _axisAlignment(XY_PLANE), - _autoRotateToScreen(false), - _layout(LEFT_TO_RIGHT), _color(1.0f,1.0f,1.0f,1.0f), - _drawMode(TEXT), - _kerningType(KERNING_DEFAULT), - _lineCount(0), _backdropType(NONE), _backdropImplementation(DEPTH_RANGE), _backdropHorizontalOffset(0.07f), @@ -58,34 +43,12 @@ Text::Text(): _colorGradientBottomLeft(0.0f, 1.0f, 0.0f, 1.0f), _colorGradientBottomRight(0.0f, 0.0f, 1.0f, 1.0f), _colorGradientTopRight(1.0f, 1.0f, 1.0f, 1.0f) -{ - setStateSet(DefaultFont::instance()->getStateSet()); - setUseDisplayList(false); - setSupportsDisplayList(false); -} +{} Text::Text(const Text& text,const osg::CopyOp& copyop): - osg::Drawable(text,copyop), + osgText::TextBase(text,copyop), _font(text._font), - _fontWidth(text._fontWidth), - _fontHeight(text._fontHeight), - _characterHeight(text._characterHeight), - _characterAspectRatio(text._characterAspectRatio), - _characterSizeMode(text._characterSizeMode), - _maximumWidth(text._maximumWidth), - _maximumHeight(text._maximumHeight), - _lineSpacing(text._lineSpacing), - _text(text._text), - _position(text._position), - _alignment(text._alignment), - _axisAlignment(text._axisAlignment), - _rotation(text._rotation), - _autoRotateToScreen(text._autoRotateToScreen), - _layout(text._layout), _color(text._color), - _drawMode(text._drawMode), - _kerningType(text._kerningType), - _lineCount(text._lineCount), _backdropType(text._backdropType), _backdropImplementation(text._backdropImplementation), _backdropHorizontalOffset(text._backdropHorizontalOffset), @@ -126,182 +89,12 @@ void Text::setFont(const std::string& fontfile) setFont(readFontFile(fontfile)); } -void Text::setFontResolution(unsigned int width, unsigned int height) -{ - _fontWidth = width; - _fontHeight = height; - computeGlyphRepresentation(); -} - -void Text::setCharacterSize(float height,float aspectRatio) -{ - _characterHeight = height; - _characterAspectRatio = aspectRatio; - computeGlyphRepresentation(); -} - -void Text::setMaximumWidth(float maximumWidth) -{ - _maximumWidth = maximumWidth; - computeGlyphRepresentation(); -} - -void Text::setMaximumHeight(float maximumHeight) -{ - _maximumHeight = maximumHeight; - computeGlyphRepresentation(); -} - -void Text::setLineSpacing(float lineSpacing) -{ - _lineSpacing = lineSpacing; - computeGlyphRepresentation(); -} - - -void Text::setText(const String& text) -{ - if (_text==text) return; - - _text = text; - computeGlyphRepresentation(); -} - -void Text::setText(const std::string& text) -{ - setText(String(text)); -} - -void Text::setText(const std::string& text,String::Encoding encoding) -{ - setText(String(text,encoding)); -} - - -void Text::setText(const wchar_t* text) -{ - setText(String(text)); -} - -void Text::setPosition(const osg::Vec3& pos) -{ - if (_position==pos) return; - - _position = pos; - computePositions(); -} - -void Text::setAlignment(AlignmentType alignment) -{ - if (_alignment==alignment) return; - - _alignment = alignment; - computePositions(); -} - -void Text::setAxisAlignment(AxisAlignment axis) -{ - _axisAlignment = axis; - - switch(axis) - { - case XZ_PLANE: - setAutoRotateToScreen(false); - setRotation(osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))); - break; - case REVERSED_XZ_PLANE: - setAutoRotateToScreen(false); - setRotation(osg::Quat(osg::inDegrees(180.0f),osg::Vec3(0.0f,1.0f,0.0f))* - osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))); - break; - case YZ_PLANE: - setAutoRotateToScreen(false); - setRotation(osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))* - osg::Quat(osg::inDegrees(90.0f),osg::Vec3(0.0f,0.0f,1.0f))); - break; - case REVERSED_YZ_PLANE: - setAutoRotateToScreen(false); - setRotation(osg::Quat(osg::inDegrees(180.0f),osg::Vec3(0.0f,1.0f,0.0f))* - osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))* - osg::Quat(osg::inDegrees(90.0f),osg::Vec3(0.0f,0.0f,1.0f))); - break; - case XY_PLANE: - setAutoRotateToScreen(false); - setRotation(osg::Quat()); // nop - already on XY plane. - break; - case REVERSED_XY_PLANE: - setAutoRotateToScreen(false); - setRotation(osg::Quat(osg::inDegrees(180.0f),osg::Vec3(0.0f,1.0f,0.0f))); - break; - case SCREEN: - setAutoRotateToScreen(true); - setRotation(osg::Quat()); // nop - already on XY plane. - break; - } -} - -void Text::setRotation(const osg::Quat& quat) -{ - _rotation = quat; - computePositions(); -} - - -void Text::setAutoRotateToScreen(bool autoRotateToScreen) -{ - if (_autoRotateToScreen==autoRotateToScreen) return; - - _autoRotateToScreen = autoRotateToScreen; -} - - -void Text::setLayout(Layout layout) -{ - if (_layout==layout) return; - - _layout = layout; - computeGlyphRepresentation(); -} void Text::setColor(const osg::Vec4& color) { _color = color; } -void Text::setDrawMode(unsigned int mode) -{ - if (_drawMode==mode) return; - - _drawMode=mode; -} - - -osg::BoundingBox Text::computeBound() const -{ - osg::BoundingBox bbox; - - if (_textBB.valid()) - { - - for(unsigned int i=0;i<_autoTransformCache.size();++i) - { - if (_autoTransformCache[i]._traversalNumber<0 && (_characterSizeMode!=OBJECT_COORDS || _autoRotateToScreen)) - { - // _autoTransformCache is not valid so don't take it into accoumt when compute bounding volume. - } - else - { - osg::Matrix& matrix = _autoTransformCache[i]._matrix; - bbox.expandBy(osg::Vec3(_textBB.xMin(),_textBB.yMin(),_textBB.zMin())*matrix); - bbox.expandBy(osg::Vec3(_textBB.xMax(),_textBB.yMin(),_textBB.zMin())*matrix); - bbox.expandBy(osg::Vec3(_textBB.xMax(),_textBB.yMax(),_textBB.zMin())*matrix); - bbox.expandBy(osg::Vec3(_textBB.xMin(),_textBB.yMax(),_textBB.zMin())*matrix); - } - } - } - - return bbox; -} Font* Text::getActiveFont() { @@ -466,11 +259,11 @@ void Text::computeGlyphRepresentation() if (_text.empty()) { _textBB.set(0,0,0,0,0,0);//no size text - computePositions(); //to reset the origin + TextBase::computePositions(); //to reset the origin return; } - OpenThreads::ScopedLock lock(*(Font::getSerializeFontCallsMutex())); + OpenThreads::ScopedLock lock(*(activefont->getSerializeFontCallsMutex())); // initialize bounding box, it will be expanded during glyph position calculation _textBB.init(); @@ -746,7 +539,7 @@ void Text::computeGlyphRepresentation() } - computePositions(); + TextBase::computePositions(); computeBackdropBoundingBox(); computeColorGradients(); } @@ -803,22 +596,6 @@ bool Text::computeAverageGlyphWidthAndHeight(float& avg_width, float& avg_height return is_valid_size; } -void Text::computePositions() -{ - unsigned int size = osg::maximum(osg::DisplaySettings::instance()->getMaxNumberOfGraphicsContexts(),_autoTransformCache.size()); - - // FIXME: OPTIMIZE: This would be one of the ideal locations to - // call computeAverageGlyphWidthAndHeight(). It is out of the contextID loop - // so the value would be computed fewer times. But the code will need changes - // to get the value down to the locations it is needed. (Either pass through parameters - // or member variables, but we would need a system to know if the values are stale.) - - - for(unsigned int i=0;isetThreadSafeRefUnref(threadSafe); } void Text::resizeGLObjectBuffers(unsigned int maxSize) { - Drawable::resizeGLObjectBuffers(maxSize); - - _autoTransformCache.resize(maxSize); + TextBase::resizeGLObjectBuffers(maxSize); getActiveFont()->resizeGLObjectBuffers(maxSize); } @@ -1687,7 +1462,7 @@ void Text::resizeGLObjectBuffers(unsigned int maxSize) void Text::releaseGLObjects(osg::State* state) const { - Drawable::releaseGLObjects(state); + TextBase::releaseGLObjects(state); getActiveFont()->releaseGLObjects(state); } @@ -1983,7 +1758,6 @@ void Text::renderOnlyForegroundText(osg::State& state, const osg::Vec4& colorMul } - void Text::renderWithPolygonOffset(osg::State& state, const osg::Vec4& colorMultiplier) const { unsigned int contextID = state.getContextID(); @@ -2044,7 +1818,6 @@ void Text::renderWithPolygonOffset(osg::State& state, const osg::Vec4& colorMult glPopAttrib(); } - void Text::renderWithNoDepthBuffer(osg::State& state, const osg::Vec4& colorMultiplier) const { diff --git a/src/osgText/Text3D.cpp b/src/osgText/Text3D.cpp new file mode 100644 index 000000000..0f5f5b6b0 --- /dev/null +++ b/src/osgText/Text3D.cpp @@ -0,0 +1,719 @@ +/* -*-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 +namespace osgText +{ + +Text3D::Text3D(): + _font(0), + _characterDepth(1), + _renderMode(PER_GLYPH) +{ +} + +Text3D::Text3D(const Text3D & text3D, const osg::CopyOp & copyop): + osgText::TextBase(text3D, copyop), + _font(text3D._font), + _characterDepth(text3D._characterDepth), + _renderMode(text3D._renderMode) +{ + computeGlyphRepresentation(); +} + +//void Text3D::accept(osg::Drawable::ConstAttributeFunctor& af) const +//{ +// TODO +//} + +//void Text3D::accept(osg::PrimitiveFunctor& /*pf*/) const +//{ +// TODO +//} + +void Text3D::setFont(Font3D * font) +{ + _font = font; + + computeGlyphRepresentation(); +} + +void Text3D::setFont(const std::string & fontfile) +{ + setFont(readFont3DFile(fontfile)); +} + +String::iterator Text3D::computeLastCharacterOnLine(osg::Vec2& cursor, String::iterator first,String::iterator last) +{ + if (_font.valid() == false) return last; + + bool kerning = true; + unsigned int previous_charcode = 0; + + String::iterator lastChar = first; + + std::set deliminatorSet; + deliminatorSet.insert(' '); + deliminatorSet.insert('\n'); + deliminatorSet.insert(':'); + deliminatorSet.insert('/'); + deliminatorSet.insert(','); + deliminatorSet.insert(';'); + deliminatorSet.insert(':'); + deliminatorSet.insert('.'); + + float maximumHeight = _maximumHeight / _font->getScale(); + float maximumWidth = _maximumWidth / _font->getScale(); + + for(bool outOfSpace=false;lastChar!=last;++lastChar) + { + unsigned int charcode = *lastChar; + + if (charcode=='\n') + { + return lastChar; + } + + Font3D::Glyph3D* glyph = _font->getGlyph(charcode); + if (glyph) + { + const osg::BoundingBox & bb = glyph->getBoundingBox(); + + // adjust cursor position w.r.t any kerning. + if (previous_charcode) + { + + if (_layout == RIGHT_TO_LEFT) + { + cursor.x() -= glyph->getHorizontalAdvance(); + } + + if (kerning) + { + switch (_layout) + { + case LEFT_TO_RIGHT: + { + cursor += _font->getKerning(previous_charcode, charcode, _kerningType); + break; + } + case RIGHT_TO_LEFT: + { + cursor -= _font->getKerning(charcode, previous_charcode, _kerningType); + break; + } + case VERTICAL: + break; // no kerning when vertical. + } + } + } + else + { + switch (_layout) + { + case LEFT_TO_RIGHT: + { + cursor.x() -= glyph->getHorizontalBearing().x(); + break; + } + case RIGHT_TO_LEFT: + { + cursor.x() -= bb.xMax(); + break; + } + case VERTICAL: + break; + } + + } + + + + switch(_layout) + { + case LEFT_TO_RIGHT: + { + if (maximumWidth>0.0f && cursor.x()+bb.xMax()>maximumWidth) outOfSpace=true; + if(maximumHeight>0.0f && cursor.y()<-maximumHeight) outOfSpace=true; + break; + } + case RIGHT_TO_LEFT: + { + if (maximumWidth>0.0f && cursor.x()+bb.xMin()<-maximumWidth) outOfSpace=true; + if(maximumHeight>0.0f && cursor.y()<-maximumHeight) outOfSpace=true; + break; + } + case VERTICAL: + if (maximumHeight>0.0f && cursor.y()<-maximumHeight) outOfSpace=true; + break; + } + + // => word boundary detection & wrapping + if (outOfSpace) break; + + // move the cursor onto the next character. + switch(_layout) + { + case LEFT_TO_RIGHT: cursor.x() += glyph->getHorizontalAdvance(); break; + case RIGHT_TO_LEFT: break; + case VERTICAL: cursor.y() -= glyph->getVerticalAdvance(); break; + } + + previous_charcode = charcode; + + } + + } + + // word boundary detection & wrapping + if (lastChar!=last) + { + if (deliminatorSet.count(*lastChar)==0) + { + String::iterator lastValidChar = lastChar; + while (lastValidChar!=first && deliminatorSet.count(*lastValidChar)==0) + { + --lastValidChar; + + //Substract off glyphs from the cursor position (to correctly center text) + Font3D::Glyph3D* glyph = _font->getGlyph(*lastValidChar); + if (glyph) + { + switch(_layout) + { + case LEFT_TO_RIGHT: cursor.x() -= glyph->getHorizontalAdvance(); break; + case RIGHT_TO_LEFT: cursor.x() += glyph->getHorizontalAdvance(); break; + case VERTICAL: cursor.y() += glyph->getVerticalAdvance(); break; + } + } + } + + if (first!=lastValidChar) + { + ++lastValidChar; + lastChar = lastValidChar; + } + } + } + + return lastChar; +} + +void Text3D::computeGlyphRepresentation() +{ + if (_font.valid() == false) return; + + _textRenderInfo.clear(); + _lineCount = 0; + + if (_text.empty()) + { + _textBB.set(0,0,0, 0,0,0);//no size text + TextBase::computePositions(); //to reset the origin + return; + } + + OpenThreads::ScopedLock lock(*(_font->getSerializeFontCallsMutex())); + + // initialize bounding box, it will be expanded during glyph position calculation + _textBB.init(); + + osg::Vec2 startOfLine_coords(0.0f,0.0f); + osg::Vec2 cursor(startOfLine_coords); + osg::Vec2 local(0.0f,0.0f); + osg::Vec2 startOffset(0.0f,0.0f); + + unsigned int previous_charcode = 0; + unsigned int linelength = 0; + bool kerning = true; + + unsigned int lineNumber = 0; + + + + for(String::iterator itr=_text.begin(); itr!=_text.end(); ) + { + _textRenderInfo.resize(lineNumber + 1); + LineRenderInfo & currentLineRenderInfo = _textRenderInfo.back(); + + // record the start of the current line + String::iterator startOfLine_itr = itr; + + // find the end of the current line. + osg::Vec2 endOfLine_coords(cursor); + String::iterator endOfLine_itr = computeLastCharacterOnLine(endOfLine_coords, itr,_text.end()); + + // ** position the cursor function to the Layout and the alignement + TextBase::positionCursor(endOfLine_coords, cursor, (unsigned int) (endOfLine_itr - startOfLine_itr)); + + + if (itr!=endOfLine_itr) + { + for(;itr!=endOfLine_itr;++itr) + { + unsigned int charcode = *itr; + + Font3D::Glyph3D* glyph = _font->getGlyph(charcode); + if (glyph) + { + const osg::BoundingBox & bb = glyph->getBoundingBox(); + + // adjust cursor position w.r.t any kerning. + if (previous_charcode) + { + if (_layout == RIGHT_TO_LEFT) + { + cursor.x() -= glyph->getHorizontalAdvance(); + } + + if (kerning) + { + switch (_layout) + { + case LEFT_TO_RIGHT: + { + cursor += _font->getKerning(previous_charcode, charcode, _kerningType); + break; + } + case RIGHT_TO_LEFT: + { + cursor -= _font->getKerning(charcode, previous_charcode, _kerningType); + break; + } + case VERTICAL: + break; // no kerning when vertical. + } + } + } + else + { + switch (_layout) + { + case LEFT_TO_RIGHT: + { + cursor.x() -= glyph->getHorizontalBearing().x(); + break; + } + case RIGHT_TO_LEFT: + { + cursor.x() -= bb.xMax(); + break; + } + case VERTICAL: + { +// cursor.y() += glyph->getVerticalBearing().y(); + break; + } + } + + } + + local = cursor; + + if (_layout==VERTICAL) + { + local.x() += -glyph->getVerticalBearing().x(); + local.y() += -glyph->getVerticalBearing().y(); + } + + + + // move the cursor onto the next character. + // also expand bounding box + switch (_layout) + { + case LEFT_TO_RIGHT: + _textBB.expandBy(osg::Vec3(cursor.x() + bb.xMin(), cursor.y() + bb.yMin(), 0.0f)); //lower left corner + _textBB.expandBy(osg::Vec3(cursor.x() + bb.xMax(), cursor.y() + bb.yMax(), 0.0f)); //upper right corner + cursor.x() += glyph->getHorizontalAdvance(); + break; + case VERTICAL: + _textBB.expandBy(osg::Vec3(cursor.x(), cursor.y(), 0.0f)); //upper left corner + _textBB.expandBy(osg::Vec3(cursor.x() + glyph->getWidth(), cursor.y() - glyph->getHeight(), 0.0f)); //lower right corner + cursor.y() -= glyph->getVerticalAdvance(); + break; + case RIGHT_TO_LEFT: + _textBB.expandBy(osg::Vec3(cursor.x()+bb.xMax(), cursor.y() + bb.yMax(), 0.0f)); //upper right corner + _textBB.expandBy(osg::Vec3(cursor.x()+bb.xMin(), cursor.y()+bb.yMin(), 0.0f)); //lower left corner + + break; + } + + osg::Vec3 pos = osg::Vec3(local.x(), local.y(), 0.0f); + currentLineRenderInfo.push_back(Text3D::GlyphRenderInfo(glyph, pos)); + + + previous_charcode = charcode; + } + } + } + else + { + ++itr; + } + + if (itr!=_text.end()) + { + // skip over return. + if (*itr=='\n') ++itr; + } + + // move to new line. + switch(_layout) + { + case LEFT_TO_RIGHT: + case RIGHT_TO_LEFT: + { + startOfLine_coords.y() -= (1.0 + _lineSpacing) / _font->getScale(); + ++_lineCount; + break; + } + case VERTICAL: + { + startOfLine_coords.x() += _characterHeight / _font->getScale() / _characterAspectRatio * (1.0 + _lineSpacing); + // because _lineCount is the max vertical no. of characters.... + _lineCount = (_lineCount >linelength)?_lineCount:linelength; + break; + } + default: + break; + } + + cursor = startOfLine_coords; + previous_charcode = 0; + + ++lineNumber; + } + _textBB.expandBy(0.0f,0.0f,-1); + + TextBase::computePositions(); +} + + + + + + + + + + + +void Text3D::computePositions(unsigned int contextID) const +{ + if (_font.valid() == false) return; + + switch(_alignment) + { + case LEFT_TOP: _offset.set(_textBB.xMin(),_textBB.yMax(),_textBB.zMin()); break; + case LEFT_CENTER: _offset.set(_textBB.xMin(),(_textBB.yMax()+_textBB.yMin())*0.5f,_textBB.zMin()); break; + case LEFT_BOTTOM: _offset.set(_textBB.xMin(),_textBB.yMin(),_textBB.zMin()); break; + + case CENTER_TOP: _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,_textBB.yMax(),_textBB.zMin()); break; + case CENTER_CENTER: _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,(_textBB.yMax()+_textBB.yMin())*0.5f,_textBB.zMin()); break; + case CENTER_BOTTOM: _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,_textBB.yMin(),_textBB.zMin()); break; + + case RIGHT_TOP: _offset.set(_textBB.xMax(),_textBB.yMax(),_textBB.zMin()); break; + case RIGHT_CENTER: _offset.set(_textBB.xMax(),(_textBB.yMax()+_textBB.yMin())*0.5f,_textBB.zMin()); break; + case RIGHT_BOTTOM: _offset.set(_textBB.xMax(),_textBB.yMin(),_textBB.zMin()); break; + + case LEFT_BASE_LINE: _offset.set(0.0f,0.0f,0.0f); break; + case CENTER_BASE_LINE: _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,0.0f,0.0f); break; + case RIGHT_BASE_LINE: _offset.set(_textBB.xMax(),0.0f,0.0f); break; + + case LEFT_BOTTOM_BASE_LINE: _offset.set(0.0f,-_characterHeight*(_lineCount-1),0.0f); break; + case CENTER_BOTTOM_BASE_LINE: _offset.set((_textBB.xMax()+_textBB.xMin())*0.5f,-_characterHeight*(_lineCount-1),0.0f); break; + case RIGHT_BOTTOM_BASE_LINE: _offset.set(_textBB.xMax(),-_characterHeight*(_lineCount-1),0.0f); break; + } + + AutoTransformCache& atc = _autoTransformCache[contextID]; + osg::Matrix& matrix = atc._matrix; + + + float scale = _font->getScale(); + osg::Matrix scaleMatrix = osg::Matrix::scale(scale * _characterHeight, + scale * _characterHeight / _characterAspectRatio, + _characterDepth); + if (!_rotation.zeroRotation()) + { + matrix.makeTranslate(-_offset); + matrix.postMult(scaleMatrix); + matrix.postMult(osg::Matrix::rotate(_rotation)); + matrix.postMult(osg::Matrix::translate(_position)); + } + else + { + matrix.makeTranslate(-_offset); + matrix.postMult(scaleMatrix); + matrix.postMult(osg::Matrix::translate(_position)); + } + + + _normal = osg::Matrix::transform3x3(osg::Vec3(0.0f,0.0f,1.0f),matrix); + _normal.normalize(); + + const_cast(this)->dirtyBound(); +} + +void Text3D::drawImplementation(osg::RenderInfo& renderInfo) const +{ + osg::State & state = *renderInfo.getState(); + unsigned int contextID = state.getContextID(); + + + // ** save the previous modelview matrix + osg::ref_ptr previous(new osg::RefMatrix(state.getModelViewMatrix())); + + // ** get the modelview for this context + osg::ref_ptr modelview(new osg::RefMatrix(_autoTransformCache[contextID]._matrix)); + + // ** mult previous by the modelview for this context + modelview->postMult(*previous.get()); + + // ** apply this new modelview matrix + state.applyModelViewMatrix(modelview.get()); + + + if (_drawMode & TEXT) + { + renderInfo.getState()->disableAllVertexArrays(); + + glPushAttrib(GL_TRANSFORM_BIT); + glEnable(GL_RESCALE_NORMAL); + + switch(_renderMode) + { + case PER_FACE: renderPerFace(*renderInfo.getState()); break; + case PER_GLYPH: + default: renderPerGlyph(*renderInfo.getState()); break; + } + + glPopAttrib(); + } + + if (_drawMode & BOUNDINGBOX) + { + if (_textBB.valid()) + { + osg::Vec3 c000(osg::Vec3(_textBB.xMin(),_textBB.yMin(),_textBB.zMax())); + osg::Vec3 c100(osg::Vec3(_textBB.xMax(),_textBB.yMin(),_textBB.zMax())); + osg::Vec3 c110(osg::Vec3(_textBB.xMax(),_textBB.yMax(),_textBB.zMax())); + osg::Vec3 c010(osg::Vec3(_textBB.xMin(),_textBB.yMax(),_textBB.zMax())); + + osg::Vec3 c001(osg::Vec3(_textBB.xMin(),_textBB.yMin(),_textBB.zMin())); + osg::Vec3 c101(osg::Vec3(_textBB.xMax(),_textBB.yMin(),_textBB.zMin())); + osg::Vec3 c111(osg::Vec3(_textBB.xMax(),_textBB.yMax(),_textBB.zMin())); + osg::Vec3 c011(osg::Vec3(_textBB.xMin(),_textBB.yMax(),_textBB.zMin())); + + glBegin(GL_LINE_LOOP); + glVertex3fv(c000.ptr()); + glVertex3fv(c100.ptr()); + glVertex3fv(c110.ptr()); + glVertex3fv(c010.ptr()); + glEnd(); + + glBegin(GL_LINE_LOOP); + glVertex3fv(c001.ptr()); + glVertex3fv(c011.ptr()); + glVertex3fv(c111.ptr()); + glVertex3fv(c101.ptr()); + glEnd(); + + glBegin(GL_LINES); + glVertex3fv(c000.ptr()); + glVertex3fv(c001.ptr()); + + glVertex3fv(c100.ptr()); + glVertex3fv(c101.ptr()); + + glVertex3fv(c110.ptr()); + glVertex3fv(c111.ptr()); + + glVertex3fv(c010.ptr()); + glVertex3fv(c011.ptr()); + glEnd(); + } + } + + if (_drawMode & ALIGNMENT) + { + float cursorsize = _characterHeight*0.5f; + + osg::Vec3 hl(osg::Vec3(_offset.x()-cursorsize,_offset.y(),_offset.z())); + osg::Vec3 hr(osg::Vec3(_offset.x()+cursorsize,_offset.y(),_offset.z())); + osg::Vec3 vt(osg::Vec3(_offset.x(),_offset.y()-cursorsize,_offset.z())); + osg::Vec3 vb(osg::Vec3(_offset.x(),_offset.y()+cursorsize,_offset.z())); + + glBegin(GL_LINES); + glVertex3fv(hl.ptr()); + glVertex3fv(hr.ptr()); + glVertex3fv(vt.ptr()); + glVertex3fv(vb.ptr()); + glEnd(); + + } + + // restore the previous modelview matrix + state.applyModelViewMatrix(previous.get()); +} + + + +void Text3D::renderPerGlyph(osg::State & state) const +{ + // ** for each line, do ... + TextRenderInfo::const_iterator itLine, endLine = _textRenderInfo.end(); + for (itLine = _textRenderInfo.begin(); itLine!=endLine; ++itLine) + { + // ** for each glyph in the line, do ... + LineRenderInfo::const_iterator it, end = itLine->end(); + for (it = itLine->begin(); it!=end; ++it) + { + + glPushMatrix(); + + glTranslatef(it->_position.x(), it->_position.y(), it->_position.z()); + + // ** apply the vertex array + state.setVertexPointer(it->_glyph->getVertexArray()); + + // ** render the front face of the glyph + glNormal3f(0.0f,0.0f,1.0f); + + osg::Geometry::PrimitiveSetList & pslFront = it->_glyph->getFrontPrimitiveSetList(); + for(osg::Geometry::PrimitiveSetList::const_iterator itr=pslFront.begin(), end = pslFront.end(); itr!=end; ++itr) + { + + (*itr)->draw(state, false); + } + + // ** render the wall face of the glyph + state.setNormalPointer(it->_glyph->getNormalArray()); + osg::Geometry::PrimitiveSetList & pslWall = it->_glyph->getWallPrimitiveSetList(); + for(osg::Geometry::PrimitiveSetList::const_iterator itr=pslWall.begin(), end=pslWall.end(); itr!=end; ++itr) + { + (*itr)->draw(state, false); + } + + // ** render the back face of the glyph + glNormal3f(0.0f,0.0f,-1.0f); + + osg::Geometry::PrimitiveSetList & pslBack = it->_glyph->getBackPrimitiveSetList(); + for(osg::Geometry::PrimitiveSetList::const_iterator itr=pslBack.begin(), end=pslBack.end(); itr!=end; ++itr) + { + (*itr)->draw(state, false); + } + + glPopMatrix(); + } + } +} + +void Text3D::renderPerFace(osg::State & state) const +{ + // ** render all front faces + glNormal3f(0.0f,0.0f,1.0f); + + TextRenderInfo::const_iterator itLine, endLine = _textRenderInfo.end(); + for (itLine = _textRenderInfo.begin(); itLine!=endLine; ++itLine) + { + // ** for each glyph in the line, do ... + LineRenderInfo::const_iterator it, end = itLine->end(); + for (it = itLine->begin(); it!=end; ++it) + { + glPushMatrix(); + glTranslatef(it->_position.x(), it->_position.y(), it->_position.z()); + state.setVertexPointer(it->_glyph->getVertexArray()); + + // ** render the front face of the glyph + osg::Geometry::PrimitiveSetList & psl = it->_glyph->getFrontPrimitiveSetList(); + for(osg::Geometry::PrimitiveSetList::const_iterator itr=psl.begin(), end=psl.end(); itr!=end; ++itr) + { + (*itr)->draw(state, false); + } + glPopMatrix(); + } + } + + + // ** render all wall face of the text + for (itLine = _textRenderInfo.begin(); itLine!=endLine; ++itLine) + { + // ** for each glyph in the line, do ... + LineRenderInfo::const_iterator it, end = itLine->end(); + for (it = itLine->begin(); it!=end; ++it) + { + glPushMatrix(); + glTranslatef(it->_position.x(), it->_position.y(), it->_position.z()); + state.setVertexPointer(it->_glyph->getVertexArray()); + state.setNormalPointer(it->_glyph->getNormalArray()); + + osg::Geometry::PrimitiveSetList & psl = it->_glyph->getWallPrimitiveSetList(); + for(osg::Geometry::PrimitiveSetList::const_iterator itr=psl.begin(), end=psl.end(); itr!=end; ++itr) + { + (*itr)->draw(state, false); + } + glPopMatrix(); + } + } + + + // ** render all back face of the text + glNormal3f(0.0f,0.0f,-1.0f); + + for (itLine = _textRenderInfo.begin(); itLine!=endLine; ++itLine) + { + // ** for each glyph in the line, do ... + LineRenderInfo::const_iterator it, end = itLine->end(); + for (it = itLine->begin(); it!=end; ++it) + { + glPushMatrix(); + glTranslatef(it->_position.x(), it->_position.y(), it->_position.z()); + state.setVertexPointer(it->_glyph->getVertexArray()); + + // ** render the back face of the glyph + osg::Geometry::PrimitiveSetList & psl = it->_glyph->getBackPrimitiveSetList(); + for(osg::Geometry::PrimitiveSetList::const_iterator itr=psl.begin(), end=psl.end(); itr!=end; ++itr) + { + (*itr)->draw(state, false); + } + glPopMatrix(); + } + } +} + + + +void Text3D::setThreadSafeRefUnref(bool threadSafe) +{ + TextBase::setThreadSafeRefUnref(threadSafe); + + if (_font.valid()) _font->setThreadSafeRefUnref(threadSafe); +} + +void Text3D::resizeGLObjectBuffers(unsigned int maxSize) +{ + TextBase::resizeGLObjectBuffers(maxSize); + + if (_font.valid()) _font->resizeGLObjectBuffers(maxSize); +} + +void Text3D::releaseGLObjects(osg::State* state) const +{ + TextBase::releaseGLObjects(state); + + if (_font.valid()) _font->releaseGLObjects(state); +} + +} diff --git a/src/osgText/TextBase.cpp b/src/osgText/TextBase.cpp new file mode 100644 index 000000000..bfc6d9712 --- /dev/null +++ b/src/osgText/TextBase.cpp @@ -0,0 +1,392 @@ +/* -*-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 "DefaultFont.h" + +using namespace osg; +using namespace osgText; + +//#define TREES_CODE_FOR_MAKING_SPACES_EDITABLE + +TextBase::TextBase(): + _fontWidth(32), + _fontHeight(32), + _characterHeight(32), + _characterAspectRatio(1.0f), + _characterSizeMode(OBJECT_COORDS), + _maximumWidth(0.0f), + _maximumHeight(0.0f), + _lineSpacing(1.0f), + _alignment(BASE_LINE), + _axisAlignment(XY_PLANE), + _autoRotateToScreen(false), + _layout(LEFT_TO_RIGHT), + _drawMode(TEXT), + _kerningType(KERNING_DEFAULT), + _lineCount(0) +{ + setStateSet(DefaultFont::instance()->getStateSet()); + setUseDisplayList(false); + setSupportsDisplayList(false); +} + +TextBase::TextBase(const TextBase& textBase,const osg::CopyOp& copyop): + osg::Drawable(textBase,copyop), + _fontWidth(textBase._fontWidth), + _fontHeight(textBase._fontHeight), + _characterHeight(textBase._characterHeight), + _characterAspectRatio(textBase._characterAspectRatio), + _characterSizeMode(textBase._characterSizeMode), + _maximumWidth(textBase._maximumWidth), + _maximumHeight(textBase._maximumHeight), + _lineSpacing(textBase._lineSpacing), + _text(textBase._text), + _position(textBase._position), + _alignment(textBase._alignment), + _axisAlignment(textBase._axisAlignment), + _rotation(textBase._rotation), + _autoRotateToScreen(textBase._autoRotateToScreen), + _layout(textBase._layout), + _drawMode(textBase._drawMode), + _kerningType(textBase._kerningType), + _lineCount(textBase._lineCount) +{ +} + +TextBase::~TextBase() +{ +} + +void TextBase::setFontResolution(unsigned int width, unsigned int height) +{ + _fontWidth = width; + _fontHeight = height; + computeGlyphRepresentation(); +} + +void TextBase::setCharacterSize(float height,float aspectRatio) +{ + _characterHeight = height; + _characterAspectRatio = aspectRatio; + computeGlyphRepresentation(); +} + +void TextBase::setMaximumWidth(float maximumWidth) +{ + _maximumWidth = maximumWidth; + computeGlyphRepresentation(); +} + +void TextBase::setMaximumHeight(float maximumHeight) +{ + _maximumHeight = maximumHeight; + computeGlyphRepresentation(); +} + +void TextBase::setLineSpacing(float lineSpacing) +{ + _lineSpacing = lineSpacing; + computeGlyphRepresentation(); +} + + +void TextBase::setText(const String& text) +{ + if (_text==text) return; + + _text = text; + computeGlyphRepresentation(); +} + +void TextBase::setText(const std::string& text) +{ + setText(String(text)); +} + +void TextBase::setText(const std::string& text,String::Encoding encoding) +{ + setText(String(text,encoding)); +} + + +void TextBase::setText(const wchar_t* text) +{ + setText(String(text)); +} + +void TextBase::setPosition(const osg::Vec3& pos) +{ + if (_position==pos) return; + + _position = pos; + computePositions(); +} + +void TextBase::setAlignment(AlignmentType alignment) +{ + if (_alignment==alignment) return; + + _alignment = alignment; + computePositions(); +} + +void TextBase::setAxisAlignment(AxisAlignment axis) +{ + _axisAlignment = axis; + + switch(axis) + { + case XZ_PLANE: + setAutoRotateToScreen(false); + setRotation(osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))); + break; + case REVERSED_XZ_PLANE: + setAutoRotateToScreen(false); + setRotation(osg::Quat(osg::inDegrees(180.0f),osg::Vec3(0.0f,1.0f,0.0f))* + osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))); + break; + case YZ_PLANE: + setAutoRotateToScreen(false); + setRotation(osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))* + osg::Quat(osg::inDegrees(90.0f),osg::Vec3(0.0f,0.0f,1.0f))); + break; + case REVERSED_YZ_PLANE: + setAutoRotateToScreen(false); + setRotation(osg::Quat(osg::inDegrees(180.0f),osg::Vec3(0.0f,1.0f,0.0f))* + osg::Quat(osg::inDegrees(90.0f),osg::Vec3(1.0f,0.0f,0.0f))* + osg::Quat(osg::inDegrees(90.0f),osg::Vec3(0.0f,0.0f,1.0f))); + break; + case XY_PLANE: + setAutoRotateToScreen(false); + setRotation(osg::Quat()); // nop - already on XY plane. + break; + case REVERSED_XY_PLANE: + setAutoRotateToScreen(false); + setRotation(osg::Quat(osg::inDegrees(180.0f),osg::Vec3(0.0f,1.0f,0.0f))); + break; + case SCREEN: + setAutoRotateToScreen(true); + setRotation(osg::Quat()); // nop - already on XY plane. + break; + default: break; + } +} + +void TextBase::setRotation(const osg::Quat& quat) +{ + _rotation = quat; + computePositions(); +} + + +void TextBase::setAutoRotateToScreen(bool autoRotateToScreen) +{ + if (_autoRotateToScreen==autoRotateToScreen) return; + + _autoRotateToScreen = autoRotateToScreen; +} + + +void TextBase::setLayout(Layout layout) +{ + if (_layout==layout) return; + + _layout = layout; + computeGlyphRepresentation(); +} + + +void TextBase::setDrawMode(unsigned int mode) +{ + if (_drawMode==mode) return; + + _drawMode=mode; +} + + +osg::BoundingBox TextBase::computeBound() const +{ + osg::BoundingBox bbox; + + if (_textBB.valid()) + { + for(unsigned int i=0;i<_autoTransformCache.size();++i) + { + if (_autoTransformCache[i]._traversalNumber<0 && (_characterSizeMode!=OBJECT_COORDS || _autoRotateToScreen)) + { + // _autoTransformCache is not valid so don't take it into accoumt when compute bounding volume. + } + else + { + osg::Matrix& matrix = _autoTransformCache[i]._matrix; + bbox.expandBy(osg::Vec3(_textBB.xMin(),_textBB.yMin(),_textBB.zMin())*matrix); +// bbox.expandBy(osg::Vec3(_textBB.xMax(),_textBB.yMin(),_textBB.zMin())*matrix); + bbox.expandBy(osg::Vec3(_textBB.xMax(),_textBB.yMax(),_textBB.zMax())*matrix); +// bbox.expandBy(osg::Vec3(_textBB.xMin(),_textBB.yMax(),_textBB.zMin())*matrix); + } + } + } + + return bbox; +} + +void TextBase::computePositions() +{ + unsigned int size = osg::maximum(osg::DisplaySettings::instance()->getMaxNumberOfGraphicsContexts(),_autoTransformCache.size()); + + // FIXME: OPTIMIZE: This would be one of the ideal locations to + // call computeAverageGlyphWidthAndHeight(). It is out of the contextID loop + // so the value would be computed fewer times. But the code will need changes + // to get the value down to the locations it is needed. (Either pass through parameters + // or member variables, but we would need a system to know if the values are stale.) + + + for(unsigned int i=0;i