1 /* OpenSceneGraph example, osgtext.
2 *
3 *  Permission is hereby granted, free of charge, to any person obtaining a copy
4 *  of this software and associated documentation files (the "Software"), to deal
5 *  in the Software without restriction, including without limitation the rights
6 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 *  copies of the Software, and to permit persons to whom the Software is
8 *  furnished to do so, subject to the following conditions:
9 *
10 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
16 *  THE SOFTWARE.
17 */
18 
19 #include <osgUtil/Optimizer>
20 
21 #include <osgDB/ReadFile>
22 #include <osgDB/WriteFile>
23 #include <osgDB/Registry>
24 
25 #include <osgGA/StateSetManipulator>
26 #include <osgViewer/Viewer>
27 #include <osgViewer/ViewerEventHandlers>
28 
29 #include <osg/Geode>
30 #include <osg/Camera>
31 #include <osg/ShapeDrawable>
32 #include <osg/Sequence>
33 #include <osg/PolygonMode>
34 #include <osg/io_utils>
35 
36 #include <osgText/Font>
37 #include <osgText/Text>
38 
39 // These shaders are only required for GL3/GL4 core profile.
40 // The fragment shader uses the red component of the font texture and not the alpha channel (GL_ALPHA is deprecated in the core profile),.
41 // osgText will write to GL_RED instead of GL_ALPHA if it is compiled with OSG_GL3_AVAILABLE but not with OSG_GL2_AVAILABLE or OSG_GL1_AVAILABLE.
42 #if defined(OSG_GL3_AVAILABLE) && !defined(OSG_GL2_AVAILABLE) && !defined(OSG_GL1_AVAILABLE)
43 static const char *gl3TextVertexShader = {
44     "#version 330 core\n"
45     "in vec4 osg_Vertex;\n"
46     "in vec4 osg_Color;\n"
47     "in vec4 osg_MultiTexCoord0;\n"
48     "uniform mat4 osg_ModelViewProjectionMatrix;\n"
49     "out vec2 texCoord;\n"
50     "out vec4 vertexColor;\n"
51     "void main(void)\n"
52     "{\n"
53     "    gl_Position = osg_ModelViewProjectionMatrix * osg_Vertex;\n"
54     "    texCoord = osg_MultiTexCoord0.xy;\n"
55     "    vertexColor = osg_Color; \n"
56     "}\n"
57 };
58 
59 static const char *gl3TextFragmentShader = {
60     "#version 330 core\n"
61     "uniform sampler2D glyphTexture;\n"
62     "in vec2 texCoord;\n"
63     "in vec4 vertexColor;\n"
64     "out vec4 color;\n"
65     "void main(void)\n"
66     "{\n"
67     "    color = vertexColor * texture(glyphTexture, texCoord).rrrr;\n"
68     "}\n"
69 };
70 #endif
71 
72 
createHUDText()73 osg::Group* createHUDText()
74 {
75 
76     osg::Group* rootNode = new osg::Group;
77 
78     osg::ref_ptr<osgText::Font> font = osgText::readRefFontFile("fonts/arial.ttf");
79 
80     //osg::setNotifyLevel(osg::INFO);
81 
82     osg::Geode* geode  = new osg::Geode;
83     rootNode->addChild(geode);
84 
85     bool useVBOs = false;
86 #if defined(OSG_GL3_AVAILABLE) && !defined(OSG_GL2_AVAILABLE) && !defined(OSG_GL1_AVAILABLE)
87     useVBOs = true;
88     osg::Program* program = new osg::Program;
89     program->addShader(new osg::Shader(osg::Shader::VERTEX, gl3TextVertexShader));
90     program->addShader(new osg::Shader(osg::Shader::FRAGMENT, gl3TextFragmentShader));
91     geode->getOrCreateStateSet()->setAttributeAndModes(program, osg::StateAttribute::ON);
92 #endif
93 
94     float windowHeight = 1024.0f;
95     float windowWidth = 1280.0f;
96     float margin = 50.0f;
97 
98 
99 ////////////////////////////////////////////////////////////////////////////////////////////////////////
100 //
101 // Examples of how to set up different text layout
102 //
103 
104     osg::Vec4 layoutColor(1.0f,1.0f,0.0f,1.0f);
105     float layoutCharacterSize = 20.0f;
106 
107     {
108         osgText::Text* text = new osgText::Text;
109         text->setUseVertexBufferObjects(useVBOs);
110         text->setFont(font);
111         text->setColor(layoutColor);
112         text->setCharacterSize(layoutCharacterSize);
113         text->setPosition(osg::Vec3(margin,windowHeight-margin,0.0f));
114 
115         // the default layout is left to right, typically used in languages
116         // originating from europe such as English, French, German, Spanish etc..
117         text->setLayout(osgText::Text::LEFT_TO_RIGHT);
118 
119         text->setText("text->setLayout(osgText::Text::LEFT_TO_RIGHT);");
120         geode->addDrawable(text);
121     }
122 
123     {
124         osgText::Text* text = new osgText::Text;
125         text->setUseVertexBufferObjects(useVBOs);
126         text->setFont(font);
127         text->setColor(layoutColor);
128         text->setCharacterSize(layoutCharacterSize);
129         text->setPosition(osg::Vec3(windowWidth-margin,windowHeight-margin,0.0f));
130 
131         // right to left layouts would be used for hebrew or arabic fonts.
132         text->setLayout(osgText::Text::RIGHT_TO_LEFT);
133         text->setAlignment(osgText::Text::RIGHT_BASE_LINE);
134 
135         text->setText("text->setLayout(osgText::Text::RIGHT_TO_LEFT);");
136         geode->addDrawable(text);
137     }
138 
139     {
140         osgText::Text* text = new osgText::Text;
141         text->setUseVertexBufferObjects(useVBOs);
142         text->setFont(font);
143         text->setColor(layoutColor);
144         text->setPosition(osg::Vec3(margin,windowHeight-margin,0.0f));
145         text->setCharacterSize(layoutCharacterSize);
146 
147         // vertical font layout would be used for asian fonts.
148         text->setLayout(osgText::Text::VERTICAL);
149 
150         text->setText("text->setLayout(osgText::Text::VERTICAL);");
151         geode->addDrawable(text);
152     }
153 
154 
155 ////////////////////////////////////////////////////////////////////////////////////////////////////////
156 //
157 // Examples of how to set up different font resolution
158 //
159 
160     osg::Vec4 fontSizeColor(0.0f,1.0f,1.0f,1.0f);
161     float fontSizeCharacterSize = 30;
162 
163     osg::Vec3 cursor = osg::Vec3(margin*2,windowHeight-margin*2,0.0f);
164 
165     {
166         osgText::Text* text = new osgText::Text;
167         text->setUseVertexBufferObjects(useVBOs);
168         text->setFont(font);
169         text->setColor(fontSizeColor);
170         text->setCharacterSize(fontSizeCharacterSize);
171         text->setPosition(cursor);
172 
173         // use text that uses 10 by 10 texels as a target resolution for fonts.
174         text->setFontResolution(10,10); // blocky but small texture memory usage
175 
176         text->setText("text->setFontResolution(10,10); // blocky but small texture memory usage");
177         geode->addDrawable(text);
178     }
179 
180     cursor.y() -= fontSizeCharacterSize;
181     {
182         osgText::Text* text = new osgText::Text;
183         text->setUseVertexBufferObjects(useVBOs);
184         text->setFont(font);
185         text->setColor(fontSizeColor);
186         text->setCharacterSize(fontSizeCharacterSize);
187         text->setPosition(cursor);
188 
189         // use text that uses 20 by 20 texels as a target resolution for fonts.
190         text->setFontResolution(20,20); // smoother but higher texture memory usage (but still quite low).
191 
192         text->setText("text->setFontResolution(20,20); // smoother but higher texture memory usage (but still quite low).");
193         geode->addDrawable(text);
194     }
195 
196     cursor.y() -= fontSizeCharacterSize;
197     {
198         osgText::Text* text = new osgText::Text;
199         text->setUseVertexBufferObjects(useVBOs);
200         text->setFont(font);
201         text->setColor(fontSizeColor);
202         text->setCharacterSize(fontSizeCharacterSize);
203         text->setPosition(cursor);
204 
205         // use text that uses 40 by 40 texels as a target resolution for fonts.
206         text->setFontResolution(40,40); // even smoother but again higher texture memory usage.
207 
208         text->setText("text->setFontResolution(40,40); // even smoother but again higher texture memory usage.");
209         geode->addDrawable(text);
210     }
211 
212 
213 ////////////////////////////////////////////////////////////////////////////////////////////////////////
214 //
215 // Examples of how to set up different sized text
216 //
217 
218     osg::Vec4 characterSizeColor(1.0f,0.0f,1.0f,1.0f);
219 
220     cursor.y() -= fontSizeCharacterSize*2.0f;
221 
222     {
223         osgText::Text* text = new osgText::Text;
224         text->setUseVertexBufferObjects(useVBOs);
225         text->setFont(font);
226         text->setColor(characterSizeColor);
227         text->setFontResolution(20,20);
228         text->setPosition(cursor);
229 
230         // use text that is 20 units high.
231         text->setCharacterSize(20); // small
232 
233         text->setText("text->setCharacterSize(20.0f); // small");
234         geode->addDrawable(text);
235     }
236 
237     cursor.y() -= 30.0f;
238     {
239         osgText::Text* text = new osgText::Text;
240         text->setUseVertexBufferObjects(useVBOs);
241         text->setFont(font);
242         text->setColor(characterSizeColor);
243         text->setFontResolution(30,30);
244         text->setPosition(cursor);
245 
246         // use text that is 30 units high.
247         text->setCharacterSize(30.0f); // medium
248 
249         text->setText("text->setCharacterSize(30.0f); // medium");
250         geode->addDrawable(text);
251     }
252 
253     cursor.y() -= 50.0f;
254     {
255         osgText::Text* text = new osgText::Text;
256         text->setUseVertexBufferObjects(useVBOs);
257         text->setFont(font);
258         text->setColor(characterSizeColor);
259         text->setFontResolution(40,40);
260         text->setPosition(cursor);
261 
262         // use text that is 60 units high.
263         text->setCharacterSize(60.0f); // large
264 
265         text->setText("text->setCharacterSize(60.0f); // large");
266         geode->addDrawable(text);
267     }
268 
269 
270 ////////////////////////////////////////////////////////////////////////////////////////////////////////
271 //
272 // Examples of how to set up different alignments
273 //
274 
275     osg::Vec4 alignmentSizeColor(0.0f,1.0f,0.0f,1.0f);
276     float alignmentCharacterSize = 25.0f;
277     cursor.x() = 640;
278     cursor.y() = margin*4.0f;
279 
280     typedef std::pair<osgText::Text::AlignmentType,std::string> AlignmentPair;
281     typedef std::vector<AlignmentPair> AlignmentList;
282     AlignmentList alignmentList;
283     alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_TOP,"text->setAlignment(\nosgText::Text::LEFT_TOP);"));
284     alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_CENTER,"text->setAlignment(\nosgText::Text::LEFT_CENTER);"));
285     alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_BOTTOM,"text->setAlignment(\nosgText::Text::LEFT_BOTTOM);"));
286     alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_TOP,"text->setAlignment(\nosgText::Text::CENTER_TOP);"));
287     alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_CENTER,"text->setAlignment(\nosgText::Text::CENTER_CENTER);"));
288     alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_BOTTOM,"text->setAlignment(\nosgText::Text::CENTER_BOTTOM);"));
289     alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_TOP,"text->setAlignment(\nosgText::Text::RIGHT_TOP);"));
290     alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_CENTER,"text->setAlignment(\nosgText::Text::RIGHT_CENTER);"));
291     alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_BOTTOM,"text->setAlignment(\nosgText::Text::RIGHT_BOTTOM);"));
292     alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_BASE_LINE,"text->setAlignment(\nosgText::Text::LEFT_BASE_LINE);"));
293     alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_BASE_LINE,"text->setAlignment(\nosgText::Text::CENTER_BASE_LINE);"));
294     alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_BASE_LINE,"text->setAlignment(\nosgText::Text::RIGHT_BASE_LINE);"));
295     alignmentList.push_back(AlignmentPair(osgText::Text::LEFT_BOTTOM_BASE_LINE,"text->setAlignment(\nosgText::Text::LEFT_BOTTOM_BASE_LINE);"));
296     alignmentList.push_back(AlignmentPair(osgText::Text::CENTER_BOTTOM_BASE_LINE,"text->setAlignment(\nosgText::Text::CENTER_BOTTOM_BASE_LINE);"));
297     alignmentList.push_back(AlignmentPair(osgText::Text::RIGHT_BOTTOM_BASE_LINE,"text->setAlignment(\nosgText::Text::RIGHT_BOTTOM_BASE_LINE);"));
298 
299 
300     osg::Sequence* sequence = new osg::Sequence;
301     {
302         for(AlignmentList::iterator itr=alignmentList.begin();
303             itr!=alignmentList.end();
304             ++itr)
305         {
306             osg::Geode* alignmentGeode = new osg::Geode;
307             sequence->addChild(alignmentGeode);
308             sequence->setTime(sequence->getNumChildren(), 1.0f);
309 
310             osgText::Text* text = new osgText::Text;
311             text->setFont(font);
312             text->setColor(alignmentSizeColor);
313             text->setCharacterSize(alignmentCharacterSize);
314             text->setPosition(cursor);
315             text->setDrawMode(osgText::Text::TEXT|osgText::Text::ALIGNMENT|osgText::Text::BOUNDINGBOX);
316 
317             text->setAlignment(itr->first);
318             text->setText(itr->second);
319 
320             alignmentGeode->addDrawable(text);
321 
322 
323         }
324 
325     }
326 
327     sequence->setMode(osg::Sequence::START);
328     sequence->setInterval(osg::Sequence::LOOP, 0, -1);
329     sequence->setDuration(1.0f, -1);
330 
331     rootNode->addChild(sequence);
332 
333 
334 ////////////////////////////////////////////////////////////////////////////////////////////////////////
335 //
336 // Examples of how to set up different fonts...
337 //
338 
339     cursor.x() = margin*2.0f;
340     cursor.y() = margin*2.0f;
341 
342     osg::Vec4 fontColor(1.0f,0.5f,0.0f,1.0f);
343     float fontCharacterSize = 20.0f;
344     float spacing = 40.0f;
345 
346     {
347         osg::ref_ptr<osgText::Text> text = new osgText::Text;
348         text->setUseVertexBufferObjects(useVBOs);
349         text->setColor(fontColor);
350         text->setPosition(cursor);
351         text->setCharacterSize(fontCharacterSize);
352 
353         text->setFont(0);
354         text->setText("text->setFont(0); // inbuilt font.");
355         geode->addDrawable(text);
356 
357         cursor.x() = text->getBoundingBox().xMax() + spacing ;
358     }
359 
360     {
361         osg::ref_ptr<osgText::Font> arial = osgText::readRefFontFile("fonts/arial.ttf");
362 
363         osg::ref_ptr<osgText::Text> text = new osgText::Text;
364         text->setUseVertexBufferObjects(useVBOs);
365         text->setColor(fontColor);
366         text->setPosition(cursor);
367         text->setCharacterSize(fontCharacterSize);
368 
369         text->setFont(arial);
370         text->setText(arial!=0?
371                       "text->setFont(\"fonts/arial.ttf\");":
372                       "unable to load \"fonts/arial.ttf\"");
373         geode->addDrawable(text);
374 
375         cursor.x() = text->getBoundingBox().xMax() + spacing ;
376     }
377 
378     {
379         osg::ref_ptr<osgText::Font> times = osgText::readRefFontFile("fonts/times.ttf");
380 
381         osg::ref_ptr<osgText::Text> text = new osgText::Text;
382         text->setUseVertexBufferObjects(useVBOs);
383         text->setColor(fontColor);
384         text->setPosition(cursor);
385         text->setCharacterSize(fontCharacterSize);
386 
387         geode->addDrawable(text);
388         text->setFont(times);
389         text->setText(times!=0?
390                       "text->setFont(\"fonts/times.ttf\");":
391                       "unable to load \"fonts/times.ttf\"");
392 
393         cursor.x() = text->getBoundingBox().xMax() + spacing ;
394     }
395 
396     cursor.x() = margin*2.0f;
397     cursor.y() = margin;
398 
399     {
400         osg::ref_ptr<osgText::Font> dirtydoz = osgText::readRefFontFile("fonts/dirtydoz.ttf");
401 
402         osg::ref_ptr<osgText::Text> text = new osgText::Text;
403         text->setUseVertexBufferObjects(useVBOs);
404         text->setColor(fontColor);
405         text->setPosition(cursor);
406         text->setCharacterSize(fontCharacterSize);
407 
408         text->setFont(dirtydoz);
409         text->setText(dirtydoz!=0?
410                       "text->setFont(\"fonts/dirtydoz.ttf\");":
411                       "unable to load \"fonts/dirtydoz.ttf\"");
412         geode->addDrawable(text);
413 
414         cursor.x() = text->getBoundingBox().xMax() + spacing ;
415     }
416 
417     {
418         osg::ref_ptr<osgText::Font> fudd = osgText::readRefFontFile("fonts/fudd.ttf");
419 
420         osg::ref_ptr<osgText::Text> text = new osgText::Text;
421         text->setUseVertexBufferObjects(useVBOs);
422         text->setColor(fontColor);
423         text->setPosition(cursor);
424         text->setCharacterSize(fontCharacterSize);
425 
426         text->setFont(fudd);
427         text->setText(fudd!=0?
428                       "text->setFont(\"fonts/fudd.ttf\");":
429                       "unable to load \"fonts/fudd.ttf\"");
430         geode->addDrawable(text);
431 
432         cursor.x() = text->getBoundingBox().xMax() + spacing ;
433     }
434 
435     return rootNode;
436 }
437 
438 
439 
440 
441 // create text which sits in 3D space such as would be inserted into a normal model
create3DText(const osg::Vec3 & center,float radius)442 osg::Group* create3DText(const osg::Vec3& center,float radius)
443 {
444 
445     osg::Geode* geode  = new osg::Geode;
446 
447     bool useVBOs = false;
448 #if defined(OSG_GL3_AVAILABLE) && !defined(OSG_GL2_AVAILABLE) && !defined(OSG_GL1_AVAILABLE)
449     useVBOs = true;
450     osg::Program* program = new osg::Program;
451     program->addShader(new osg::Shader(osg::Shader::VERTEX, gl3TextVertexShader));
452     program->addShader(new osg::Shader(osg::Shader::FRAGMENT, gl3TextFragmentShader));
453     geode->getOrCreateStateSet()->setAttributeAndModes(program, osg::StateAttribute::ON);
454 #endif
455 
456 
457 ////////////////////////////////////////////////////////////////////////////////////////////////////////
458 //
459 // Examples of how to set up axis/orientation alignments
460 //
461 
462     float characterSize=radius*0.2f;
463 
464 
465     osg::Vec3 pos(center.x()-radius*.5f,center.y()-radius*.5f,center.z()-radius*.5f);
466 
467     osgText::Text* text1 = new osgText::Text;
468     text1->setUseVertexBufferObjects(useVBOs);
469     text1->setFont("fonts/times.ttf");
470     text1->setCharacterSize(characterSize);
471     text1->setPosition(pos);
472     text1->setAxisAlignment(osgText::Text::XY_PLANE);
473     text1->setText("XY_PLANE");
474     geode->addDrawable(text1);
475 
476     osgText::Text* text2 = new osgText::Text;
477     text2->setUseVertexBufferObjects(useVBOs);
478     text2->setFont("fonts/times.ttf");
479     text2->setCharacterSize(characterSize);
480     text2->setPosition(pos);
481     text2->setAxisAlignment(osgText::Text::YZ_PLANE);
482     text2->setText("YZ_PLANE");
483     geode->addDrawable(text2);
484 
485     osgText::Text* text3 = new osgText::Text;
486     text3->setUseVertexBufferObjects(useVBOs);
487     text3->setFont("fonts/times.ttf");
488     text3->setCharacterSize(characterSize);
489     text3->setPosition(pos);
490     text3->setAxisAlignment(osgText::Text::XZ_PLANE);
491     text3->setText("XZ_PLANE");
492     geode->addDrawable(text3);
493 
494     osg::Vec4 characterSizeModeColor(1.0f,0.0f,0.5f,1.0f);
495 
496     osgText::Text* text4 = new osgText::Text;
497     text4->setUseVertexBufferObjects(useVBOs);
498     text4->setFont("fonts/times.ttf");
499     text4->setCharacterSize(characterSize);
500     text4->setPosition(center);
501     text4->setAxisAlignment(osgText::Text::SCREEN);
502 
503     // reproduce outline bounding box compute problem with backdrop on.
504     text4->setBackdropType(osgText::Text::OUTLINE);
505     text4->setDrawMode(osgText::Text::TEXT | osgText::Text::BOUNDINGBOX);
506 
507     text4->setText("SCREEN");
508     geode->addDrawable(text4);
509 
510     osgText::Text* text5 = new osgText::Text;
511     text5->setUseVertexBufferObjects(useVBOs);
512     text5->setColor(characterSizeModeColor);
513     text5->setFont("fonts/times.ttf");
514     //text5->setCharacterSize(characterSize);
515     text5->setCharacterSize(32.0f); // medium
516     text5->setPosition(center - osg::Vec3(0.0, 0.0, 0.2));
517     text5->setAxisAlignment(osgText::Text::SCREEN);
518     text5->setCharacterSizeMode(osgText::Text::SCREEN_COORDS);
519     text5->setDrawMode(osgText::Text::TEXT | osgText::Text::BOUNDINGBOX);
520     text5->setText("CharacterSizeMode SCREEN_COORDS(size 32.0)");
521     geode->addDrawable(text5);
522 
523     osgText::Text* text6 = new osgText::Text;
524     text6->setUseVertexBufferObjects(useVBOs);
525     text6->setColor(characterSizeModeColor);
526     text6->setFont("fonts/times.ttf");
527     text6->setCharacterSize(characterSize);
528     text6->setPosition(center - osg::Vec3(0.0, 0.0, 0.4));
529     text6->setAxisAlignment(osgText::Text::SCREEN);
530     text6->setCharacterSizeMode(osgText::Text::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT);
531     text6->setText("CharacterSizeMode OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT");
532     geode->addDrawable(text6);
533 
534     osgText::Text* text7 = new osgText::Text;
535     text7->setUseVertexBufferObjects(useVBOs);
536     text7->setColor(characterSizeModeColor);
537     text7->setFont("fonts/times.ttf");
538     text7->setCharacterSize(characterSize);
539     text7->setPosition(center - osg::Vec3(0.0, 0.0, 0.6));
540     text7->setAxisAlignment(osgText::Text::SCREEN);
541     text7->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
542     text7->setText("CharacterSizeMode OBJECT_COORDS (default)");
543     geode->addDrawable(text7);
544 
545 
546 
547     osg::ShapeDrawable* shape = new osg::ShapeDrawable(new osg::Sphere(center,characterSize*0.2f));
548     shape->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::ON);
549     geode->addDrawable(shape);
550 
551     osg::Group* rootNode = new osg::Group;
552     rootNode->addChild(geode);
553 
554     return rootNode;
555 }
556 
557 class UpdateTextOperation : public osg::Operation
558 {
559 public:
560 
UpdateTextOperation(const osg::Vec3 & center,float diameter,osg::Group * group)561     UpdateTextOperation(const osg::Vec3& center, float diameter, osg::Group* group):
562         osg::Referenced(true),
563         Operation("UpdateTextOperation", true),
564         _center(center),
565         _diameter(diameter),
566         _maxNumChildren(200),
567         _maxNumTextPerGeode(10),
568         _group(group)
569     {
570     }
571 
operator ()(osg::Object * callingObject)572     virtual void operator () (osg::Object* callingObject)
573     {
574         // decided which method to call according to whole has called me.
575         osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(callingObject);
576 
577         if (viewer) update();
578         else load();
579     }
580 
update()581     void update()
582     {
583         // osg::notify(osg::NOTICE)<<"*** Doing update"<<std::endl;
584 
585         OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
586 
587         if (_mergeSubgraph.valid())
588         {
589             _group->addChild(_mergeSubgraph.get());
590 
591             _mergeSubgraph = 0;
592 
593             if (_group->getNumChildren()>_maxNumChildren)
594             {
595                 osg::Geode* geode = dynamic_cast<osg::Geode*>(_group->getChild(0));
596                 if (geode)
597                 {
598                     _availableSubgraph.push_back(geode);
599                     geode->removeDrawables(0,geode->getNumDrawables());
600                 }
601                 _group->removeChild(0,1);
602             }
603 
604             _waitOnMergeBlock.release();
605         }
606     }
607 
load()608     void load()
609     {
610 
611         // osg::notify(osg::NOTICE)<<"Doing load"<<std::endl;
612 
613         osg::ref_ptr<osg::Geode> geode;
614         {
615             OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
616             if (!_availableSubgraph.empty())
617             {
618                 geode = _availableSubgraph.front();
619                 _availableSubgraph.pop_front();
620             }
621         }
622 
623         if (!geode) geode = new osg::Geode;
624 
625         for(unsigned int i=0; i<_maxNumTextPerGeode; ++i)
626         {
627             float x = float(rand()) / float(RAND_MAX) - 0.5f;
628             float y = float(rand()) / float(RAND_MAX) - 0.5f;
629             float z = float(i)      / float(_maxNumTextPerGeode) - 0.5f;
630             osg::Vec3 position(x, y, z);
631 
632             std::string str;
633             unsigned int _numCharacters = 5;
634             for(unsigned int ni=0; ni<_numCharacters;++ni)
635             {
636                 str.push_back(char(32.0 + (float(rand())/float(RAND_MAX))*128.0f));
637             }
638 
639             osgText::Text* text = new osgText::Text;
640             text->setDataVariance(osg::Object::DYNAMIC);
641             text->setPosition(_center + position * _diameter);
642             text->setFont("times.ttf");
643             text->setText(str);
644             text->setCharacterSize(0.025f * _diameter);
645             text->setAxisAlignment(osgText::Text::SCREEN);
646 
647             geode->addDrawable(text);
648         }
649 
650 
651         {
652             OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
653             _mergeSubgraph = geode;
654         }
655 
656         // osg::notify(osg::NOTICE)<<"Waiting on merge"<<std::endl;
657 
658         _waitOnMergeBlock.block();
659 
660     }
661 
release()662     virtual void release()
663     {
664         _waitOnMergeBlock.release();
665     }
666 
667     typedef std::list< osg::ref_ptr<osg::Geode> > AvailableList;
668 
669     osg::Vec3                   _center;
670     float                       _diameter;
671     unsigned int                _maxNumChildren;
672     unsigned int                _maxNumTextPerGeode;
673 
674     OpenThreads::Mutex          _mutex;
675     osg::ref_ptr<osg::Group>    _group;
676     osg::ref_ptr<osg::Geode>    _mergeSubgraph;
677     AvailableList               _availableSubgraph;
678     OpenThreads::Block          _waitOnMergeBlock;
679 
680     unsigned int                _counter;
681 
682 };
683 
684 
main(int argc,char ** argv)685 int main(int argc, char** argv)
686 {
687     osg::ArgumentParser arguments(&argc, argv);
688 
689     // construct the viewer.
690     osgViewer::Viewer viewer(arguments);
691 
692     typedef std::list< osg::ref_ptr<osg::OperationThread> > Threads;
693 
694     Threads operationThreads;
695     osg::ref_ptr<UpdateTextOperation> updateOperation;
696 
697     unsigned int numThreads = 0;
698     if (arguments.read("--mt", numThreads) || arguments.read("--mt"))
699     {
700         // construct a multi-threaded text updating test.
701         if (numThreads==0) numThreads = 1;
702 
703         // create a group to add everything into.
704         osg::Group* mainGroup = new osg::Group;
705 
706         osg::Vec3 center(0.5f,0.5f,0.5f);
707         float diameter = 1.0f;
708 
709         osg::ref_ptr<osg::Node> loadedModel = osgDB::readRefNodeFiles(arguments);
710         if (loadedModel.valid())
711         {
712             mainGroup->addChild(loadedModel);
713 
714             center = loadedModel->getBound().center();
715             diameter = loadedModel->getBound().radius() * 2.0f;
716         }
717 
718         for(unsigned int i=0; i<numThreads; ++i)
719         {
720             osg::Group* textGroup = new osg::Group;
721             mainGroup->addChild(textGroup);
722 
723             // create the background thread
724             osg::OperationThread* operationThread = new osg::OperationThread;
725 
726             operationThreads.push_back(operationThread);
727 
728             // create the operation that will run in the background and
729             // sync once per frame with the main viewer loop.
730             updateOperation = new UpdateTextOperation(center, diameter, textGroup);
731 
732             // add the operation to the operation thread and start it.
733             operationThread->add(updateOperation.get());
734             operationThread->startThread();
735 
736             // add the operation to the viewer to sync once per frame.
737             viewer.addUpdateOperation(updateOperation.get());
738 
739 
740             // add a unit cube for the text to appear within.
741             osg::Geode* geode = new osg::Geode;
742             geode->getOrCreateStateSet()->setAttribute(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::LINE));
743             geode->addDrawable(new osg::ShapeDrawable(new osg::Box(center,diameter)));
744 
745             mainGroup->addChild(geode);
746         }
747 
748         viewer.setSceneData(mainGroup);
749     }
750     else
751     {
752         // prepare scene.
753         osg::Vec3 center(0.0f,0.0f,0.0f);
754         float radius = 1.0f;
755 
756         // make sure the root node is group so we can add extra nodes to it.
757         osg::Group* group = new osg::Group;
758 
759         if (true)
760         {
761             // create the hud.
762             osg::Camera* camera = new osg::Camera;
763             camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
764             camera->setProjectionMatrixAsOrtho2D(0,1280,0,1024);
765             camera->setViewMatrix(osg::Matrix::identity());
766             camera->setClearMask(GL_DEPTH_BUFFER_BIT);
767             camera->addChild(createHUDText());
768             camera->getOrCreateStateSet()->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
769 
770             group->addChild(camera);
771         }
772 
773         if (true)
774         {
775             group->addChild(create3DText(center,radius));
776         }
777 
778         // set the scene to render
779         viewer.setSceneData(group);
780     }
781 
782     std::string filename;
783     if (arguments.read("-o",filename))
784     {
785         osgDB::writeNodeFile(*viewer.getSceneData(),filename);
786         return 0;
787     }
788 
789     viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
790     viewer.addEventHandler(new osgViewer::StatsHandler());
791 
792     viewer.run();
793 
794     if (!operationThreads.empty())
795     {
796         for(Threads::iterator itr = operationThreads.begin();
797             itr != operationThreads.end();
798             ++itr)
799         {
800             (*itr)->cancel();
801         }
802     }
803 
804     return 0;
805 }
806 
807