10#include <osg/BlendFunc>
13#include <osg/Geometry>
15#include <osg/NodeCallback>
16#include <osg/NodeVisitor>
17#include <osg/StateSet>
19#include <osg/Texture2D>
20#include <osg/TextureRectangle>
23#include <osgText/Text>
24#include <osgText/String>
25#include <osgDB/ReadFile>
27#include <simgear/compiler.h>
29#include <simgear/debug/logstream.hxx>
30#include <simgear/math/sg_random.hxx>
31#include <simgear/misc/sg_path.hxx>
32#include <simgear/misc/sg_dir.hxx>
33#include <simgear/scene/util/SGReaderWriterOptions.hxx>
34#include <simgear/scene/util/OsgUtils.hxx>
35#include <simgear/props/condition.hxx>
50static const char*
LICENSE_URL_TEXT =
"Licensed under the GNU GPL. See https://www.flightgear.org for more information";
52using namespace std::string_literals;
57 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
66 _splashAlphaNode(
fgGetNode(
"/sim/startup/splash-alpha", true))
69 uint32_t splashW = 1920, splashH = 1080;
70 float aspect = (float)splashW / splashH;
71 _splashSwapchain =
new osgXR::Swapchain(splashW, splashH);
72 _splashSwapchain->setAlphaBits(8);
73 _splashSwapchain->allowRGBEncoding(osgXR::Swapchain::Encoding::ENCODING_SRGB);
74 _splashLayer =
new osgXR::CompositionLayerQuad(flightgear::VRManager::instance());
75 _splashLayer->setSubImage(_splashSwapchain);
76 _splashLayer->setSize(osg::Vec2f(aspect, 1.0f));
77 _splashLayer->setPosition(osg::Vec3f(0, 0, -2.0f));
78 _splashLayer->setAlphaMode(osgXR::CompositionLayer::BLEND_ALPHA_UNPREMULT);
81 const std::string vertex_source =
84 "layout(location = 0) in vec4 pos;\n"
85 "layout(location = 3) in vec4 multitexcoord0;\n"
87 "out vec2 texcoord;\n"
89 "uniform mat4 osg_ModelViewProjectionMatrix;\n"
93 " gl_Position = osg_ModelViewProjectionMatrix * pos;\n"
94 " texcoord = multitexcoord0.st;\n"
96 osg::Shader* vertex_shader =
new osg::Shader(osg::Shader::VERTEX, vertex_source);
98 const std::string fragment_source =
101 "layout(location = 0) out vec4 fragColor;\n"
103 "in vec2 texcoord;\n"
105 "uniform sampler2D tex;\n"
109 " fragColor = texture(tex, texcoord);\n"
111 osg::Shader* fragment_shader =
new osg::Shader(osg::Shader::FRAGMENT, fragment_source);
113 _program =
new osg::Program();
114 _program->addShader(vertex_shader);
115 _program->addShader(fragment_shader);
117 setName(
"splashGroup");
125void SplashScreen::createNodes()
129 bool useSRGB =
false;
135 osg::GraphicsContext* gc = guiCamera->getGraphicsContext();
136 osg::GLExtensions* glext = gc->getState()->get<osg::GLExtensions>();
138 useSRGB = osg::isGLExtensionOrVersionSupported(glext->contextID,
"GL_EXT_texture_sRGB", 2.1f) &&
139 osg::isGLExtensionOrVersionSupported(glext->contextID,
"GL_EXT_framebuffer_sRGB", 3.0f);
144 _splashFBOTexture =
new osg::Texture2D;
145 _splashFBOTexture->setInternalFormat(useSRGB ? GL_SRGB8 : GL_RGB);
147 _splashFBOTexture->setResizeNonPowerOfTwoHint(
false);
148 _splashFBOTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
149 _splashFBOTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
151 _splashFBOCamera = createFBOCamera();
152 addChild(_splashFBOCamera);
154 osg::Geometry* geometry =
new osg::Geometry;
155 geometry->setSupportsDisplayList(
false);
157 osg::Geode* geode =
new osg::Geode;
158 _splashFBOCamera->addChild(geode);
159 geode->addDrawable(geometry);
164 std::string splashImageName = selectSplashImage();
165 addImage(splashImageName,
true, 0, 0, 1, 1,
nullptr,
true);
173 bool legacySplashLogoMode =
false;
176 std::vector<SGPropertyNode_ptr> images = root->getChildren(
"image");
177 if (!images.empty()) {
178 for (
const auto& image : images) {
179 addImage(image->getStringValue(
"path",
""),
181 image->getDoubleValue(
"x", 0.025f),
182 image->getDoubleValue(
"y", 0.935f),
183 image->getDoubleValue(
"width", 0.1),
184 image->getDoubleValue(
"height", 0.1),
185 image->getNode(
"condition"),
190 auto splashLogoImage =
fgGetString(
"/sim/startup/splash-logo-image");
191 if (!splashLogoImage.empty())
193 float logoX =
fgGetDouble(
"/sim/startup/splash-logo-x-norm", 0.0);
194 float logoY = 1.0 -
fgGetDouble(
"/sim/startup/splash-logo-y-norm", 0.065);
196 float logoWidth =
fgGetDouble(
"/sim/startup/splash-logo-width", 0.6);
198 auto img = addImage(splashLogoImage,
false, logoX, logoY, logoWidth, 0,
nullptr,
false);
200 legacySplashLogoMode =
true;
209 if (!strcmp(FG_BUILD_TYPE,
"Nightly")) {
211 fgSetBool(
"sim/build-warning-active",
true);
216 fgSetString(
"/sim/startup/licence", licenseUrlText);
220 fgSetBool(
"/sim/startup/build-type-debug",
true);
222 fgSetBool(
"/sim/startup/build-type-debug",
false);
225 fgSetBool(
"/sim/startup/legacy-splash-screen", _legacySplashScreenMode);
226 fgSetBool(
"/sim/startup/legacy-splash-logo", legacySplashLogoMode);
229 for (
const auto& content : root->getChildren(
"model-content")) {
230 CreateTextFromNode(content, geode,
true);
234 for (
const auto& content : root->getChildren(
"content")) {
235 if (content->getIndex()) {
238 if (!content->getBoolValue(
"hide"))
239 CreateTextFromNode(content, geode,
false);
243 addText(geode, osg::Vec2(0.025f, 0.02f), 0.08,
"FlightGear "s +
fgGetString(
"/sim/version/flightgear"), osgText::Text::LEFT_TOP);
247 geometry =
new osg::Geometry;
248 geometry->setSupportsDisplayList(
false);
250 _splashSpinnerVertexArray =
new osg::Vec3Array;
251 for (
int i = 0;
i < 12; ++
i) {
252 _splashSpinnerVertexArray->push_back(osg::Vec3(0.0f, 0.0f, -0.1f));
254 geometry->setVertexArray(_splashSpinnerVertexArray);
257 osg::Vec4Array* colorArray =
new osg::Vec4Array;
258 colorArray->push_back(osg::Vec4(27 / 255.0f, 122 / 255.0f, 211 / 255.0f, 0.75f));
259 geometry->setColorArray(colorArray);
260 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
261 geometry->addPrimitiveSet(
new osg::DrawArrays(GL_TRIANGLES, 0, 12));
263 geode->addDrawable(geometry);
267 _splashQuadCamera =
new osg::Camera;
268 _splashQuadCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
269 _splashQuadCamera->setViewMatrix(osg::Matrix::identity());
270 _splashQuadCamera->setProjectionMatrixAsOrtho2D(0.0, 1.0, 0.0, 1.0);
271 _splashQuadCamera->setAllowEventFocus(
false);
272 _splashQuadCamera->setCullingActive(
false);
273 _splashQuadCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
275 osg::StateSet* stateSet = _splashQuadCamera->getOrCreateStateSet();
276 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
277 stateSet->setAttribute(
new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON);
278 stateSet->setRenderBinDetails(1000,
"RenderBin");
279 stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
281 bool use_vertex_attribute_aliasing =
false;
283 auto gc = guiCamera->getGraphicsContext();
284 use_vertex_attribute_aliasing = gc->getState()->getUseVertexAttributeAliasing();
286 if (use_vertex_attribute_aliasing) {
287 stateSet->setAttribute(_program);
288 stateSet->addUniform(
new osg::Uniform(
"tex", 0));
292 stateSet->setMode(GL_FRAMEBUFFER_SRGB, osg::StateAttribute::ON);
295 geometry = osg::createTexturedQuadGeometry(osg::Vec3(0.0, 0.0, 0.0),
296 osg::Vec3(1.0, 0.0, 0.0),
297 osg::Vec3(0.0, 1.0, 0.0));
298 geometry->setSupportsDisplayList(
false);
300 _splashFSQuadColor =
new osg::Vec4Array;
301 _splashFSQuadColor->push_back(osg::Vec4(1, 1.0f, 1, 1));
302 geometry->setColorArray(_splashFSQuadColor);
303 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
305 stateSet = geometry->getOrCreateStateSet();
306 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
307 stateSet->setTextureAttribute(0, _splashFBOTexture);
309 geode =
new osg::Geode;
310 geode->addDrawable(geometry);
313 _splashSwapchain->attachToMirror(stateSet);
315 _splashQuadCamera->addChild(geode);
316 addChild(_splashQuadCamera);
331void SplashScreen::CreateTextFromNode(
const SGPropertyNode_ptr& content, osg::Geode* geode,
bool modelContent)
333 auto text = content->getStringValue(
"text",
"");
334 std::string textFromProperty = content->getStringValue(
"text-prop",
"");
335 if (!textFromProperty.empty())
338 SGPropertyNode* dynamicValueNode =
nullptr;
339 std::string dynamicProperty = content->getStringValue(
"dynamic-text");
340 if (!dynamicProperty.empty())
341 dynamicValueNode =
fgGetNode(dynamicProperty,
true);
343 auto conditionNode = content->getChild(
"condition");
344 SGCondition* condition =
nullptr;
346 if (conditionNode !=
nullptr) {
347 condition = sgReadCondition(
fgGetNode(
"/"), conditionNode);
349 auto x = content->getDoubleValue(
"x", 0.5);
350 auto y = content->getDoubleValue(
"y", 0.5);
355 SG_LOG(SG_VIEW, SG_ALERT,
"model content cannot be above 0.2 y");
360 auto textItem = addText(geode, osg::Vec2(x, y),
361 content->getDoubleValue(
"font/size", 0.06),
363 osgutils::mapAlignment(content->getStringValue(
"font/alignment",
"left-top")),
365 content->getDoubleValue(
"max-width", -1.0),
366 osg::Vec4(content->getDoubleValue(
"color/r", 1), content->getDoubleValue(
"color/g", 1), content->getDoubleValue(
"color/b", 1), content->getDoubleValue(
"color/a", 1)),
367 content->getStringValue(
"font/face",
"Fonts/LiberationFonts/LiberationSans-BoldItalic.ttf"));
369 textItem->condition = condition;
371 if (textItem->condition !=
nullptr && !textItem->condition->test())
372 textItem->textNode->setDrawMode(0);
375 auto maxHeight = content->getDoubleValue(
"max-height", -1.0);
376 auto maxLineCount = content->getIntValue(
"max-line-count", -1);
378 if (maxLineCount > 0)
379 textItem->maxLineCount = maxLineCount;
381 textItem->maxHeightFraction = maxHeight;
384osg::ref_ptr<osg::Camera> SplashScreen::createFBOCamera()
386 osg::ref_ptr<osg::Camera> c =
new osg::Camera;
387 c->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
388 c->setViewMatrix(osg::Matrix::identity());
389 c->setClearMask( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
390 c->setClearColor( osg::Vec4( 0., 0., 0., 0. ) );
391 c->setAllowEventFocus(
false);
392 c->setCullingActive(
false);
393 c->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT );
394 c->setRenderOrder(osg::Camera::PRE_RENDER);
395 c->attach(osg::Camera::COLOR_BUFFER, _splashFBOTexture);
397 _splashSwapchain->attachToCamera(c);
400 osg::StateSet* stateSet = c->getOrCreateStateSet();
401 stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
402 stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
404 bool use_vertex_attribute_aliasing =
false;
407 auto gc = guiCamera->getGraphicsContext();
408 use_vertex_attribute_aliasing = gc->getState()->getUseVertexAttributeAliasing();
410 if (use_vertex_attribute_aliasing) {
411 stateSet->setAttribute(_program);
412 stateSet->addUniform(
new osg::Uniform(
"tex", 0));
423const SplashScreen::ImageItem *SplashScreen::addImage(
const std::string &path,
bool isAbsolutePath,
double x,
double y,
double width,
double height, SGPropertyNode*
424 conditionNode,
bool isBackground)
436 if (!imagePath.exists() || !imagePath.isFile()) {
437 SG_LOG(SG_VIEW, SG_INFO,
"Splash Image " << path <<
" not be found");
445 item.height = height;
447 item.isBackground = isBackground;
449 if (conditionNode !=
nullptr)
450 item.condition = sgReadCondition(
fgGetNode(
"/"), conditionNode);
452 item.condition =
nullptr;
454 osg::ref_ptr<simgear::SGReaderWriterOptions> staticOptions = simgear::SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions());
455 staticOptions->setLoadOriginHint(simgear::SGReaderWriterOptions::LoadOriginHint::ORIGIN_SPLASH_SCREEN);
457 item.Image = osgDB::readRefImageFile(imagePath.utf8Str(), staticOptions);
459 SG_LOG(SG_VIEW, SG_INFO,
"Splash Image " << imagePath <<
" failed to load");
463 item.imageWidth = item.Image->s();
464 item.imageHeight = item.Image->t();
465 item.aspectRatio =
static_cast<double>(item.imageWidth) / item.imageHeight;
466 if (item.height == 0 && item.imageWidth != 0)
467 item.height = item.imageHeight * (item.width / item.imageWidth);
469 osg::Texture2D* imageTexture =
new osg::Texture2D(item.Image);
470 imageTexture->setResizeNonPowerOfTwoHint(
false);
471 imageTexture->setInternalFormat(GL_RGBA);
472 imageTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
473 imageTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
475 osg::Geometry* geometry =
new osg::Geometry;
476 geometry->setSupportsDisplayList(
false);
478 item.vertexArray =
new osg::Vec3Array;
479 for (
int i=0;
i < 4; ++
i) {
480 item.vertexArray->push_back(osg::Vec3(0.0, 0.0, 0.0));
482 geometry->setVertexArray(item.vertexArray);
484 osg::Vec2Array* imageTextureCoordinates =
new osg::Vec2Array;
485 imageTextureCoordinates->push_back(osg::Vec2(0, 0));
486 imageTextureCoordinates->push_back(osg::Vec2(1.0, 0));
487 imageTextureCoordinates->push_back(osg::Vec2(1.0, 1.0));
488 imageTextureCoordinates->push_back(osg::Vec2(0, 1.0));
489 geometry->setTexCoordArray(0, imageTextureCoordinates);
491 osg::Vec4Array* imageColorArray =
new osg::Vec4Array;
492 imageColorArray->push_back(osg::Vec4(1, 1, 1, 1));
493 geometry->setColorArray(imageColorArray);
494 geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
495 geometry->addPrimitiveSet(
new osg::DrawArrays(GL_TRIANGLE_FAN, 0, 4));
497 osg::StateSet* stateSet = geometry->getOrCreateStateSet();
498 stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
499 stateSet->setTextureAttribute(0, imageTexture);
500 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
502 osg::Geode* geode =
new osg::Geode;
503 _splashFBOCamera->addChild(geode);
504 geode->addDrawable(geometry);
507 item.nodeMask = geode->getNodeMask();
509 if (item.condition !=
nullptr && !item.condition->test())
510 item.geode->setNodeMask(0);
512 _imageItems.push_back(item);
513 return &_imageItems.back();
516SplashScreen::TextItem *SplashScreen::addText(osg::Geode* geode ,
517 const osg::Vec2& pos,
double size,
const std::string& text,
518 const osgText::Text::AlignmentType alignment,
519 SGPropertyNode* dynamicValue,
520 double maxWidthFraction,
521 const osg::Vec4& textColor,
522 const std::string &fontFace )
527 osg::ref_ptr<osgText::Text> t =
new osgText::Text;
529 t->setFont(path.utf8Str());
530 t->setColor(textColor);
531 t->setFontResolution(64, 64);
532 t->setText(text, osgText::String::Encoding::ENCODING_UTF8);
533 t->setBackdropType(osgText::Text::OUTLINE);
534 t->setBackdropColor(osg::Vec4(0.2, 0.2, 0.2, 1));
535 t->setBackdropOffset(0.04);
537 item.fractionalCharSize = size;
538 item.fractionalPosition = pos;
539 item.dynamicContent = dynamicValue;
540 item.textNode->setAlignment(alignment);
541 item.maxWidthFraction = maxWidthFraction;
542 item.condition =
nullptr;
543 item.drawMode = t->getDrawMode();
544 geode->addDrawable(item.textNode);
546 _items.push_back(item);
547 return &_items.back();
550void SplashScreen::TextItem::reposition(
int width,
int height)
const
552 const int halfWidth = width >> 1;
553 const int halfHeight = height >> 1;
554 osg::Vec3 pixelPos(fractionalPosition.x() * width - halfWidth,
555 (1.0 - fractionalPosition.y()) * height - halfHeight,
557 textNode->setPosition(pixelPos);
558 textNode->setCharacterSize(fractionalCharSize * height);
560 if (maxWidthFraction > 0.0) {
561 textNode->setMaximumWidth(maxWidthFraction * width);
564 recomputeSize(height);
567void SplashScreen::TextItem::recomputeSize(
int height)
const
569 if ((maxLineCount == 0) && (maxHeightFraction < 0.0)) {
573 double heightFraction = maxHeightFraction;
574 if (heightFraction < 0.0) {
575 heightFraction = 9999.0;
578 double baseSize = fractionalCharSize;
580 while ((textNode->getLineCount() > maxLineCount) ||
581 (baseSize * textNode->getLineCount() > heightFraction)) {
583 textNode->setCharacterSize(baseSize * height);
588std::string SplashScreen::selectSplashImage()
592 simgear::PropertyList previewNodes =
fgGetNode(
"/sim/previews",
true)->getChildren(
"preview");
593 std::vector<SGPath> paths;
595 for (
auto n : previewNodes) {
596 if (!n->getBoolValue(
"splash",
false)) {
601 if (tpath.exists()) {
602 paths.push_back(tpath);
608 simgear::PropertyList nodes =
fgGetNode(
"/sim/startup",
true)->getChildren(
"splash-texture");
609 for (
auto n : nodes) {
611 if (tpath.exists()) {
612 paths.push_back(tpath);
613 _legacySplashScreenMode =
true;
618 if (!paths.empty()) {
620 const int index = (int)(sg_random() * paths.size());
621 return paths.at(index).utf8Str();
626 paths = simgear::Dir(tpath).children(simgear::Dir::TYPE_FILE);
627 paths.erase(std::remove_if(paths.begin(), paths.end(), [](
const SGPath&
p) {
628 const auto f = p.file();
629 if (f.find(
"Splash") != 0) return true;
630 const auto ext = p.extension();
631 return ext !=
"png" && ext !=
"jpg";
634 if (!paths.empty()) {
636 const int index = (int)(sg_random() * paths.size());
637 return paths.at(index).utf8Str();
640 SG_LOG(SG_GUI, SG_ALERT,
"Couldn't find any splash screens at all");
644void SplashScreen::doUpdate()
651 double alpha = _splashAlphaNode->getDoubleValue();
653 if (alpha <= 0 || !
fgGetBool(
"/sim/startup/splash-screen")) {
654 removeChild(0, getNumChildren());
655 _splashFBOCamera =
nullptr;
656 _splashQuadCamera =
nullptr;
658 _splashLayer->setVisible(
false);
660 }
else if (getNumChildren() == 0) {
662 _splashStartTime.stamp();
666 _splashLayer->setVisible(
true);
667 _splashSwapchain->setForcedAlpha(alpha);
670 (*_splashFSQuadColor)[0] = osg::Vec4(1.0, 1.0, 1.0, _splashAlphaNode->getFloatValue());
671 _splashFSQuadColor->dirty();
673 for (
const TextItem& item : _items) {
674 if (item.condition !=
nullptr) {
676 if (item.condition->test())
677 item.textNode->setDrawMode(item.drawMode);
679 item.textNode->setDrawMode(0);
681 if (item.dynamicContent) {
682 item.textNode->setText(
683 item.dynamicContent->getStringValue(),
684 osgText::String::Encoding::ENCODING_UTF8);
687 for (
const ImageItem& image : _imageItems) {
688 if (image.condition) {
689 if (!image.condition->test())
690 image.geode->setNodeMask(0);
692 image.geode->setNodeMask(image.nodeMask);
695 updateSplashSpinner();
698 _splashSwapchain->setForcedAlpha(alpha);
705 return halfWidth * ((v * 2.0) - 1.0);
708void SplashScreen::updateSplashSpinner()
710 const int elapsedMsec = _splashStartTime.elapsedMSec();
711 float splashSpinnerPos = (elapsedMsec % 2000) / 2000.0f;
712 float endPos = splashSpinnerPos + 0.25f;
713 float wrapStartPos = 0.0f;
714 float wrapEndPos = 0.0f;
716 wrapEndPos = endPos - 1.0f;
719 const float halfWidth = _width * 0.5f;
720 const float halfHeight = _height * 0.5f;
721 const float bottomY = -halfHeight;
722 const float topY = bottomY + 8;
723 const float z = -0.05f;
730 osg::Vec3 positions[12] = {
731 osg::Vec3(splashSpinnerPos, bottomY, z),
732 osg::Vec3(endPos, bottomY, z),
733 osg::Vec3(endPos, topY, z),
735 osg::Vec3(splashSpinnerPos, bottomY, z),
736 osg::Vec3(endPos, topY, z),
737 osg::Vec3(splashSpinnerPos, topY, z),
739 osg::Vec3(wrapStartPos, bottomY, z),
740 osg::Vec3(wrapEndPos, bottomY, z),
741 osg::Vec3(wrapEndPos, topY, z),
743 osg::Vec3(wrapStartPos, bottomY, z),
744 osg::Vec3(wrapEndPos, topY, z),
745 osg::Vec3(wrapStartPos, topY, z)
748 for (
int i = 0;
i < 12; ++
i) {
749 (*_splashSpinnerVertexArray)[
i] = positions[
i];
752 _splashSpinnerVertexArray->dirty();
755void SplashScreen::updateTipText()
760 if (!_haveSetStartupTip && (_splashStartTime.elapsedMSec() > 5000)) {
761 _haveSetStartupTip =
true;
768 int tipIndex =
globals->
get_props()->getIntValue(
"/sim/session",0) % tipCount;
777 if (getNumChildren() == 0) {
784 _splashQuadCamera->setViewport(0, 0, width, height);
785 _splashFBOCamera->resizeAttachments(width, height);
786 _splashFBOCamera->setViewport(0, 0, width, height);
787 _splashFBOCamera->setProjectionMatrixAsOrtho2D(-width * 0.5, width * 0.5,
788 -height * 0.5, height * 0.5);
790 float aspect = (float)width / height;
791 _splashSwapchain->setSize(width, height);
792 _splashLayer->setSize(osg::Vec2f(aspect, 1.0f));
795 const double screenAspectRatio =
static_cast<double>(width) / height;
798 for (
const auto& _imageItem : _imageItems) {
800 if (_imageItem.isBackground) {
804 double halfWidth = width * 0.5;
805 double halfHeight = height * 0.5;
809 if (_legacySplashScreenMode) {
810 halfWidth = width * 0.35;
811 halfHeight = height * 0.35;
813 if (screenAspectRatio > _imageItem.aspectRatio) {
815 halfWidth = halfHeight;
819 halfHeight = halfWidth;
824 if (screenAspectRatio > _imageItem.aspectRatio) {
826 halfHeight = halfWidth / _imageItem.aspectRatio;
830 halfWidth = halfHeight * _imageItem.aspectRatio;
833 (*_imageItem.vertexArray)[0] = osg::Vec3(-halfWidth, -halfHeight, 0.0);
834 (*_imageItem.vertexArray)[1] = osg::Vec3(halfWidth, -halfHeight, 0.0);
835 (*_imageItem.vertexArray)[2] = osg::Vec3(halfWidth, halfHeight, 0.0);
836 (*_imageItem.vertexArray)[3] = osg::Vec3(-halfWidth, halfHeight, 0.0);
840 float imageWidth = _imageItem.width * width;
841 float imageHeight = _imageItem.imageHeight * (imageWidth / _imageItem.imageWidth);
843 float imageX = _imageItem.x * (width - imageWidth);
844 float imageY = (1.0 - _imageItem.y) * (height - imageHeight);
846 float originX = imageX - (width * 0.5);
847 float originY = imageY - (height * 0.5);
849 (*_imageItem.vertexArray)[0] = osg::Vec3(originX, originY, 0.0);
850 (*_imageItem.vertexArray)[1] = osg::Vec3(originX + imageWidth, originY, 0.0);
851 (*_imageItem.vertexArray)[2] = osg::Vec3(originX + imageWidth, originY + imageHeight, 0.0);
852 (*_imageItem.vertexArray)[3] = osg::Vec3(originX, originY + imageHeight, 0.0);
854 _imageItem.vertexArray->dirty();
858 for (
const TextItem& item : _items) {
859 item.reposition(width, height);
865 fgSetString(
"/sim/startup/splash-progress-spinner",
"");
868 if (identifier[0] != 0)
870 text =
globals->get_locale()->getLocalizedString(identifier,
"sys");
873 text =
"<incomplete language resource>: "s + identifier;
876 if (!strcmp(identifier,
"downloading-scenery")) {
879 std::string kbytesUnitText =
globals->get_locale()->getLocalizedString(
"units-kbytes",
"sys",
"KB");
880 std::string mbytesUnitText =
globals->get_locale()->getLocalizedString(
"units-mbytes",
"sys",
"MB");
881 std::string kbytesPerSecUnitText =
globals->get_locale()->getLocalizedString(
"units-kbytes-per-sec",
"sys",
"KB/s");
882 std::string mbytesPerSecUnitText =
globals->get_locale()->getLocalizedString(
"units-mbytes-per-sec",
"sys",
"MB/s");
884 std::ostringstream oss;
885 unsigned int kbytesPerSec =
fgGetInt(
"/sim/terrasync/transfer-rate-bytes-sec") / 1024;
886 unsigned int kbytesPending =
fgGetInt(
"/sim/terrasync/pending-kbytes");
887 unsigned int kbytesPendingExtract =
fgGetInt(
"/sim/terrasync/extract-pending-kbytes");
888 if (kbytesPending > 0) {
889 if (kbytesPending > 1024) {
890 int mBytesPending = kbytesPending >> 10;
891 oss <<
" " << mBytesPending <<
" "s << mbytesUnitText;
893 oss <<
" " << kbytesPending <<
" "s << kbytesUnitText;
897 if (kbytesPerSec > 100) {
898 double mbytesPerSec = kbytesPerSec / 1024.0;
899 oss <<
" - " << std::fixed << std::setprecision(1) << mbytesPerSec <<
" "s << mbytesPerSecUnitText;
900 }
else if (kbytesPerSec > 0) {
901 oss <<
" - " << kbytesPerSec <<
" "s << kbytesPerSecUnitText;
902 }
else if (kbytesPendingExtract > 0) {
903 const std::string extractText =
globals->get_locale()->getLocalizedString(
"scenery-extract",
"sys");
904 std::ostringstream os2;
906 if (kbytesPendingExtract > 1024) {
907 int mBytesPendingExtract = kbytesPendingExtract >> 10;
908 os2 << mBytesPendingExtract <<
" "s << mbytesUnitText;
910 os2 << kbytesPendingExtract <<
" "s << kbytesUnitText;
912 auto finalText = simgear::strutils::replace(extractText,
"[VALUE]", os2.str());
913 oss <<
" - " << finalText;
915 fgSetString(
"/sim/startup/splash-progress-spinner", oss.str());
918 if (!strcmp(identifier,
"loading-scenery")) {
921 std::string kbytesUnitText =
globals->get_locale()->getLocalizedString(
"units-kbytes",
"sys",
"KB");
922 std::string mbytesUnitText =
globals->get_locale()->getLocalizedString(
"units-mbytes",
"sys",
"MB");
924 unsigned int kbytesPendingExtract =
fgGetInt(
"/sim/terrasync/extract-pending-kbytes");
925 if (kbytesPendingExtract > 0) {
926 const std::string extractText =
globals->get_locale()->getLocalizedString(
"scenery-extract",
"sys");
927 std::ostringstream oss;
928 if (kbytesPendingExtract > 1024) {
929 int mBytesPendingExtract = kbytesPendingExtract >> 10;
930 oss << mBytesPendingExtract <<
" "s << mbytesUnitText;
932 oss << kbytesPendingExtract <<
" "s << kbytesUnitText;
935 auto finalText = simgear::strutils::replace(extractText,
"[VALUE]", oss.str());
936 fgSetString(
"/sim/startup/splash-progress-spinner", finalText);
938 fgSetString(
"/sim/startup/splash-progress-spinner",
"");
943 if (!strncmp(identifier,
"navdata-", 8)) {
944 const std::string percentText =
globals->get_locale()->getLocalizedString(
"navdata-load-percent",
"sys");
945 auto finalText = simgear::strutils::replace(percentText,
"[VALUE]", std::to_string(percent));
946 fgSetString(
"/sim/startup/splash-progress-spinner", finalText);
949 if(
fgGetString(
"/sim/startup/splash-progress-text") == text )
952 SG_LOG( SG_VIEW, SG_INFO,
"Splash screen progress " << identifier );
953 fgSetString(
"/sim/startup/splash-progress-text", text);
SGPath resolve_maybe_aircraft_path(const std::string &branch) const
Same as above, but test for non 'Aircraft/' branch paths, and always resolve them against fg_root.
virtual FGRenderer * get_renderer() const
SGPropertyNode * get_props()
const SGPath & get_fg_root() const
std::string getLocalizedStringWithIndex(const std::string &id, const std::string &context, int index) const
Obtain a single translation with the given identifier, context and index.
std::size_t getLocalizedStringCount(const std::string &id, const std::string &context) const
Return the number of strings with a given id in the specified context.
std::string getLocalizedString(const std::string &id, const std::string &resource, const std::string &defaultValue={})
Obtain a single string matching the given id, with fallback.
bool runInitOperation()
Run a graphics operation that retrieves some OpenGL parameters.
virtual void operator()(osg::Node *node, osg::NodeVisitor *nv)
void resize(int width, int height)
friend class SplashScreenUpdateCallback
static CameraGroup * getDefault()
Get the default CameraGroup.
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
FlightGear Localization Support.
std::string getAircraftAuthorsText()
getAircraftAuthorsText - get the aircraft authors as a single string value.
osg::Camera * getGUICamera(CameraGroup *cgroup)
Get the osg::Camera that draws the GUI, if any, from a camera group.
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
float scaleAndOffset(float v, float halfWidth)
static const char * LICENSE_URL_TEXT
void fgSplashProgress(const char *identifier, unsigned int percent=0)
Set progress information.