]> git.mxchange.org Git - flightgear.git/blob - src/Viewer/splash.cxx
Code cleanups, code updates and fix at least on (possible) devide-by-zero
[flightgear.git] / src / Viewer / splash.cxx
1 // splash.cxx -- draws the initial splash screen
2 //
3 // Written by Curtis Olson, started July 1998.  (With a little looking
4 // at Freidemann's panel code.) :-)
5 //
6 // Copyright (C) 1997  Michele F. America  - nomimarketing@mail.telepac.pt
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23
24
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28
29 #include <osg/BlendFunc>
30 #include <osg/Camera>
31 #include <osg/Depth>
32 #include <osg/Geometry>
33 #include <osg/Node>
34 #include <osg/NodeCallback>
35 #include <osg/NodeVisitor>
36 #include <osg/StateSet>
37 #include <osg/Switch>
38 #include <osg/Texture2D>
39 #include <osgUtil/CullVisitor>
40 #include <osgText/Text>
41 #include <osgDB/ReadFile>
42
43 #include <simgear/compiler.h>
44
45 #include <simgear/debug/logstream.hxx>
46 #include <simgear/math/sg_random.h>
47 #include <simgear/misc/sg_path.hxx>
48
49 #include <plib/fnt.h>
50
51 #include "GUI/FGFontCache.hxx"
52 #include "GUI/FGColor.hxx"
53
54 #include <Main/globals.hxx>
55 #include <Main/fg_props.hxx>
56 #include <Main/fg_os.hxx>
57 #include <Main/locale.hxx>
58 #include "splash.hxx"
59 #include "renderer.hxx"
60
61 class FGSplashUpdateCallback : public osg::Drawable::UpdateCallback {
62 public:
63   FGSplashUpdateCallback(osg::Vec4Array* colorArray, SGPropertyNode* prop) :
64     _colorArray(colorArray),
65     _colorProperty(prop),
66     _alphaProperty(fgGetNode("/sim/startup/splash-alpha", true))
67   { }
68   virtual void update(osg::NodeVisitor*, osg::Drawable*)
69   {
70     FGColor c(0, 0, 0);
71     if (_colorProperty) {
72       c.merge(_colorProperty);
73       (*_colorArray)[0][0] = c.red();
74       (*_colorArray)[0][1] = c.green();
75       (*_colorArray)[0][2] = c.blue();
76     }
77     (*_colorArray)[0][3] = _alphaProperty->getFloatValue();
78     _colorArray->dirty();
79   }
80 private:
81   osg::ref_ptr<osg::Vec4Array> _colorArray;
82   SGSharedPtr<const SGPropertyNode> _colorProperty;
83   SGSharedPtr<const SGPropertyNode> _alphaProperty;
84 };
85
86 class FGSplashTextUpdateCallback : public osg::Drawable::UpdateCallback {
87 public:
88   FGSplashTextUpdateCallback(const SGPropertyNode* prop) :
89     _textProperty(prop),
90     _alphaProperty(fgGetNode("/sim/startup/splash-alpha", true)),
91     _styleProperty(fgGetNode("/sim/gui/style[0]", true))
92   {}
93   virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
94   {
95     assert(dynamic_cast<osgText::Text*>(drawable));
96     osgText::Text* text = static_cast<osgText::Text*>(drawable);
97
98     FGColor c(1.0, 0.9, 0.0);
99     c.merge(_styleProperty->getNode("colors/splash-font"));
100     float alpha = _alphaProperty->getFloatValue();
101     text->setColor(osg::Vec4(c.red(), c.green(), c.blue(), alpha));
102
103     const char* s = _textProperty->getStringValue();
104     if (s && fgGetBool("/sim/startup/splash-progress", true))
105       text->setText(s);
106     else
107       text->setText("");
108   }
109 private:
110   SGSharedPtr<const SGPropertyNode> _textProperty;
111   SGSharedPtr<const SGPropertyNode> _alphaProperty;
112   SGSharedPtr<const SGPropertyNode> _styleProperty;
113 };
114
115
116
117 class FGSplashContentProjectionCalback : public osg::NodeCallback {
118 public:
119   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
120   { 
121     assert(dynamic_cast<osgUtil::CullVisitor*>(nv));
122     osgUtil::CullVisitor* cullVisitor = static_cast<osgUtil::CullVisitor*>(nv);
123
124     // adjust the projection matrix in a way that preserves the aspect ratio
125     // of the content ...
126     const osg::Viewport* viewport = cullVisitor->getViewport();
127     float viewportAspect = float(viewport->height())/float(viewport->width());
128
129     float height, width;
130     if (viewportAspect < 1) {
131       height = 1;
132       width = 1/viewportAspect;
133     } else {
134       height = viewportAspect;
135       width = 1;
136     }
137
138     osg::RefMatrix* matrix = new osg::RefMatrix;
139     matrix->makeOrtho2D(-width, width, -height, height);
140
141     // The trick is to have the projection matrix adapted independent
142     // of the scenegraph but dependent on the viewport of this current
143     // camera we cull for. Therefore we do not put that projection matrix into
144     // an additional camera rather than from within that cull callback.
145     cullVisitor->pushProjectionMatrix(matrix);
146     traverse(node, nv);
147     cullVisitor->popProjectionMatrix();
148   }
149 };
150
151 char *genNameString()
152 {
153     std::string website = "http://www.flightgear.org";
154     std::string programName = "FlightGear";
155     char *name = new char[26];
156     name[20] = 114;
157     name[8] = 119;
158     name[5] = 47;
159     name[12] = 108;
160     name[2] = 116;
161     name[1] = 116;
162     name[16] = 116;
163     name[13] = 105;
164     name[17] = 103;
165     name[19] = 97;
166     name[25] = 0;
167     name[0] = 104;
168     name[24] = 103;
169     name[21] = 46;
170     name[15] = 104;
171     name[3] = 112;
172     name[22] = 111;
173     name[18] = 101;
174     name[7] = 119;
175     name[14] = 103;
176     name[23] = 114;
177     name[4] = 58;
178     name[11] = 102;
179     name[9] = 119;
180     name[10] = 46;
181     name[6] = 47;
182     return name;
183 }
184
185 static osg::Node* fgCreateSplashCamera()
186 {
187   char *namestring = genNameString();
188   fgSetString("/sim/startup/program-name", namestring);
189   delete[] namestring;
190
191     simgear::PropertyList textures = fgGetNode("/sim/startup", true)->getChildren("splash-texture");
192     std::vector<SGPath> useable_textures;
193     SGPath tpath;
194
195     // Build a list of textures that are usable; those whose path can be resolved
196     for (simgear::PropertyList::iterator it = textures.begin(); it != textures.end(); it++) {
197         std::string value = (*it)->getStringValue();
198         if (!value.empty()) {
199             tpath = globals->resolve_maybe_aircraft_path(value);
200             if (!tpath.isNull()) {
201                 useable_textures.push_back(tpath);
202             }
203             else {
204                 SG_LOG(SG_VIEW, SG_ALERT, "Cannot find splash screen file '" << value << "'");
205             }
206         }
207     }
208
209     // Seed the RNG in order to get randomness
210     sg_srandom_time();
211
212     if (!useable_textures.empty()) {
213         // Select a random useable texture
214         const int index = (int)(sg_random() * useable_textures.size());
215         tpath = useable_textures[index];
216     }
217
218   if (tpath.isNull()) {
219     // no splash screen specified - select random image
220     tpath = globals->get_fg_root();
221     // load in the texture data
222     int num = (int)(sg_random() * 5.0 + 1.0);
223     char num_str[5];
224     snprintf(num_str, 4, "%d", num);
225
226     tpath.append( "Textures/Splash" );
227     tpath.concat( num_str );
228     tpath.concat( ".png" );
229   }
230
231   SGSharedPtr<SGPropertyNode> style = fgGetNode("/sim/gui/style[0]", true);
232
233   osg::Texture2D* splashTexture = new osg::Texture2D;
234   splashTexture->setImage(osgDB::readImageFile(tpath.c_str()));
235
236   osg::Camera* camera = new osg::Camera;
237   camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
238   camera->setProjectionMatrix(osg::Matrix::ortho2D(-1, 1, -1, 1));
239   camera->setViewMatrix(osg::Matrix::identity());
240   camera->setRenderOrder(osg::Camera::POST_RENDER, 10000);
241   camera->setClearMask(0);
242   camera->setAllowEventFocus(false);
243   camera->setCullingActive(false);
244
245   osg::StateSet* stateSet = camera->getOrCreateStateSet();
246   stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::OFF);
247   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
248   stateSet->setAttribute(new osg::BlendFunc);
249   stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
250   stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
251   stateSet->setAttribute(new osg::Depth(osg::Depth::ALWAYS, 0, 1, false));
252   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
253
254
255   osg::Geometry* geometry = new osg::Geometry;
256   geometry->setSupportsDisplayList(false);
257
258   osg::Vec3Array* vertexArray = new osg::Vec3Array;
259   vertexArray->push_back(osg::Vec3(-1, -1, 0));
260   vertexArray->push_back(osg::Vec3( 1, -1, 0));
261   vertexArray->push_back(osg::Vec3( 1,  1, 0));
262   vertexArray->push_back(osg::Vec3(-1,  1, 0));
263   geometry->setVertexArray(vertexArray);
264   osg::Vec4Array* colorArray = new osg::Vec4Array;
265   colorArray->push_back(osg::Vec4(0, 0, 0, 1));
266   geometry->setColorArray(colorArray);
267   geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
268   geometry->addPrimitiveSet(new osg::DrawArrays(GL_POLYGON, 0, 4));
269   geometry->setUpdateCallback(new FGSplashUpdateCallback(colorArray,
270                               style->getNode("colors/splash-screen")));
271
272   osg::Geode* geode = new osg::Geode;
273   geode->addDrawable(geometry);
274
275   stateSet = geode->getOrCreateStateSet();
276   stateSet->setRenderBinDetails(1, "RenderBin");
277   camera->addChild(geode);
278
279
280   // The group is needed because of osg is handling the cull callbacks in a
281   // different way for groups than for a geode. It does not hurt here ...
282   osg::Group* group = new osg::Group;
283   group->setCullCallback(new FGSplashContentProjectionCalback);
284   camera->addChild(group);
285
286   geode = new osg::Geode;
287   stateSet = geode->getOrCreateStateSet();
288   stateSet->setRenderBinDetails(2, "RenderBin");
289   group->addChild(geode);
290
291
292   geometry = new osg::Geometry;
293   geometry->setSupportsDisplayList(false);
294
295   vertexArray = new osg::Vec3Array;
296   vertexArray->push_back(osg::Vec3(-0.84, -0.84, 0));
297   vertexArray->push_back(osg::Vec3( 0.84, -0.84, 0));
298   vertexArray->push_back(osg::Vec3( 0.84,  0.84, 0));
299   vertexArray->push_back(osg::Vec3(-0.84,  0.84, 0));
300   geometry->setVertexArray(vertexArray);
301   osg::Vec2Array* texCoordArray = new osg::Vec2Array;
302   texCoordArray->push_back(osg::Vec2(0, 0));
303   texCoordArray->push_back(osg::Vec2(1, 0));
304   texCoordArray->push_back(osg::Vec2(1, 1));
305   texCoordArray->push_back(osg::Vec2(0, 1));
306   geometry->setTexCoordArray(0, texCoordArray);
307   colorArray = new osg::Vec4Array;
308   colorArray->push_back(osg::Vec4(1, 1, 1, 1));
309   geometry->setColorArray(colorArray);
310   geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
311   geometry->addPrimitiveSet(new osg::DrawArrays(GL_POLYGON, 0, 4));
312   geometry->setUpdateCallback(new FGSplashUpdateCallback(colorArray, 0));
313   stateSet = geometry->getOrCreateStateSet();
314   stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
315   stateSet->setTextureAttribute(0, splashTexture);
316   geode->addDrawable(geometry);
317
318     FGFontCache* fontCache = FGFontCache::instance();
319   osgText::Text* text = new osgText::Text;
320   std::string fn = style->getStringValue("fonts/splash", "");
321   text->setFont(fontCache->getfntpath(fn).str());
322   text->setCharacterSize(0.06);
323   text->setColor(osg::Vec4(1, 1, 1, 1));
324   text->setPosition(osg::Vec3(0, -0.92, 0));
325   text->setAlignment(osgText::Text::CENTER_CENTER);
326   SGPropertyNode* prop = fgGetNode("/sim/startup/splash-progress-text", true);
327   prop->setStringValue("");
328   text->setUpdateCallback(new FGSplashTextUpdateCallback(prop));
329   geode->addDrawable(text);
330
331   osgText::Text* spinnertext = new osgText::Text;
332   spinnertext->setFont(fontCache->getfntpath(fn).str());
333   spinnertext->setCharacterSize(0.06);
334   spinnertext->setColor(osg::Vec4(1, 1, 1, 1));
335   spinnertext->setPosition(osg::Vec3(0, -0.97, 0));
336   spinnertext->setAlignment(osgText::Text::CENTER_CENTER);
337   prop = fgGetNode("/sim/startup/splash-progress-spinner", true);
338   prop->setStringValue("");
339   spinnertext->setUpdateCallback(new FGSplashTextUpdateCallback(prop));
340   geode->addDrawable(spinnertext);
341
342   text = new osgText::Text;
343   text->setFont(fontCache->getfntpath(fn).str());
344   text->setCharacterSize(0.08);
345   text->setColor(osg::Vec4(1, 1, 1, 1));
346   text->setPosition(osg::Vec3(0, 0.92, 0));
347   text->setAlignment(osgText::Text::CENTER_CENTER);
348   prop = fgGetNode("/sim/startup/program-name", "FlightGear");
349   text->setUpdateCallback(new FGSplashTextUpdateCallback(prop));
350   geode->addDrawable(text);
351
352
353   text = new osgText::Text;
354   text->setFont(fontCache->getfntpath(fn).str());
355   text->setCharacterSize(0.06);
356   text->setColor(osg::Vec4(1, 1, 1, 1));
357   text->setPosition(osg::Vec3(0, 0.82, 0));
358   text->setAlignment(osgText::Text::CENTER_CENTER);
359   prop = fgGetNode("/sim/startup/splash-title", true);
360   text->setUpdateCallback(new FGSplashTextUpdateCallback(prop));
361   geode->addDrawable(text);
362
363   fgSplashProgress("init");
364
365   return camera;
366 }
367
368 // update callback for the switch node guarding that splash
369 class FGSplashGroupUpdateCallback : public osg::NodeCallback {
370 public:
371   FGSplashGroupUpdateCallback() :
372     _splashAlphaNode(fgGetNode("/sim/startup/splash-alpha", true))
373   { }
374   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
375   {
376     assert(dynamic_cast<osg::Group*>(node));
377     osg::Group* group = static_cast<osg::Group*>(node);
378
379     double alpha = _splashAlphaNode->getDoubleValue();
380     if (alpha <= 0 || !fgGetBool("/sim/startup/splash-screen"))
381       group->removeChild(0, group->getNumChildren());
382     else if (group->getNumChildren() == 0)
383       group->addChild(fgCreateSplashCamera());
384
385     traverse(node, nv);
386   }
387 private:
388   SGSharedPtr<const SGPropertyNode> _splashAlphaNode;
389 };
390
391 osg::Node* fgCreateSplashNode() {
392   osg::Group* group = new osg::Group;
393     group->setName("splashGroup");
394   group->setUpdateCallback(new FGSplashGroupUpdateCallback);
395   return group;
396 }
397
398 // Initialize the splash screen
399 void fgSplashInit ()
400 {
401   globals->get_renderer()->splashinit();
402 }
403
404 void fgSplashProgress( const char *identifier, unsigned int percent ) {
405   const char* spinChars = "-\\|/";
406   static int spin_count = 0;
407   std::string spin_status = std::string("");
408
409   if (identifier[0] != 0)
410       spin_status += spinChars[spin_count++ % 4];
411
412   fgSetString("/sim/startup/splash-progress-spinner", spin_status);
413
414   std::string text;
415   if (identifier[0] != 0)
416   {
417     std::string id = std::string("splash/") + identifier;
418     text = globals->get_locale()->getLocalizedString(id.c_str(), "sys", "");
419
420     if( text.empty() )
421       text = "<incomplete language resource>: " + id;
422   }
423     
424     if (!strcmp(identifier,"downloading-scenery")) {
425         std::ostringstream oss;
426         unsigned int kbytesPerSec = fgGetInt("/sim/terrasync/transfer-rate-bytes-sec") / 1024;
427         oss << text;
428         if (kbytesPerSec > 0) {
429             oss << " - " << kbytesPerSec << " KBytes/sec";
430         }
431         fgSetString("/sim/startup/splash-progress-text", oss.str());
432         return;
433     }
434
435     // over-write the spinner
436     if (!strncmp(identifier, "navdata-", 8)) {
437         std::ostringstream oss;
438         oss << percent << "% complete";
439         fgSetString("/sim/startup/splash-progress-spinner", oss.str());
440     }
441     
442     if( fgGetString("/sim/startup/splash-progress-text") == text )
443       return;
444     
445     SG_LOG( SG_VIEW, SG_INFO, "Splash screen progress " << identifier );
446     fgSetString("/sim/startup/splash-progress-text", text);
447 }