FlightGear next
renderer.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: renderer.cxx
3 * SPDX-FileComment: Written by Curtis Olson, started May 1997.
4 * SPDX-FileCopyrightText: Copyright (C) 1997 - 2002 Curtis L. Olson - http://www.flightgear.org/~curt
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <config.h>
9#include <simgear/compiler.h>
10
11#include <algorithm>
12#include <iostream>
13#include <map>
14#include <vector>
15#include <typeinfo>
16
17#include <osg/Camera>
18#include <osg/CullFace>
19#include <osg/CullStack>
20#include <osg/GraphicsContext>
21#include <osg/Group>
22#include <osg/Hint>
23#include <osg/Math>
24#include <osg/NodeCallback>
25#include <osg/Notify>
26#include <osg/PolygonMode>
27#include <osg/Program>
28#include <osgUtil/LineSegmentIntersector>
29#include <osgDB/WriteFile>
30
31#include <simgear/ephemeris/ephemeris.hxx>
32#include <simgear/scene/material/EffectCullVisitor.hxx>
33#include <simgear/scene/sky/sky.hxx>
34#include <simgear/scene/tgdb/GroundLightManager.hxx>
35#include <simgear/scene/tgdb/pt_lights.hxx>
36#include <simgear/scene/tgdb/userdata.hxx>
37#include <simgear/scene/util/SGUpdateVisitor.hxx>
38#include <simgear/scene/util/RenderConstants.hxx>
39#include <simgear/scene/util/SGSceneUserData.hxx>
40#include <simgear/scene/util/OsgUtils.hxx>
41#include <simgear/timing/sg_time.hxx>
42
44#include <Model/modelmgr.hxx>
45#include <Model/acmodel.hxx>
46#include <Scenery/scenery.hxx>
47#include <GUI/new_gui.hxx>
48#include <GUI/gui.h>
49#include <GUI/Highlight.hxx>
50#include <Time/light.hxx>
51
55
56#include "CameraGroup.hxx"
57#include "FGEventHandler.hxx"
58#include "splash.hxx"
59#include "view.hxx"
60#include "viewmgr.hxx"
62
63#include "renderer.hxx"
64
65#if defined(ENABLE_QQ_UI)
67#endif
68
69using namespace flightgear;
70
71// Operation for querying OpenGL parameters. This must be done in a
72// valid OpenGL context, potentially in another thread.
74public:
75 QueryGLParametersOperation() : GraphicsContextOperation(std::string("Query OpenGL Parameters"))
76 {
77 }
78
79 void run(osg::GraphicsContext* gc)
80 {
81 OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
82
83 SGPropertyNode* p_rendering = fgGetNode("/sim/rendering/gl-info", true);
84 auto query_gl_string =
85 [p_rendering](const std::string& prop_name, GLenum name) {
86 const char* value = (const char*)glGetString(name);
87 // glGetString may return a null string
88 std::string str(value ? value : "");
89 p_rendering->setStringValue(prop_name, str);
90 SG_LOG(SG_GL, SG_INFO, " " << prop_name << ": " << str);
91 };
92
93 auto query_gl_int =
94 [p_rendering](const std::string& prop_name, GLenum name) {
95 GLint value = 0;
96 glGetIntegerv(name, &value);
97 p_rendering->setIntValue(prop_name, value);
98 SG_LOG(SG_GL, SG_INFO, " " << prop_name << ": " << value);
99 };
100
101 SG_LOG(SG_GL, SG_INFO, "OpenGL context info:");
102 query_gl_string("gl-vendor", GL_VENDOR);
103 query_gl_string("gl-renderer", GL_RENDERER);
104 query_gl_string("gl-version", GL_VERSION);
105 query_gl_string("gl-shading-language-version", GL_SHADING_LANGUAGE_VERSION);
106 query_gl_int("gl-max-texture-size", GL_MAX_TEXTURE_SIZE);
107 query_gl_int("gl-max-texture-units", GL_MAX_TEXTURE_UNITS);
108 }
109
110private:
111 OpenThreads::Mutex _mutex;
112};
113
114class PointSpriteListener : public SGPropertyChangeListener {
115public:
116 virtual void valueChanged(SGPropertyNode* node) {
117 static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
118 sceneFeatures->setEnablePointSpriteLights(node->getIntValue());
119 }
120};
121
122class DistanceAttenuationListener : public SGPropertyChangeListener {
123public:
124 virtual void valueChanged(SGPropertyNode* node) {
125 static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
126 sceneFeatures->setEnableDistanceAttenuationLights(node->getIntValue());
127 }
128};
129
130class DirectionalLightsListener : public SGPropertyChangeListener {
131public:
132 virtual void valueChanged(SGPropertyNode* node) {
133 static SGSceneFeatures* sceneFeatures = SGSceneFeatures::instance();
134 sceneFeatures->setEnableTriangleDirectionalLights(node->getIntValue());
135 }
136};
137
138class LODScaleListener : public SGPropertyChangeListener {
139public:
140 void valueChanged(SGPropertyNode* node) override {
141 auto* cg = CameraGroup::getDefault();
142 if (cg) {
143 cg->setLODScale(node->getFloatValue());
144 }
145 }
146};
147
148class FGHintUpdateCallback : public osg::StateAttribute::Callback {
149public:
150 FGHintUpdateCallback(const char* configNode) :
151 mConfigNode(fgGetNode(configNode, true))
152 {
153 }
154 void operator()(osg::StateAttribute* stateAttribute, osg::NodeVisitor*) override
155 {
156 assert(dynamic_cast<osg::Hint*>(stateAttribute));
157 osg::Hint* hint = static_cast<osg::Hint*>(stateAttribute);
158 std::string value = mConfigNode->getStringValue();
159 if (value.empty())
160 hint->setMode(GL_DONT_CARE);
161 else if (value == "nicest")
162 hint->setMode(GL_NICEST);
163 else if (value == "fastest")
164 hint->setMode(GL_FASTEST);
165 else
166 hint->setMode(GL_DONT_CARE);
167 }
168private:
169 SGPropertyNode_ptr mConfigNode;
170};
171
172class FGWireFrameModeUpdateCallback : public osg::StateAttribute::Callback {
173public:
175 mWireframe(fgGetNode("/sim/rendering/wireframe", true))
176 {
177 }
178 void operator()(osg::StateAttribute* stateAttribute, osg::NodeVisitor*) override
179 {
180 assert(dynamic_cast<osg::PolygonMode*>(stateAttribute));
181 osg::PolygonMode* polygonMode = static_cast<osg::PolygonMode*>(stateAttribute);
182 if (mWireframe->getBoolValue()) {
183 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
184 osg::PolygonMode::LINE);
185 } else {
186 polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
187 osg::PolygonMode::FILL);
188 }
189 }
190private:
191 SGPropertyNode_ptr mWireframe;
192};
193
194// update callback for the switch node guarding that splash
195class FGScenerySwitchCallback : public osg::NodeCallback {
196public:
197 void operator()(osg::Node* node, osg::NodeVisitor* nv) override
198 {
199 assert(dynamic_cast<osg::Switch*>(node));
200 osg::Switch* sw = static_cast<osg::Switch*>(node);
201 bool enabled = scenery_enabled;
202 sw->setValue(0, enabled);
203 if (!enabled)
204 return;
205 traverse(node, nv);
206 }
207 static bool scenery_enabled;
208};
209
211
215
217{
218 SGPropertyChangeListenerVec::iterator i = _listeners.begin();
219 for (; i != _listeners.end(); ++i) {
220 delete *i;
221 }
222 // replace the viewer's scene completely
223 if (getView()) {
224 getView()->setSceneData(new osg::Group);
225 }
226 if (_sky) {
227 delete _sky;
228 _sky = nullptr;
229 }
230}
231
232void
234{
235 osg::initNotifyLevel();
236
237 osg::DisplaySettings* display_settings = osg::DisplaySettings::instance();
238 assert(display_settings);
239 // Don't let OSG do automatic shader generation
240 display_settings->setShaderHint(osg::DisplaySettings::SHADER_NONE, false);
241
242 // Create the update visitor
243 _update_visitor = new SGUpdateVisitor;
244
245 if (!_event_handler)
246 _event_handler = new FGEventHandler;
247 _event_handler->setChangeStatsCameraRenderOrder(true);
248
249 sgUserDataInit(globals->get_props());
250
251 if (_composite_viewer) {
252 // reinit.
253 } else {
254 _composite_viewer = new osgViewer::CompositeViewer;
255 std::string affinity = fgGetString("/sim/thread-cpu-affinity");
256 bool osg_affinity_flag = true;
257 if (affinity == "") {}
258 else if (affinity == "none") {
259 osg_affinity_flag = false;
260 }
261 else if (affinity == "osg") {
262 /* This is handled elsewhere. */
263 }
264 else {
265 SG_LOG(SG_VIEW, SG_ALERT, "Unrecognised value for /sim/thread-cpu-affinity: " << affinity);
266 }
267 _composite_viewer->setUseConfigureAffinity(osg_affinity_flag);
268 }
269
270 // https://stackoverflow.com/questions/15207076/openscenegraph-and-multiple-viewers
271 _composite_viewer->setReleaseContextAtEndOfFrameHint(false);
272 _composite_viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
273
274 _scenery_loaded = fgGetNode("/sim/sceneryloaded", true);
275 _position_finalized = fgGetNode("/sim/position-finalized", true);
276 _panel_hotspots = fgGetNode("/sim/panel-hotspots", true);
277
278 _sim_delta_sec = fgGetNode("/sim/time/delta-sec", true);
279
280 _xsize = fgGetNode("/sim/startup/xsize", true);
281 _ysize = fgGetNode("/sim/startup/ysize", true);
282 _xpos = fgGetNode("/sim/startup/xpos", true);
283 _ypos = fgGetNode("/sim/startup/ypos", true);
284 _splash_alpha = fgGetNode("/sim/startup/splash-alpha", true);
285 _splashHiddenSignal = fgGetNode("/sim/signals/splash-hidden", true);
286
287 _altitude_ft = fgGetNode("/position/altitude-ft", true);
288
289 _cloud_status = fgGetNode("/environment/clouds/status", true);
290 _visibility_m = fgGetNode("/environment/visibility-m", true);
291
292 // configure the lighting related parameters and add change listeners.
293 bool use_point_sprites = fgGetBool("/sim/rendering/point-sprites", true);
294 bool distance_attenuation = fgGetBool("/sim/rendering/distance-attenuation", false);
295 bool triangles = fgGetBool("/sim/rendering/triangle-directional-lights", true);
296 SGConfigureDirectionalLights(use_point_sprites, distance_attenuation, triangles);
297
298 addChangeListener(new PointSpriteListener, "/sim/rendering/point-sprites");
299 addChangeListener(new DistanceAttenuationListener, "/sim/rendering/distance-attenuation");
300 addChangeListener(new DirectionalLightsListener, "/sim/rendering/triangle-directional-lights");
301 addChangeListener(new LODScaleListener, "/sim/rendering/lod-scale");
302
303 // Setup texture compression
304 std::string tc = fgGetString("/sim/rendering/texture-compression");
305 if (!tc.empty()) {
306 if (tc == "false" || tc == "off" ||
307 tc == "0" || tc == "no" ||
308 tc == "none"
309 ) {
310 SGSceneFeatures::instance()->setTextureCompression(SGSceneFeatures::DoNotUseCompression);
311 } else if (tc == "arb") {
312 SGSceneFeatures::instance()->setTextureCompression(SGSceneFeatures::UseARBCompression);
313 } else if (tc == "dxt1") {
314 SGSceneFeatures::instance()->setTextureCompression(SGSceneFeatures::UseDXT1Compression);
315 } else if (tc == "dxt3") {
316 SGSceneFeatures::instance()->setTextureCompression(SGSceneFeatures::UseDXT3Compression);
317 } else if (tc == "dxt5") {
318 SGSceneFeatures::instance()->setTextureCompression(SGSceneFeatures::UseDXT5Compression);
319 } else {
320 SG_LOG(SG_VIEW, SG_WARN, "Unknown texture compression setting!");
321 }
322 }
323 SGSceneFeatures::instance()->setTextureCompressionPath(globals->get_texture_cache_dir());
324
325 // create sky, but can't build until setupView, since we depend
326 // on other subsystems to be inited, eg Ephemeris
327 _sky = new SGSky;
328
329 const SGPath texture_path = globals->get_fg_root() / "Textures" / "Sky";
330 for (int i = 0; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) {
331 SGCloudLayer * layer = new SGCloudLayer(texture_path);
332 _sky->add_cloud_layer(layer);
333 }
334}
335
336void
338{
339 // important that we reset the viewer sceneData here, to ensure the reference
340 // time for everything is in sync; otherwise on reset the Viewer and
341 // GraphicsWindow clocks are out of sync.
342 osgViewer::View* view = getView();
343 _scene_root = new osg::Group;
344 _scene_root->setName("viewerSceneRoot");
345 view->setSceneData(_scene_root);
346 view->setDatabasePager(FGScenery::getPagerSingleton());
347
348 // Scene doesn't seem to pass the frame stamp to the update
349 // visitor automatically.
350 _update_visitor->setFrameStamp(getFrameStamp());
351 getViewerBase()->setUpdateVisitor(_update_visitor.get());
352
353 fgSetDouble("/sim/startup/splash-alpha", 1.0);
354 // hide the menubar if it overlaps the window, so the splash screen
355 // is completely visible. We reset this value when the splash screen
356 // is fading out.
357 fgSetBool("/sim/menubar/overlap-hide", true);
358}
359
360void
362{
363 // Do not automatically compute near far values
364 getView()->getCamera()->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
365
366 // Attach empty program to the scene root so that shader programs
367 // don't leak into state sets (effects) that shouldn't have one.
368 _scene_root->getOrCreateStateSet()->setAttributeAndModes(
369 new osg::Program, osg::StateAttribute::ON);
370
371 // Specify implementation-specific hints
372 // osg::Hint* hint = new osg::Hint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_DONT_CARE);
373 // hint->setUpdateCallback(new FGHintUpdateCallback("/sim/rendering/hints/fragment-shader-derivative"));
374 // stateSet->setAttribute(hint);
375 // hint = new osg::Hint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
376 // hint->setUpdateCallback(new FGHintUpdateCallback("/sim/rendering/hints/line-smooth"));
377 // stateSet->setAttribute(hint);
378 // hint = new osg::Hint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
379 // hint->setUpdateCallback(new FGHintUpdateCallback("/sim/rendering/hints/polygon-smooth"));
380 // stateSet->setAttribute(hint);
381 // hint = new osg::Hint(GL_TEXTURE_COMPRESSION_HINT, GL_DONT_CARE);
382 // hint->setUpdateCallback(new FGHintUpdateCallback("/sim/rendering/hints/texture-compression"));
383 // stateSet->setAttribute(hint);
384
385 // Build the sky
386 // The sun and moon radius are scaled down numbers of the actual
387 // diameters. This is needed to fit both the sun and the moon
388 // within the distance to the far clip plane.
389 //
390 // Their distance to the observer is specified in FGRendered::updateSky() and
391 // set to 40000 for the Moon semi-mayor axis, 50000 for the mean
392 // Earth-Sun distance (1AU). The location of the stars is
393 // specified below in the call to sky->build() (80000).
394 //
395 // Mean Moon radius: 1,737.4 kilometers
396 // Moon Semi-mayor axis: 384,399 km
397 // => Rendered Moon radius = 1,737.4 / 384,399 * 40000 = 232.5
398 //
399 // Photosphere Sun radius: 695,700 kilometers
400 // 1UA = 149,597,870.700 km
401 // => Rendered Sun radius = 695,700/149,597,870.700 * 50000 = 180.8
402 //
403 auto ephemerisSub = globals->get_subsystem<Ephemeris>();
404 osg::ref_ptr<simgear::SGReaderWriterOptions> opt;
405 opt = simgear::SGReaderWriterOptions::fromPath(globals->get_fg_root());
406 opt->setPropertyNode(globals->get_props());
407 _sky->build(80000.0, 80000.0,
408 232.5, 180.8,
409 *ephemerisSub->data(),
410 fgGetNode("/environment", true),
411 opt.get());
412
413 // Add the sky to the root
414 _scene_root->addChild(_sky->getPreRoot());
415 // Add the clouds as well
416 _scene_root->addChild(_sky->getCloudRoot());
417
418 // Add the main scenery (including models and aircraft) to the root with
419 // a switch to enable/disable it on demand.
420 osg::Group* scenery_group = globals->get_scenery()->get_scene_graph();
421 scenery_group->setName("Scenery group");
422 scenery_group->setNodeMask(~simgear::BACKGROUND_BIT);
423 osg::Switch* scenery_switch = new osg::Switch;
424 scenery_switch->setName("Scenery switch");
425 scenery_switch->setUpdateCallback(new FGScenerySwitchCallback);
426 scenery_switch->addChild(scenery_group);
427 _scene_root->addChild(scenery_switch);
428
429 // Switch to enable wireframe mode on the scenery group
430 osg::PolygonMode* polygonMode = new osg::PolygonMode;
431 polygonMode->setUpdateCallback(new FGWireFrameModeUpdateCallback);
432 scenery_group->getOrCreateStateSet()->setAttributeAndModes(polygonMode);
433
434 osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
435 if (guiCamera) {
436#if defined(ENABLE_QQ_UI)
437 osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(view);
438 if (viewer) {
439 std::string rootQMLPath = fgGetString("/sim/gui/qml-root-path");
440 auto graphicsWindow = dynamic_cast<osgViewer::GraphicsWindow*>(guiCamera->getGraphicsContext());
441
442 if (!rootQMLPath.empty()) {
443 _quickDrawable = new QQuickDrawable;
444 _quickDrawable->setup(graphicsWindow, viewer);
445 _quickDrawable->setSourcePath(rootQMLPath);
446
447 osg::Geode* qqGeode = new osg::Geode;
448 qqGeode->addDrawable(_quickDrawable);
449 guiCamera->addChild(qqGeode);
450 }
451 }
452#endif
453 }
454}
455
456bool
458{
459 static osg::ref_ptr<QueryGLParametersOperation> genOp;
460 static bool didInit = false;
461
462 if (didInit) {
463 return true;
464 }
465
466 if (!genOp.valid()) {
467 genOp = new QueryGLParametersOperation;
469 wsa->windows[0]->gc->add(genOp.get());
470 return false; // not ready yet
471 } else {
472 if (!genOp->isFinished())
473 return false;
474
475 genOp = nullptr;
476 didInit = true;
477 // we're done
478 return true;
479 }
480}
481
482void
484{
485 if (!_position_finalized || !_scenery_loaded->getBoolValue()) {
486 _splash_alpha->setDoubleValue(1.0);
487 if (_splashHiddenSignal->getBoolValue()) {
488 _splashHiddenSignal->setBoolValue(false);
489 }
490
491 if (!_maximum_texture_size) {
492 osg::Camera* guiCamera = getGUICamera(CameraGroup::getDefault());
493 if (guiCamera) {
494 osg::GraphicsContext *gc = guiCamera->getGraphicsContext();
495 osg::GLExtensions* gl2ext = gc->getState()->get<osg::GLExtensions>();
496 if (gl2ext) {
497 _maximum_texture_size = gl2ext->maxTextureSize;
498 SGSceneFeatures::instance()->setMaxTextureSize(_maximum_texture_size);
499 }
500 }
501 }
502 return;
503 }
504
505 if (_splash_alpha->getDoubleValue() > 0.0) {
506 // Fade out the splash screen
507 const double fade_time = 0.5;
508 const double fade_steps_per_sec = 10;
509 double delay_time = SGMiscd::min(fade_time/fade_steps_per_sec,
510 (SGTimeStamp::now() - _splash_time).toSecs());
511 _splash_time = SGTimeStamp::now();
512 double sAlpha = _splash_alpha->getDoubleValue();
513 sAlpha -= SGMiscd::max(0.0,delay_time/fade_time);
515
516 if (sAlpha <= 0.0) {
517 _splashHiddenSignal->setBoolValue(true);
518 flightgear::addSentryBreadcrumb("splash-screen fade out complete", "info");
519 }
520
521 _splash_alpha->setDoubleValue((sAlpha < 0) ? 0.0 : sAlpha);
522
524 fgSetBool("/sim/menubar/overlap-hide", false);
525 }
526
527 auto l = globals->get_subsystem<FGLight>();
528
529 // update fog params
530 double actual_visibility;
531 if (_cloud_status->getBoolValue()) {
532 actual_visibility = _sky->get_visibility();
533 } else {
534 actual_visibility = _visibility_m->getDoubleValue();
535 }
536
537 // idle_state is now 1000 meaning we've finished all our
538 // initializations and are running the main loop, so this will
539 // now work without seg faulting the system.
540
541 flightgear::View *current__view = globals->get_current_view();
542 // Force update of center dependent values ...
543 current__view->set_dirty();
544
545 // Update the sky
546 updateSky();
547
548 // need to call the update visitor once
549 getFrameStamp()->setCalendarTime(*globals->get_time_params()->getGmt());
550 _update_visitor->setViewData(current__view->getViewPosition(),
551 current__view->getViewOrientation());
552
553 SGVec3f sundirection(l->sun_vec()[0], l->sun_vec()[1], l->sun_vec()[2]);
554 SGVec3f moondirection(l->moon_vec()[0], l->moon_vec()[1], l->moon_vec()[2]);
555
556 _update_visitor->setLight(sundirection, moondirection,
557 l->get_sun_angle()*SGD_RADIANS_TO_DEGREES);
558 _update_visitor->setVisibility(actual_visibility);
559
560 osg::Node::NodeMask cullMask = ~simgear::LIGHTS_BITS & ~simgear::PICK_BIT;
561 cullMask |= simgear::GroundLightManager::instance()
562 ->getLightNodeMask(_update_visitor.get());
563 if (_panel_hotspots->getBoolValue())
564 cullMask |= simgear::PICK_BIT;
566}
567
568void
569FGRenderer::updateSky()
570{
571 // update fog params if visibility has changed
572 double visibility_meters = _visibility_m->getDoubleValue();
573 _sky->set_visibility(visibility_meters);
574
575 double altitude_m = _altitude_ft->getDoubleValue() * SG_FEET_TO_METER;
576 _sky->modify_vis( altitude_m, 0.0 /* time factor, now unused */);
577
578 auto l = globals->get_subsystem<FGLight>();
579
580 // The sun and moon distances are scaled down versions of the
581 // actual distance. See FGRenderer::setupView() for more details.
582
583 SGSkyState sstate;
584 sstate.pos = globals->get_current_view()->getViewPosition();
585 sstate.pos_geod = globals->get_current_view()->getPosition();
586 sstate.ori = globals->get_current_view()->getViewOrientation();
587 sstate.spin = l->get_sun_rotation();
588 sstate.gst = globals->get_time_params()->getGst();
589 sstate.sun_dist = 50000.0;
590 sstate.moon_dist_bare = 40000.0;
591 sstate.moon_dist_factor = 1.0;
592 sstate.sun_angle = l->get_sun_angle();
593
594 SGSkyColor scolor;
595 scolor.sun_angle = l->get_sun_angle();
596 scolor.moon_angle = l->get_moon_angle();
597 scolor.altitude_m = altitude_m;
598
599 auto ephemerisSub = globals->get_subsystem<Ephemeris>();
600 double delta_time_sec = _sim_delta_sec->getDoubleValue();
601 _sky->reposition( sstate, *ephemerisSub->data(), delta_time_sec );
602 _sky->repaint( scolor, *ephemerisSub->data() );
603}
604
605void
606FGRenderer::resize(int width, int height, int x, int y)
607{
608 SG_LOG(SG_VIEW, SG_DEBUG, "FGRenderer::resize: new size " << width << " x " << height);
609 // must guard setting these, or PLIB-PUI fails with too many live interfaces
610 if (width != _xsize->getIntValue()) _xsize->setIntValue(width);
611 if (height != _ysize->getIntValue()) _ysize->setIntValue(height);
612 if (x != _xpos->getIntValue()) _xpos->setIntValue(x);
613 if (y != _ypos->getIntValue()) _ypos->setIntValue(y);
614
615 // update splash node if present
616 _splash->resize(width, height);
617#if defined(ENABLE_QQ_UI)
618 if (_quickDrawable) {
619 _quickDrawable->resize(width, height);
620 }
621#endif
622}
623
624void
625FGRenderer::resize(int width, int height)
626{
627 resize(width, height, _xpos->getIntValue(), _ypos->getIntValue());
628}
629
630namespace {
631
632typedef osgUtil::LineSegmentIntersector::Intersection Intersection;
633
634SGVec2d uvFromIntersection(const Intersection& hit)
635{
636 // Taken from http://trac.openscenegraph.org/projects/osg/browser/OpenSceneGraph/trunk/examples/osgmovie/osgmovie.cpp
637
638 osg::Drawable* drawable = hit.drawable.get();
639 osg::Geometry* geometry = drawable ? drawable->asGeometry() : nullptr;
640 osg::Vec3Array* vertices = geometry ?
641 dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()) : nullptr;
642
643 if (!vertices) {
644 SG_LOG(SG_INPUT, SG_WARN, "Unable to get vertices for intersection.");
645 return SGVec2d(-9999,-9999);
646 }
647
648 // get the vertex indices.
649 const Intersection::IndexList& indices = hit.indexList;
650 const Intersection::RatioList& ratios = hit.ratioList;
651
652 if (indices.size() != 3 || ratios.size() != 3) {
653 SG_LOG(SG_INPUT, SG_WARN, "Intersection has insufficient indices to work with.");
654 return SGVec2d(-9999,-9999);
655 }
656
657 unsigned int i1 = indices[0];
658 unsigned int i2 = indices[1];
659 unsigned int i3 = indices[2];
660
661 float r1 = ratios[0];
662 float r2 = ratios[1];
663 float r3 = ratios[2];
664
665 osg::Array* texcoords = (geometry->getNumTexCoordArrays() > 0) ?
666 geometry->getTexCoordArray(0) : nullptr;
667 osg::Vec2Array* texcoords_Vec2Array = dynamic_cast<osg::Vec2Array*>(texcoords);
668
669 if (!texcoords_Vec2Array) {
670 SG_LOG(SG_INPUT, SG_WARN, "Unable to get texcoords for intersection.");
671 return SGVec2d(-9999,-9999);
672 }
673
674 // we have tex coord array so now we can compute the final tex coord at the
675 // point of intersection.
676 osg::Vec2 tc1 = (*texcoords_Vec2Array)[i1];
677 osg::Vec2 tc2 = (*texcoords_Vec2Array)[i2];
678 osg::Vec2 tc3 = (*texcoords_Vec2Array)[i3];
679
680 return toSG(osg::Vec2d(tc1 * r1 + tc2 * r2 + tc3 * r3));
681}
682
683} // anonymous namespace
684
685FGRenderer::PickList FGRenderer::pick(const osg::Vec2& windowPos)
686{
687 PickList result;
688 osgUtil::LineSegmentIntersector::Intersections intersections;
689
690 if (!computeIntersections(CameraGroup::getDefault(), windowPos, intersections))
691 return result; // return empty list
692
693 // We attempt to highlight nodes until Highlight::highlight_nodes()
694 // succeeds and returns +ve, or highlighting is disabled and it returns -1.
695 auto highlight = globals->get_subsystem<Highlight>();
696 int highlight_num_props = 0;
697
698 for (const auto& hit : intersections) {
699 const osg::NodePath& np = hit.nodePath;
700 osg::NodePath::const_reverse_iterator npi;
701
702 for (npi = np.rbegin(); npi != np.rend(); ++npi) {
703 if (!highlight_num_props && highlight) {
704 highlight_num_props = highlight->highlightNodes(*npi);
705 }
706 SGSceneUserData* ud = SGSceneUserData::getSceneUserData(*npi);
707 if (!ud || (ud->getNumPickCallbacks() == 0))
708 continue;
709
710 for (unsigned i = 0; i < ud->getNumPickCallbacks(); ++i) {
711 SGPickCallback* pickCallback = ud->getPickCallback(i);
712 if (!pickCallback)
713 continue;
714 SGSceneryPick sceneryPick;
715 sceneryPick.info.local = toSG(hit.getLocalIntersectPoint());
716 sceneryPick.info.wgs84 = toSG(hit.getWorldIntersectPoint());
717
718 if( pickCallback->needsUV() )
719 sceneryPick.info.uv = uvFromIntersection(hit);
720
721 sceneryPick.callback = pickCallback;
722 result.push_back(sceneryPick);
723 } // of installed pick callbacks iteration
724 } // of reverse node path walk
725 }
726
727 return result;
728}
729
730void
731FGRenderer::addCanvasCamera(osg::Camera* camera)
732{
733 assert(camera);
734
735 bool should_restart_threading = getViewerBase()->areThreadsRunning();
736 if (should_restart_threading) {
737 getViewerBase()->stopThreading();
738 }
739
740 // Use the same graphics context as the GUI camera
741 osg::Camera *guiCamera = getGUICamera(CameraGroup::getDefault());
742 osg::GraphicsContext *gc = guiCamera->getGraphicsContext();
743 camera->setGraphicsContext(gc);
744
745 // Add it as a slave to the viewer
746 _composite_viewer->getView(0)->addSlave(camera, false);
747 simgear::installEffectCullVisitor(camera);
748
749 if (should_restart_threading) {
750 getViewerBase()->startThreading();
751 }
752}
753
754void
756{
757 assert(camera);
758
759 bool should_restart_threading = getViewerBase()->areThreadsRunning();
760 if (should_restart_threading) {
761 getViewerBase()->stopThreading();
762 }
763
764 // Remove all children before removing the slave to prevent the graphics
765 // window from automatically cleaning up all associated OpenGL objects.
766 camera->removeChildren(0, camera->getNumChildren());
767
768 auto view = _composite_viewer->getView(0);
769 unsigned int index = view->findSlaveIndexForCamera(camera);
770 if (index < view->getNumSlaves()) {
771 view->removeSlave(index);
772 } else {
773 SG_LOG(SG_GL, SG_WARN, "Attempted to remove unregistered Canvas camera");
774 }
775
776 if (should_restart_threading) {
777 getViewerBase()->startThreading();
778 }
779}
780
781osgViewer::ViewerBase*
783{
784 return _composite_viewer;
785}
786
787osg::ref_ptr<osgViewer::CompositeViewer>
789{
790 return _composite_viewer;
791}
792
793void
794FGRenderer::setCompositeViewer(osg::ref_ptr<osgViewer::CompositeViewer> composite_viewer)
795{
796 _composite_viewer = composite_viewer;
797}
798
799osg::FrameStamp*
801{
802 assert(_composite_viewer);
803 return _composite_viewer->getFrameStamp();
804}
805
806osgViewer::View*
808{
809 // Would like to assert that FGRenderer::init() has always been called
810 // before we are called, but this fails if user specifies -h, when we are
811 // called by FGGlobals::~FGGlobals().
812 if (_composite_viewer && _composite_viewer->getNumViews() > 0) {
813 assert(_composite_viewer->getNumViews());
814 return _composite_viewer->getView(0);
815 }
816 return nullptr;
817}
818
819const osgViewer::View*
821{
822 FGRenderer* this_ = const_cast<FGRenderer*>(this);
823 return this_->getView();
824}
825
826void
827FGRenderer::setView(osgViewer::View* view)
828{
829 if (_composite_viewer && _composite_viewer->getNumViews() == 0) {
830 SG_LOG(SG_VIEW, SG_DEBUG, "adding view to composite_viewer.");
831 _composite_viewer->stopThreading();
832 _composite_viewer->addView(view);
833 _composite_viewer->startThreading();
834 }
835}
836
839{
840 return _event_handler.get();
841}
842
843const FGEventHandler*
845{
846 return _event_handler.get();
847}
848
849void
851{
852 _event_handler = event_handler;
853}
854
855SGSky*
857{
858 return _sky;
859}
860
863{
864 if (!_splash)
865 _splash = new SplashScreen;
866 return _splash;
867}
868
869void
870FGRenderer::addChangeListener(SGPropertyChangeListener* l, const char* path)
871{
872 _listeners.push_back(l);
873 fgAddChangeListener(l, path);
874}
875
876//------------------------------------------------------------------------------
877
878bool
879fgDumpSceneGraphToFile(const char* filename)
880{
881 osgViewer::View* view = globals->get_renderer()->getView();
882 return osgDB::writeNodeFile(*view->getSceneData(), filename);
883}
884
885bool
886fgDumpTerrainBranchToFile(const char* filename)
887{
888 return osgDB::writeNodeFile(*globals->get_scenery()->get_terrain_branch(),
889 filename);
890}
891
892bool
893fgDumpNodeToFile(osg::Node* node, const char* filename)
894{
895 return osgDB::writeNodeFile(*node, filename);
896}
897
898namespace {
899
900using namespace osg;
901
902class VisibleSceneInfoVisitor : public NodeVisitor, CullStack {
903public:
904 VisibleSceneInfoVisitor() :
905 NodeVisitor(CULL_VISITOR, TRAVERSE_ACTIVE_CHILDREN)
906 {
907 setCullingMode(CullSettings::SMALL_FEATURE_CULLING
908 | CullSettings::VIEW_FRUSTUM_CULLING);
909 setComputeNearFarMode(CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
910 }
911
912 VisibleSceneInfoVisitor(const VisibleSceneInfoVisitor& rhs) :
913 osg::Object(rhs), NodeVisitor(rhs), CullStack(rhs)
914 {
915 }
916
917 META_NodeVisitor("flightgear","VisibleSceneInfoVistor")
918
919 typedef std::map<const std::string, int> InfoMap;
920
921 void getNodeInfo(Node* node)
922 {
923 const char* typeName = typeid(*node).name();
924 classInfo[typeName]++;
925 const std::string& nodeName = node->getName();
926 if (!nodeName.empty())
927 nodeInfo[nodeName]++;
928 }
929
930 void dumpInfo()
931 {
932 using namespace std;
933 typedef vector<InfoMap::iterator> FreqVector;
934 auto freqComp = [](const InfoMap::iterator& lhs, const InfoMap::iterator& rhs) {
935 return lhs->second > rhs->second;
936 };
937 cout << "class info:\n";
938 FreqVector classes;
939 for (InfoMap::iterator itr = classInfo.begin(), end = classInfo.end();
940 itr != end;
941 ++itr)
942 classes.push_back(itr);
943 sort(classes.begin(), classes.end(), freqComp);
944 for (FreqVector::iterator itr = classes.begin(), end = classes.end();
945 itr != end;
946 ++itr) {
947 cout << (*itr)->first << " " << (*itr)->second << "\n";
948 }
949 cout << "\nnode info:\n";
950 FreqVector nodes;
951 for (InfoMap::iterator itr = nodeInfo.begin(), end = nodeInfo.end();
952 itr != end;
953 ++itr)
954 nodes.push_back(itr);
955
956 sort (nodes.begin(), nodes.end(), freqComp);
957 for (FreqVector::iterator itr = nodes.begin(), end = nodes.end();
958 itr != end;
959 ++itr) {
960 cout << (*itr)->first << " " << (*itr)->second << "\n";
961 }
962 cout << endl;
963 }
964
965 void doTraversal(Camera* camera, Node* root, Viewport* viewport)
966 {
967 ref_ptr<RefMatrix> projection
968 = createOrReuseMatrix(camera->getProjectionMatrix());
969 ref_ptr<RefMatrix> mv = createOrReuseMatrix(camera->getViewMatrix());
970 if (!viewport)
971 viewport = camera->getViewport();
972 if (viewport)
973 pushViewport(viewport);
974 pushProjectionMatrix(projection.get());
975 pushModelViewMatrix(mv.get(), Transform::ABSOLUTE_RF);
976 root->accept(*this);
977 popModelViewMatrix();
978 popProjectionMatrix();
979 if (viewport)
980 popViewport();
981 dumpInfo();
982 }
983
984 void apply(Node& node)
985 {
986 if (isCulled(node))
987 return;
988 pushCurrentMask();
989 getNodeInfo(&node);
990 traverse(node);
991 popCurrentMask();
992 }
993
994 void apply(Group& node)
995 {
996 if (isCulled(node))
997 return;
998 pushCurrentMask();
999 getNodeInfo(&node);
1000 traverse(node);
1001 popCurrentMask();
1002 }
1003
1004 void apply(Transform& node)
1005 {
1006 if (isCulled(node))
1007 return;
1008 pushCurrentMask();
1009 ref_ptr<RefMatrix> matrix = createOrReuseMatrix(*getModelViewMatrix());
1010 node.computeLocalToWorldMatrix(*matrix,this);
1011 pushModelViewMatrix(matrix.get(), node.getReferenceFrame());
1012 getNodeInfo(&node);
1013 traverse(node);
1014 popModelViewMatrix();
1015 popCurrentMask();
1016 }
1017
1018 void apply(Camera& camera)
1019 {
1020 // Save current cull settings
1021 CullSettings saved_cull_settings(*this);
1022
1023 // set cull settings from this Camera
1024 setCullSettings(camera);
1025 // inherit the settings from above
1026 inheritCullSettings(saved_cull_settings, camera.getInheritanceMask());
1027
1028 // set the cull mask.
1029 unsigned int savedTraversalMask = getTraversalMask();
1030 bool mustSetCullMask = (camera.getInheritanceMask()
1031 & osg::CullSettings::CULL_MASK) == 0;
1032 if (mustSetCullMask)
1033 setTraversalMask(camera.getCullMask());
1034
1035 osg::RefMatrix* projection = 0;
1036 osg::RefMatrix* modelview = 0;
1037
1038 if (camera.getReferenceFrame()==osg::Transform::RELATIVE_RF) {
1039 if (camera.getTransformOrder()==osg::Camera::POST_MULTIPLY) {
1040 projection = createOrReuseMatrix(*getProjectionMatrix()
1041 *camera.getProjectionMatrix());
1042 modelview = createOrReuseMatrix(*getModelViewMatrix()
1043 * camera.getViewMatrix());
1044 }
1045 else { // pre multiply
1046 projection = createOrReuseMatrix(camera.getProjectionMatrix()
1047 * (*getProjectionMatrix()));
1048 modelview = createOrReuseMatrix(camera.getViewMatrix()
1049 * (*getModelViewMatrix()));
1050 }
1051 } else {
1052 // an absolute reference frame
1053 projection = createOrReuseMatrix(camera.getProjectionMatrix());
1054 modelview = createOrReuseMatrix(camera.getViewMatrix());
1055 }
1056 if (camera.getViewport())
1057 pushViewport(camera.getViewport());
1058
1059 pushProjectionMatrix(projection);
1060 pushModelViewMatrix(modelview, camera.getReferenceFrame());
1061
1062 traverse(camera);
1063
1064 // restore the previous model view matrix.
1065 popModelViewMatrix();
1066
1067 // restore the previous model view matrix.
1068 popProjectionMatrix();
1069
1070 if (camera.getViewport()) popViewport();
1071
1072 // restore the previous traversal mask settings
1073 if (mustSetCullMask)
1074 setTraversalMask(savedTraversalMask);
1075
1076 // restore the previous cull settings
1077 setCullSettings(saved_cull_settings);
1078 }
1079
1080protected:
1081 InfoMap classInfo;
1082 InfoMap nodeInfo;
1083};
1084
1085} // anonymous namespace
1086
1088{
1089 osgViewer::View* view = renderer->getView();
1090 VisibleSceneInfoVisitor vsv;
1091 osg::Viewport* vp = 0;
1092 if (!view->getCamera()->getViewport() && view->getNumSlaves() > 0) {
1093 const osg::View::Slave& slave = view->getSlave(0);
1094 vp = slave._camera->getViewport();
1095 }
1096 vsv.doTraversal(view->getCamera(), view->getSceneData(), vp);
1097 return true;
1098}
1099
1101{
1102 osg::ref_ptr<osg::GraphicsContext::Traits> traits =
1103 new osg::GraphicsContext::Traits;
1104
1105 // 1x1 is enough for the check
1106 traits->x = 0; traits->y = 0;
1107 traits->width = 1; traits->height = 1;
1108 // RGBA8
1109 traits->red = 8; traits->green = 8; traits->blue = 8; traits->alpha = 8;
1110 // Use an off-screen pbuffer, not an actual window surface. This prevents
1111 // flashing from opening and closing a window very fast.
1112 traits->pbuffer = true;
1113
1114 traits->windowDecoration = false;
1115 traits->doubleBuffer = true;
1116 traits->sharedContext = nullptr;
1117 traits->readDISPLAY();
1118 traits->setUndefinedScreenDetailsToDefaultScreen();
1119
1120 // Our minimum is OpenGL 4.1 core
1121 traits->glContextVersion = "4.1";
1122 traits->glContextProfileMask = 0x1;
1123
1124 osg::ref_ptr<osg::GraphicsContext> pbuffer
1125 = osg::GraphicsContext::createGraphicsContext(traits.get());
1126 return pbuffer.valid();
1127}
#define i(x)
virtual void valueChanged(SGPropertyNode *node)
Definition renderer.cxx:132
virtual void valueChanged(SGPropertyNode *node)
Definition renderer.cxx:124
Wrap SGEphemeris in a subsystem/property interface.
Definition ephemeris.hxx:37
void operator()(osg::StateAttribute *stateAttribute, osg::NodeVisitor *) override
Definition renderer.cxx:154
FGHintUpdateCallback(const char *configNode)
Definition renderer.cxx:150
void removeCanvasCamera(osg::Camera *camera)
Remove a Canvas RTT camera from the renderer.
Definition renderer.cxx:755
osgViewer::View * getView()
Definition renderer.cxx:807
SplashScreen * getSplash()
Definition renderer.cxx:862
osg::ref_ptr< osgViewer::CompositeViewer > getCompositeViewer()
Both should only be used on reset.
Definition renderer.cxx:788
SGSky * getSky() const
Definition renderer.cxx:856
void setView(osgViewer::View *view)
Definition renderer.cxx:827
void setupView()
Setup the scene graph root.
Definition renderer.cxx:361
flightgear::FGEventHandler * getEventHandler()
Definition renderer.cxx:838
PickList pick(const osg::Vec2 &windowPos)
Pick into the scene and return the pick callbacks on the way.
Definition renderer.cxx:685
void init()
Initialize the renderer.
Definition renderer.cxx:233
void addCanvasCamera(osg::Camera *camera)
Add a Canvas RTT camera to the renderer.
Definition renderer.cxx:731
osgViewer::ViewerBase * getViewerBase() const
Definition renderer.cxx:782
void update()
Update rendering-related parameters.
Definition renderer.cxx:483
bool runInitOperation()
Run a graphics operation that retrieves some OpenGL parameters.
Definition renderer.cxx:457
void setEventHandler(flightgear::FGEventHandler *event_handler)
Definition renderer.cxx:850
void postinit()
Called after init() was called, the graphics window has been created and the CameraGroup has been ini...
Definition renderer.cxx:337
void setCompositeViewer(osg::ref_ptr< osgViewer::CompositeViewer > composite_viewer)
Definition renderer.cxx:794
void resize(int width, int height)
Handle a window resize event.
Definition renderer.cxx:625
std::vector< SGSceneryPick > PickList
Definition renderer.hxx:81
osg::FrameStamp * getFrameStamp() const
Definition renderer.cxx:800
static bool scenery_enabled
Definition renderer.cxx:207
void operator()(osg::Node *node, osg::NodeVisitor *nv) override
Definition renderer.cxx:197
static flightgear::SceneryPager * getPagerSingleton()
Definition scenery.cxx:560
void operator()(osg::StateAttribute *stateAttribute, osg::NodeVisitor *) override
Definition renderer.cxx:178
void valueChanged(SGPropertyNode *node) override
Definition renderer.cxx:140
virtual void valueChanged(SGPropertyNode *node)
Definition renderer.cxx:116
void setup(osgViewer::GraphicsWindow *gw, osgViewer::Viewer *viewer)
void run(osg::GraphicsContext *gc)
The body of the operation.
Definition renderer.cxx:79
static CameraGroup * getDefault()
Get the default CameraGroup.
void setCameraCullMasks(osg::Node::NodeMask nm)
Set the cull mask on all non-GUI cameras.
void setChangeStatsCameraRenderOrder(bool c)
GraphicsContextOperation(const std::string &name)
const SGQuatd & getViewOrientation()
Definition view.hxx:178
void set_dirty()
Definition view.hxx:196
const SGVec3d & getViewPosition()
Definition view.hxx:177
Adapter from windows system / graphics context management API to functions used by flightgear.
WindowVector windows
Vector of all the registered windows.
static WindowSystemAdapter * getWSA()
Get the global WindowSystemAdapter.
void fgAddChangeListener(SGPropertyChangeListener *listener, const char *path)
Add a listener to a node.
Definition fg_props.cxx:513
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
Definition fg_props.cxx:556
FGGlobals * globals
Definition globals.cxx:142
void syncPausePopupState()
synchronize /sim/freeze properties with visiblity of the popup-dialog which informs the user
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
const char * name
void addSentryBreadcrumb(const std::string &, const std::string &)
osg::Camera * getGUICamera(CameraGroup *cgroup)
Get the osg::Camera that draws the GUI, if any, from a camera group.
bool computeIntersections(const CameraGroup *cgroup, const osg::Vec2d &windowPos, osgUtil::LineSegmentIntersector::Intersections &intersections)
Choose a camera using an event and do intersection testing on its view of the scene.
Definition AIBase.hxx:25
Precipitation manager This manager calculate the intensity of precipitation in function of the altitu...
bool fgSetDouble(const char *name, double defaultValue)
Set a double value for a property.
Definition proptest.cpp:31
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
Definition proptest.cpp:25
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
Definition proptest.cpp:24
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
bool fgPrintVisibleSceneInfo(FGRenderer *renderer)
bool fgDumpTerrainBranchToFile(const char *filename)
Definition renderer.cxx:886
bool fgPreliminaryGLVersionCheck()
Attempt to create an off-screen pixel buffer to check whether our target OpenGL version is available ...
bool fgDumpNodeToFile(osg::Node *node, const char *filename)
Definition renderer.cxx:893
bool fgDumpSceneGraphToFile(const char *filename)
Definition renderer.cxx:879