1 // splash.cxx -- draws the initial splash screen
3 // Written by Curtis Olson, started July 1998. (With a little looking
4 // at Freidemann's panel code.) :-)
6 // Copyright (C) 1997 Michele F. America - nomimarketing@mail.telepac.pt
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.
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.
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.
29 #include <osg/BlendFunc>
32 #include <osg/Geometry>
34 #include <osg/NodeCallback>
35 #include <osg/NodeVisitor>
36 #include <osg/StateSet>
38 #include <osg/Texture2D>
39 #include <osgUtil/CullVisitor>
40 #include <osgText/Text>
41 #include <osgDB/ReadFile>
43 #include <simgear/compiler.h>
45 #include <simgear/debug/logstream.hxx>
46 #include <simgear/math/sg_random.h>
47 #include <simgear/misc/sg_path.hxx>
51 #include "GUI/FGFontCache.hxx"
52 #include "GUI/FGColor.hxx"
54 #include <Main/globals.hxx>
55 #include <Main/fg_props.hxx>
56 #include <Main/fg_os.hxx>
57 #include <Main/locale.hxx>
59 #include "renderer.hxx"
61 class FGSplashUpdateCallback : public osg::Drawable::UpdateCallback {
63 FGSplashUpdateCallback(osg::Vec4Array* colorArray, SGPropertyNode* prop) :
64 _colorArray(colorArray),
66 _alphaProperty(fgGetNode("/sim/startup/splash-alpha", true))
68 virtual void update(osg::NodeVisitor*, osg::Drawable*)
72 c.merge(_colorProperty);
73 (*_colorArray)[0][0] = c.red();
74 (*_colorArray)[0][1] = c.green();
75 (*_colorArray)[0][2] = c.blue();
77 (*_colorArray)[0][3] = _alphaProperty->getFloatValue();
81 osg::ref_ptr<osg::Vec4Array> _colorArray;
82 SGSharedPtr<const SGPropertyNode> _colorProperty;
83 SGSharedPtr<const SGPropertyNode> _alphaProperty;
86 class FGSplashTextUpdateCallback : public osg::Drawable::UpdateCallback {
88 FGSplashTextUpdateCallback(const SGPropertyNode* prop) :
90 _alphaProperty(fgGetNode("/sim/startup/splash-alpha", true)),
91 _styleProperty(fgGetNode("/sim/gui/style[0]", true))
93 virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
95 assert(dynamic_cast<osgText::Text*>(drawable));
96 osgText::Text* text = static_cast<osgText::Text*>(drawable);
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));
103 const char* s = _textProperty->getStringValue();
104 if (s && fgGetBool("/sim/startup/splash-progress", true))
110 SGSharedPtr<const SGPropertyNode> _textProperty;
111 SGSharedPtr<const SGPropertyNode> _alphaProperty;
112 SGSharedPtr<const SGPropertyNode> _styleProperty;
117 class FGSplashContentProjectionCalback : public osg::NodeCallback {
119 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
121 assert(dynamic_cast<osgUtil::CullVisitor*>(nv));
122 osgUtil::CullVisitor* cullVisitor = static_cast<osgUtil::CullVisitor*>(nv);
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());
130 if (viewportAspect < 1) {
132 width = 1/viewportAspect;
134 height = viewportAspect;
138 osg::RefMatrix* matrix = new osg::RefMatrix;
139 matrix->makeOrtho2D(-width, width, -height, height);
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);
147 cullVisitor->popProjectionMatrix();
151 char *genNameString()
153 std::string website = "http://www.flightgear.org";
154 std::string programName = "FlightGear";
155 char *name = new char[26];
185 static osg::Node* fgCreateSplashCamera()
187 char *namestring = genNameString();
188 fgSetString("/sim/startup/program-name", namestring);
191 simgear::PropertyList textures = fgGetNode("/sim/startup", true)->getChildren("splash-texture");
192 std::vector<SGPath> useable_textures;
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);
204 SG_LOG(SG_VIEW, SG_ALERT, "Cannot find splash screen file '" << value << "'");
209 // Seed the RNG in order to get randomness
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];
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);
224 snprintf(num_str, 4, "%d", num);
226 tpath.append( "Textures/Splash" );
227 tpath.concat( num_str );
228 tpath.concat( ".png" );
231 SGSharedPtr<SGPropertyNode> style = fgGetNode("/sim/gui/style[0]", true);
233 osg::Texture2D* splashTexture = new osg::Texture2D;
234 splashTexture->setImage(osgDB::readImageFile(tpath.c_str()));
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);
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);
255 osg::Geometry* geometry = new osg::Geometry;
256 geometry->setSupportsDisplayList(false);
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")));
272 osg::Geode* geode = new osg::Geode;
273 geode->addDrawable(geometry);
275 stateSet = geode->getOrCreateStateSet();
276 stateSet->setRenderBinDetails(1, "RenderBin");
277 camera->addChild(geode);
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);
286 geode = new osg::Geode;
287 stateSet = geode->getOrCreateStateSet();
288 stateSet->setRenderBinDetails(2, "RenderBin");
289 group->addChild(geode);
292 geometry = new osg::Geometry;
293 geometry->setSupportsDisplayList(false);
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);
319 osgText::Text* text = new osgText::Text;
320 std::string fn = style->getStringValue("fonts/splash", "");
321 text->setFont(globals->get_fontcache()->getfntpath(fn.c_str()).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);
331 osgText::Text* spinnertext = new osgText::Text;
332 spinnertext->setFont(globals->get_fontcache()->getfntpath(fn.c_str()).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);
342 text = new osgText::Text;
343 text->setFont(globals->get_fontcache()->getfntpath(fn.c_str()).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);
353 text = new osgText::Text;
354 text->setFont(globals->get_fontcache()->getfntpath(fn.c_str()).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);
363 fgSplashProgress("init");
368 // update callback for the switch node guarding that splash
369 class FGSplashGroupUpdateCallback : public osg::NodeCallback {
371 FGSplashGroupUpdateCallback() :
372 _splashAlphaNode(fgGetNode("/sim/startup/splash-alpha", true))
374 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
376 assert(dynamic_cast<osg::Group*>(node));
377 osg::Group* group = static_cast<osg::Group*>(node);
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());
388 SGSharedPtr<const SGPropertyNode> _splashAlphaNode;
391 osg::Node* fgCreateSplashNode() {
392 osg::Group* group = new osg::Group;
393 group->setName("splashGroup");
394 group->setUpdateCallback(new FGSplashGroupUpdateCallback);
398 // Initialize the splash screen
401 globals->get_renderer()->splashinit();
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("");
409 if (identifier[0] != 0)
410 spin_status += spinChars[spin_count++ % 4];
412 fgSetString("/sim/startup/splash-progress-spinner", spin_status);
415 if (identifier[0] != 0)
417 std::string id = std::string("splash/") + identifier;
418 text = globals->get_locale()->getLocalizedString(id.c_str(), "sys", "");
421 text = "<incomplete language resource>: " + id;
424 if (!strcmp(identifier,"downloading-scenery")) {
425 std::ostringstream oss;
426 unsigned int kbytesPerSec = fgGetInt("/sim/terrasync/transfer-rate-bytes-sec") / 1024;
428 if (kbytesPerSec > 0) {
429 oss << " - " << kbytesPerSec << " KBytes/sec";
431 fgSetString("/sim/startup/splash-progress-text", oss.str());
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());
442 if( fgGetString("/sim/startup/splash-progress-text") == text )
445 SG_LOG( SG_VIEW, SG_INFO, "Splash screen progress " << identifier );
446 fgSetString("/sim/startup/splash-progress-text", text);