1 /* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield
2  *
3  * This library is open source and may be redistributed and/or modified under
4  * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5  * (at your option) any later version.  The full license is in LICENSE file
6  * included with this distribution, and on the openscenegraph.org website.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * OpenSceneGraph Public License for more details.
12 */
13 
14 #include "TextNode.h"
15 #include "../../src/osgText/GlyphGeometry.h"
16 
17 #include <osg/PositionAttitudeTransform>
18 #include <osg/Geode>
19 #include <osgUtil/SmoothingVisitor>
20 
21 #include <osg/io_utils>
22 
23 using namespace osgText;
24 
25 /////////////////////////////////////////////////////////////////////////////////////////
26 //
27 // Layout
28 //
Layout()29 Layout::Layout()
30 {
31 }
32 
Layout(const Layout & layout,const osg::CopyOp & copyop)33 Layout::Layout(const Layout& layout, const osg::CopyOp& copyop):
34     osg::Object(layout,copyop)
35 {
36 }
37 
getDefaultLayout()38 osg::ref_ptr<Layout>& Layout::getDefaultLayout()
39 {
40     static OpenThreads::Mutex s_DefaultLayoutMutex;
41     OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_DefaultLayoutMutex);
42 
43     static osg::ref_ptr<Layout> s_defaultLayout = new Layout;
44     return s_defaultLayout;
45 }
46 
layout(TextNode & text) const47 void Layout::layout(TextNode& text) const
48 {
49     OSG_NOTICE<<"Layout::layout"<<std::endl;
50 
51     Font* font = text.getActiveFont();
52     Style* style = text.getActiveStyle();
53     TextTechnique* technique = text.getTextTechnique();
54     const String& str = text.getText();
55 
56     if (!text.getTextTechnique())
57     {
58         OSG_NOTICE<<"Warning: no TextTechnique assigned to Layout"<<std::endl;
59         return;
60     }
61 
62     osg::Vec3 pos(0.0f,0.0f,0.0f);
63     float characterSize = text.getCharacterSize();
64     osg::Vec3 size(characterSize, characterSize, 0.0);
65     if (style)
66     {
67         size.y() = characterSize;
68         size.z() = characterSize;
69     }
70 
71 
72     osgText::FontResolution resolution(32,32);
73     if (style)
74     {
75         resolution.first = static_cast<unsigned int>(static_cast<float>(resolution.first)*style->getSampleDensity());
76         resolution.second = static_cast<unsigned int>(static_cast<float>(resolution.second)*style->getSampleDensity());
77     }
78 
79     float characterWidthScale = 1.0f;
80 
81     bool textIs3D = (style && style->getThicknessRatio()!=0.0);
82     if (!textIs3D)
83     {
84         characterWidthScale = 1.0f/static_cast<float>(resolution.first);
85     }
86 
87     osgText::KerningType kerningType = osgText::KERNING_DEFAULT;
88 
89     technique->start();
90 
91     unsigned int previousCharcode = 0;
92     for(unsigned int i=0; i<str.size(); ++i)
93     {
94         unsigned int charcode = str[i];
95 
96         if (size.z()==0.0f)
97         {
98             osgText::Glyph* glyph = font->getGlyph(resolution, charcode);
99             if (glyph)
100             {
101                 technique->addCharacter(pos, size, glyph, style);
102                 pos += osg::Vec3(size.x()*(glyph->getHorizontalAdvance()*characterWidthScale), 0.0f ,0.0f);
103             }
104         }
105         else
106         {
107             osgText::Glyph3D* glyph = font->getGlyph3D(resolution, charcode);
108             OSG_NOTICE<<"pos = "<<pos<<", charcode="<<charcode<<", glyph="<<glyph<< std::endl;
109             if (glyph)
110             {
111                 osg::Vec3 local_scale( size );
112                 technique->addCharacter(pos, local_scale, glyph, style);
113                 pos += osg::Vec3(size.x()*glyph->getWidth(), 0.0f ,0.0f);
114             }
115         }
116 
117         if (previousCharcode!=0 && charcode!=0)
118         {
119             osg::Vec2 offset = font->getKerning(resolution, previousCharcode, charcode, kerningType);
120             OSG_NOTICE<<"  offset = "<<offset<< std::endl;
121             pos.x() += offset.x();
122             pos.y() += offset.y();
123         }
124 
125         previousCharcode = charcode;
126     }
127 
128     technique->finish();
129 }
130 
131 
132 /////////////////////////////////////////////////////////////////////////////////////////
133 //
134 // TextTechnique
135 //
TextTechnique()136 TextTechnique::TextTechnique():
137     _textNode(0)
138 {
139 }
140 
141 
TextTechnique(const TextTechnique & technique,const osg::CopyOp & copyop)142 TextTechnique::TextTechnique(const TextTechnique& technique, const osg::CopyOp& copyop):
143     osg::Object(technique, copyop),
144     _textNode(0)
145 {
146 }
147 
getDefaultTextTechinque()148 osg::ref_ptr<TextTechnique>& TextTechnique::getDefaultTextTechinque()
149 {
150     static OpenThreads::Mutex s_DefaultTextTechniqueMutex;
151     OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_DefaultTextTechniqueMutex);
152 
153     static osg::ref_ptr<TextTechnique> s_defaultTextTechnique = new TextTechnique;
154     return s_defaultTextTechnique;
155 }
156 
start()157 void TextTechnique::start()
158 {
159     OSG_NOTICE<<"TextTechnique::start()"<<std::endl;
160 }
161 
addCharacter(const osg::Vec3 & position,const osg::Vec3 & size,Glyph * glyph,Style * style)162 void TextTechnique::addCharacter(const osg::Vec3& position, const osg::Vec3& size, Glyph* glyph, Style* style)
163 {
164     OSG_NOTICE<<"TextTechnique::addCharacter 2D("<<position<<", "<<size<<", "<<glyph<<", "<<style<<")"<<std::endl;
165 }
166 
addCharacter(const osg::Vec3 & position,const osg::Vec3 & size,Glyph3D * glyph,Style * style)167 void TextTechnique::addCharacter(const osg::Vec3& position, const osg::Vec3& size, Glyph3D* glyph, Style* style)
168 {
169     OSG_NOTICE<<"TextTechnique::addCharacter 3D("<<position<<", "<<size<<", "<<glyph<<", "<<style<<")"<<std::endl;
170 
171     osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
172     transform->setPosition(position);
173     transform->setAttitude(osg::Quat(osg::inDegrees(90.0),osg::Vec3d(1.0,0.0,0.0)));
174     transform->setScale(size);
175 
176     osg::ref_ptr<osg::Geode> geode = new osg::Geode;
177 
178     const Bevel* bevel = style ? style->getBevel() : 0;
179     bool outline = style ? style->getOutlineRatio()>0.0f : false;
180     float width = style->getThicknessRatio();
181     float creaseAngle = 30.0f;
182     bool smooth = true;
183 
184     if (bevel)
185     {
186         osg::ref_ptr<osg::Geometry> glyphGeometry = osgText::computeGlyphGeometry(glyph, *bevel, width);
187         osg::ref_ptr<osg::Geometry> textGeometry = osgText::computeTextGeometry(glyphGeometry.get(), *bevel, width);
188         osg::ref_ptr<osg::Geometry> shellGeometry = outline ? osgText::computeShellGeometry(glyphGeometry.get(), *bevel, width) : 0;
189         if (textGeometry.valid()) geode->addDrawable(textGeometry.get());
190         if (shellGeometry.valid()) geode->addDrawable(shellGeometry.get());
191 
192         // create the normals
193         if (smooth && textGeometry.valid())
194         {
195             osgUtil::SmoothingVisitor::smooth(*textGeometry, osg::DegreesToRadians(creaseAngle));
196         }
197     }
198     else
199     {
200         osg::ref_ptr<osg::Geometry> textGeometry = osgText::computeTextGeometry(glyph, width);
201         if (textGeometry.valid()) geode->addDrawable(textGeometry.get());
202 
203         // create the normals
204         if (smooth && textGeometry.valid())
205         {
206             osgUtil::SmoothingVisitor::smooth(*textGeometry, osg::DegreesToRadians(creaseAngle));
207         }
208     }
209 
210     transform->addChild(geode.get());
211 
212     _textNode->addChild(transform.get());
213 
214     transform->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
215 
216 }
217 
finish()218 void TextTechnique::finish()
219 {
220     OSG_NOTICE<<"TextTechnique::finish()"<<std::endl;
221 }
222 
traverse(osg::NodeVisitor & nv)223 void TextTechnique::traverse(osg::NodeVisitor& nv)
224 {
225     // OSG_NOTICE<<"TextTechnique::traverse()"<<std::endl;
226     if (_textNode) _textNode->osg::Group::traverse(nv);
227 }
228 
229 /////////////////////////////////////////////////////////////////////////////////////////
230 //
231 // TextNode
232 //
TextNode()233 TextNode::TextNode():
234         _characterSize(1.0f)
235 {
236 }
237 
238 
TextNode(const TextNode & text,const osg::CopyOp & copyop)239 TextNode::TextNode(const TextNode& text, const osg::CopyOp& copyop):
240     osg::Group(text, copyop)
241 {
242 }
243 
~TextNode()244 TextNode::~TextNode()
245 {
246     setTextTechnique(0);
247 }
248 
traverse(osg::NodeVisitor & nv)249 void TextNode::traverse(osg::NodeVisitor& nv)
250 {
251     if (_technique.valid())
252     {
253         _technique->traverse(nv);
254     }
255     else
256     {
257         Group::traverse(nv);
258     }
259 }
260 
setTextTechnique(TextTechnique * technique)261 void TextNode::setTextTechnique(TextTechnique* technique)
262 {
263     if (_technique==technique) return;
264 
265     if (_technique.valid()) _technique->setTextNode(0);
266 
267     if (TextTechnique::getDefaultTextTechinque()==technique)
268     {
269         OSG_NOTICE<<"Warning: Attempt to assign DefaultTextTechnique() prototype to TextNode::setTextTechnique(..), assigning a clone() of it instead."<<std::endl;
270         technique = new TextTechnique(*TextTechnique::getDefaultTextTechinque());
271     }
272 
273     _technique = technique;
274 
275     if (_technique.valid()) _technique->setTextNode(this);
276 }
277 
278 
update()279 void TextNode::update()
280 {
281     getActiveLayout()->layout(*this);
282 }
283 
setText(const std::string & str)284 void TextNode::setText(const std::string& str)
285 {
286     _string.set(str);
287 }
288