FlightGear next
VRManager.cxx
Go to the documentation of this file.
1// Copyright (C) 2021 James Hogan
2//
3// This program is free software; you can redistribute it and/or
4// modify it under the terms of the GNU General Public License as
5// published by the Free Software Foundation; either version 2 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11// General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program; if not, write to the Free Software
15// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
17#include "VRManager.hxx"
18#include "WindowBuilder.hxx"
19#include "renderer.hxx"
20
21#include <osgXR/Settings>
22
23#include <simgear/scene/util/RenderConstants.hxx>
24#include <simgear/scene/viewer/Compositor.hxx>
25#include <simgear/scene/viewer/CompositorPass.hxx>
26
27#include <Main/fg_props.hxx>
28#include <Main/globals.hxx>
29
30namespace flightgear
31{
32using namespace simgear;
33using namespace compositor;
34
35// Unfortunately, this can't be scoped inside VRManager::instance().
36// If its initialisation completes after main() calls atexit(fgExitCleanup),
37// then its destruction should take place before fgExitCleanup() is called.
38static osg::ref_ptr<VRManager> managerInstance;
39
40VRManager::VRManager() :
41 _reloadCompositorCallback(new ReloadCompositorCallback(this)),
42 _propXrLayersValidation("/sim/vr/openxr/layers/validation"),
43 _propXrExtensionsDepthInfo("/sim/vr/openxr/extensions/depth-info"),
44 _propXrExtensionsVisibilityMask("/sim/vr/openxr/extensions/visibility-mask"),
45 _propXrRuntimeName("/sim/vr/openxr/runtime/name"),
46 _propXrSystemName("/sim/vr/openxr/system/name"),
47 _propStateString("/sim/vr/state-string"),
48 _propPresent("/sim/vr/present"),
49 _propRunning("/sim/vr/running"),
50 _propEnabled("/sim/vr/enabled"),
51 _propDepthInfo("/sim/vr/depth-info"),
52 _propVisibilityMask("/sim/vr/visibility-mask"),
53 _propValidationLayer("/sim/vr/validation-layer"),
54 _propMode("/sim/vr/mode"),
55 _propSwapchainMode("/sim/vr/swapchain-mode"),
56 _propMirrorEnabled("/sim/vr/mirror-enabled"),
57 _propMirrorMode("/sim/vr/mirror-mode"),
58 _listenerEnabled(this, &osgXR::Manager::setEnabled),
59 _listenerDepthInfo(this, &VRManager::setDepthInfo),
60 _listenerVisibilityMask(this, &VRManager::setVisibilityMask),
61 _listenerValidationLayer(this, &VRManager::setValidationLayer),
62 _listenerMode(this, &VRManager::setVRMode),
63 _listenerSwapchainMode(this, &VRManager::setSwapchainMode),
64 _listenerMirrorMode(this, &VRManager::setMirrorMode)
65{
66 uint32_t fgVersion = (FLIGHTGEAR_MAJOR_VERSION << 16 |
67 FLIGHTGEAR_MINOR_VERSION << 8 |
68 FLIGHTGEAR_PATCH_VERSION);
69 _settings->setApp("FlightGear", fgVersion);
70 _settings->preferEnvBlendMode(osgXR::Settings::BLEND_MODE_OPAQUE);
71
72 // Inform osgXR what node masks to use
73 setVisibilityMaskNodeMasks(simgear::NodeMask::LEFT_BIT,
74 simgear::NodeMask::RIGHT_BIT);
75
76 // Hook into viewer, but don't enable VR just yet
77 osgViewer::View *view = globals->get_renderer()->getView();
78 if (view) {
79 setViewer(globals->get_renderer()->getViewerBase());
80 view->apply(this);
81 }
82
83 syncReadOnlyProperties();
84
85 _propEnabled.node(true)->addChangeListener(&_listenerEnabled, true);
86 _propDepthInfo.node(true)->addChangeListener(&_listenerDepthInfo, true);
87 _propVisibilityMask.node(true)->addChangeListener(&_listenerVisibilityMask, true);
88 _propValidationLayer.node(true)->addChangeListener(&_listenerValidationLayer, true);
89 _propMode.node(true)->addChangeListener(&_listenerMode, true);
90 _propSwapchainMode.node(true)->addChangeListener(&_listenerSwapchainMode, true);
91 _propMirrorMode.node(true)->addChangeListener(&_listenerMirrorMode, true);
92
93 // No need for a change listener, but it should still be resolvable
94 _propMirrorEnabled.node(true);
95
96 // Determine what multiview support the default compositor implements.
97 std::string compositorPath = fgGetString("/sim/rendering/default-compositor",
98 "Compositor/default");
99 SGPropertyNode_ptr compositorProps = Compositor::loadPropertyList(compositorPath);
100 if (compositorProps.valid()) {
101 _settings->setViewAlignmentMask(compositorProps->getIntValue("multiview/view-align-mask", 0));
102
103 _settings->allowVRMode(osgXR::Settings::VRMODE_SLAVE_CAMERAS);
104 if (compositorProps->getBoolValue("multiview/sceneview", false))
105 _settings->allowVRMode(osgXR::Settings::VRMODE_SCENE_VIEW);
106
107 _settings->allowSwapchainMode(osgXR::Settings::SWAPCHAIN_MULTIPLE);
108 if (compositorProps->getBoolValue("multiview/intermediates-tiled", false))
109 _settings->allowSwapchainMode(osgXR::Settings::SWAPCHAIN_SINGLE);
110 }
111}
112
113VRManager *VRManager::instance()
114{
115 static bool initialised = false;
116 if (!initialised) {
117 managerInstance = new VRManager;
118 initialised = true;
119 }
120 return managerInstance;
121}
122
123void VRManager::syncProperties()
124{
125 // If the state has changed, properties may need synchronising
126 if (checkAndResetStateChanged()) {
127 syncReadOnlyProperties();
128 syncSettingProperties();
129 }
130}
131
132void VRManager::syncReadOnlyProperties()
133{
134 _propXrLayersValidation = hasValidationLayer();
135 _propXrExtensionsDepthInfo = hasDepthInfoExtension();
136 _propXrExtensionsVisibilityMask = hasVisibilityMaskExtension();
137 _propXrRuntimeName = getRuntimeName();
138 _propXrSystemName = getSystemName();
139
140 _propStateString = getStateString();
141 _propPresent = getPresent();
142 _propRunning = isRunning();
143}
144
145void VRManager::syncSettingProperties()
146{
147 bool enabled = getEnabled();
148 if (_propEnabled != enabled)
149 _propEnabled = enabled;
150}
151
152bool VRManager::getUseMirror() const
153{
154 return _propMirrorEnabled && isRunning();
155}
156
157void VRManager::setValidationLayer(bool validationLayer)
158{
159 _settings->setValidationLayer(validationLayer);
160 syncSettings();
161}
162
163void VRManager::setDepthInfo(bool depthInfo)
164{
165 _settings->setDepthInfo(depthInfo);
166 syncSettings();
167}
168
169void VRManager::setVisibilityMask(bool visibilityMask)
170{
171 _settings->setVisibilityMask(visibilityMask);
172 syncSettings();
173}
174
175void VRManager::setVRMode(const std::string& mode)
176{
177 osgXR::Settings::VRMode vrMode = osgXR::Settings::VRMODE_AUTOMATIC;
178
179 if (mode == "AUTOMATIC") {
180 vrMode = osgXR::Settings::VRMODE_AUTOMATIC;
181 } else if (mode == "SLAVE_CAMERAS") {
182 vrMode = osgXR::Settings::VRMODE_SLAVE_CAMERAS;
183 } else if (mode == "SCENE_VIEW") {
184 vrMode = osgXR::Settings::VRMODE_SCENE_VIEW;
185 }
186
187 _settings->setVRMode(vrMode);
188 syncSettings();
189}
190
191void VRManager::setSwapchainMode(const std::string& mode)
192{
193 osgXR::Settings::SwapchainMode swapchainMode = osgXR::Settings::SWAPCHAIN_AUTOMATIC;
194
195 if (mode == "AUTOMATIC") {
196 swapchainMode = osgXR::Settings::SWAPCHAIN_AUTOMATIC;
197 } else if (mode == "MULTIPLE") {
198 swapchainMode = osgXR::Settings::SWAPCHAIN_MULTIPLE;
199 } else if (mode == "SINGLE") {
200 swapchainMode = osgXR::Settings::SWAPCHAIN_SINGLE;
201 }
202
203 _settings->setSwapchainMode(swapchainMode);
204 syncSettings();
205}
206
207void VRManager::setMirrorMode(const std::string& mode)
208{
209 osgXR::MirrorSettings::MirrorMode mirrorMode = osgXR::MirrorSettings::MIRROR_AUTOMATIC;
210 int viewIndex = -1;
211
212 if (mode == "AUTOMATIC") {
213 mirrorMode = osgXR::MirrorSettings::MIRROR_AUTOMATIC;
214 } else if (mode == "NONE") {
215 mirrorMode = osgXR::MirrorSettings::MIRROR_NONE;
216 } else if (mode == "LEFT") {
217 mirrorMode = osgXR::MirrorSettings::MIRROR_SINGLE;
218 viewIndex = 0;
219 } else if (mode == "RIGHT") {
220 mirrorMode = osgXR::MirrorSettings::MIRROR_SINGLE;
221 viewIndex = 1;
222 } else if (mode == "LEFT_RIGHT") {
223 mirrorMode = osgXR::MirrorSettings::MIRROR_LEFT_RIGHT;
224 }
225
226 _settings->getMirrorSettings().setMirror(mirrorMode, viewIndex);
227}
228
229void VRManager::update()
230{
231 osgXR::Manager::update();
232 syncProperties();
233}
234
235void VRManager::doCreateView(osgXR::View *xrView)
236{
237 // Restarted in osgXR::Manager::update()
238 _viewer->stopThreading();
239
240 // Construct a property tree for the camera
241 SGPropertyNode_ptr camNode = new SGPropertyNode;
242 setValue(camNode->getNode("window/name", true),
244 setValue(camNode->getNode("viewport/width", true), (int)xrView->getMVRWidth());
245 setValue(camNode->getNode("viewport/height", true), (int)xrView->getMVRHeight());
246 setValue(camNode->getNode("mvr-views", true), (int)xrView->getMVRViews());
247 setValue(camNode->getNode("mvr-view-id-global", true), xrView->getMVRViewIdGlobalStr());
248 setValue(camNode->getNode("mvr-view-id-vert", true), xrView->getMVRViewIdStr(GL_VERTEX_SHADER));
249 setValue(camNode->getNode("mvr-view-id-geom", true), xrView->getMVRViewIdStr(GL_GEOMETRY_SHADER));
250 setValue(camNode->getNode("mvr-view-id-frag", true), xrView->getMVRViewIdStr(GL_FRAGMENT_SHADER));
251 setValue(camNode->getNode("mvr-cells", true), (int)xrView->getMVRCells());
252
253 // Build a camera
254 CameraGroup *cgroup = CameraGroup::getDefault();
255 CameraInfo *info = cgroup->buildCamera(camNode);
256
257 // Notify osgXR about the new compositor's scene slave cameras
258 if (info) {
259 _camInfos[xrView] = info;
260 _xrViews[info] = xrView;
261 info->reloadCompositorCallback = _reloadCompositorCallback;
262
263 postReloadCompositor(cgroup, info);
264 }
265
266 // Get notified of subview changes
267 xrView->setCallback(new ViewCallback(this));
268}
269
270void VRManager::doDestroyView(osgXR::View *xrView)
271{
272 // Restarted in osgXR::Manager::update()
273 _viewer->stopThreading();
274
275 CameraGroup *cgroup = CameraGroup::getDefault();
276 auto it = _camInfos.find(xrView);
277 if (it != _camInfos.end()) {
278 osg::ref_ptr<CameraInfo> info = (*it).second;
279 _camInfos.erase(it);
280
281 auto it2 = _xrViews.find(info.get());
282 if (it2 != _xrViews.end())
283 _xrViews.erase(it2);
284
285 cgroup->removeCamera(info.get());
286 }
287}
288
289void VRManager::onRunning()
290{
291 // Reload compositors to trigger switch to mirror of VR
292 CameraGroup *cgroup = CameraGroup::getDefault();
293 reloadCompositors(cgroup);
294}
295
296void VRManager::onStopped()
297{
298 // As long as we're not in the process of destroying FlightGear, reload
299 // compositors to trigger switch away from mirror of VR
300 if (!isDestroying())
301 {
302 CameraGroup *cgroup = CameraGroup::getDefault();
303 reloadCompositors(cgroup);
304 }
305}
306
307static osgXR::View::Flags getPassVRFlags(const simgear::compositor::Pass *pass)
308{
309 bool isScene = (pass->type == "scene");
310 bool isMultiviewQuad = (pass->type == "quad" && pass->multiview == "true");
311 bool isWidthScaled = (pass->viewport_width_scale != 0.0f);
312 bool isHeightScaled = (pass->viewport_height_scale != 0.0f);
313 // NOTE: isToFb may change after osgXR has made changes.
314 // Whether getPassVRFlags() returns 0 must not change as a result.
315 bool isToFb = (pass->camera->getRenderTargetImplementation() == osg::Camera::FRAME_BUFFER);
316
317 osgXR::View::Flags flags = osgXR::View::CAM_NO_BITS;
318
319 // If camera renders to the frame buffer, redirect to XR.
320 if ((isScene || isMultiviewQuad) && isToFb)
321 flags |= osgXR::View::CAM_TOXR_BIT;
322
323 if (isScene && (isToFb || (isWidthScaled && isHeightScaled))) {
324 // If scene is rendered to a scaled viewport, perform multiview scene
325 // rendering with shading.
326 flags |= osgXR::View::CAM_MVR_SCENE_BIT;
327 flags |= osgXR::View::CAM_MVR_SHADING_BIT;
328 } else if (isMultiviewQuad) {
329 // If multiview quad is rendered, perform multiview shading
330 flags |= osgXR::View::CAM_MVR_SHADING_BIT;
331
332 if (!(flags & osgXR::View::CAM_TOXR_BIT)) {
333 // Fixed size MVR results in identically sized viewports
334 if (!isWidthScaled)
335 flags |= osgXR::View::CAM_MVR_FIXED_WIDTH_BIT;
336 if (!isHeightScaled)
337 flags |= osgXR::View::CAM_MVR_FIXED_HEIGHT_BIT;
338 }
339 }
340
341 return flags;
342}
343
344void VRManager::preReloadCompositor(CameraGroup *cgroup, CameraInfo *info)
345{
346 osgXR::View *xrView = _xrViews[info];
347
348 auto& passes = info->compositor->getPassList();
349 for (auto& pass: passes) {
350 // osgXR may change camera's render target implementation (isToFb), but
351 // it shouldn't change whether flags == 0
352 auto flags = getPassVRFlags(pass);
353 if (flags)
354 xrView->removeSlave(pass->camera);
355 }
356}
357
358void VRManager::postReloadCompositor(CameraGroup *cgroup, CameraInfo *info)
359{
360 osgXR::View *xrView = _xrViews[info];
361
362 auto& passes = info->compositor->getPassList();
363 for (auto& pass: passes) {
364 auto flags = getPassVRFlags(pass);
365 if (flags)
366 xrView->addSlave(pass->camera, flags);
367 }
368}
369
370void VRManager::updateSubView(osgXR::View *view, unsigned int subviewIndex,
371 const osgXR::View::SubView &subview)
372{
373 auto it = _camInfos.find(view);
374 if (it == _camInfos.end())
375 return;
376
377 osg::ref_ptr<CameraInfo> info = (*it).second;
378
379 osg::Matrix viewMatrix = subview.getViewMatrix();
380 osg::Matrix projMatrix = subview.getProjectionMatrix();
381
382 // see CameraGroup::update()
383 viewMatrix = info->viewOffset * viewMatrix;
384 if ((info->flags & CameraInfo::VIEW_ABSOLUTE) == 0) {
385 auto *masterCam = CameraGroup::getDefault()->getView()->getCamera();
386 viewMatrix = masterCam->getViewMatrix() * viewMatrix;
387 }
388
389 auto vp = subview.getViewport();
390 info->compositor->updateSubView(subviewIndex, viewMatrix, projMatrix,
391 osg::Vec4(vp.x, vp.y, vp.w, vp.h));
392}
393
394}
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
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
static osg::ref_ptr< VRManager > managerInstance
Definition VRManager.cxx:38
void reloadCompositors(CameraGroup *cgroup)
Force a reload of all Compositor instances in the CameraGroup, except the one used by the GUI camera.
const char DEFAULT_WINDOW_NAME[]
static osgXR::View::Flags getPassVRFlags(const simgear::compositor::Pass *pass)