1 /** @file vrsettingsdialog.cpp  Settings for virtual reality.
2  *
3  * @authors Copyright (c) 2013-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  *
5  * @par License
6  * GPL: http://www.gnu.org/licenses/gpl.html
7  *
8  * <small>This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version. This program is distributed in the hope that it
12  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14  * Public License for more details. You should have received a copy of the GNU
15  * General Public License along with this program; if not, see:
16  * http://www.gnu.org/licenses</small>
17  */
18 
19 #include "ui/dialogs/vrsettingsdialog.h"
20 #include "ui/widgets/cvarsliderwidget.h"
21 #include "ui/widgets/cvartogglewidget.h"
22 #include "ui/widgets/cvarchoicewidget.h"
23 #include "render/vr.h"
24 #include "clientapp.h"
25 #include "api_console.h"
26 #include <doomsday/console/exec.h>
27 
28 #include <de/VariableSliderWidget>
29 #include <de/SignalAction>
30 #include <de/Config>
31 #include "CommandAction"
32 
33 using namespace de;
34 using namespace de::ui;
35 
DENG_GUI_PIMPL(VRSettingsDialog)36 DENG_GUI_PIMPL(VRSettingsDialog)
37 {
38     CVarChoiceWidget *mode;
39     CVarToggleWidget *swapEyes;
40     CVarSliderWidget *dominantEye;
41     CVarSliderWidget *humanHeight;
42     CVarSliderWidget *ipd;
43     VariableSliderWidget *riftDensity;
44     CVarSliderWidget *riftSamples;
45     ButtonWidget *riftReset;
46     ButtonWidget *riftSetup;
47     ButtonWidget *desktopSetup;
48 
49     Impl(Public *i)
50         : Base(i)
51         , riftReset(0)
52         , riftSetup(0)
53     {
54         ScrollAreaWidget &area = self().area();
55 
56         area.add(mode = new CVarChoiceWidget("rend-vr-mode"));
57         mode->items()
58                 << new ChoiceItem(tr("No stereo"),                VRConfig::Mono)
59                 << new ChoiceItem(tr("Anaglyph (green/magenta)"), VRConfig::GreenMagenta)
60                 << new ChoiceItem(tr("Anaglyph (red/cyan)"),      VRConfig::RedCyan)
61                 << new ChoiceItem(tr("Left eye only"),            VRConfig::LeftOnly)
62                 << new ChoiceItem(tr("Right eye only"),           VRConfig::RightOnly)
63                 << new ChoiceItem(tr("Top/bottom"),               VRConfig::TopBottom)
64                 << new ChoiceItem(tr("Side-by-side"),             VRConfig::SideBySide)
65                 << new ChoiceItem(tr("Parallel"),                 VRConfig::Parallel)
66                 << new ChoiceItem(tr("Cross-eye"),                VRConfig::CrossEye)
67                 << new ChoiceItem(tr("Hardware stereo"),          VRConfig::QuadBuffered)
68                 << new ChoiceItem(tr("Row interleaved"),          VRConfig::RowInterleaved)
69                 ;
70 
71         if (vrCfg().oculusRift().isEnabled())
72         {
73             mode->items() << new ChoiceItem(tr("Oculus Rift"), VRConfig::OculusRift);
74         }
75 
76         area.add(swapEyes    = new CVarToggleWidget("rend-vr-swap-eyes", tr("Swap Eyes")));
77         area.add(dominantEye = new CVarSliderWidget("rend-vr-dominant-eye"));
78         area.add(humanHeight = new CVarSliderWidget("rend-vr-player-height"));
79         area.add(riftSamples = new CVarSliderWidget("rend-vr-rift-samples"));
80 
81         area.add(ipd = new CVarSliderWidget("rend-vr-ipd"));
82         ipd->setDisplayFactor(1000);
83         ipd->setPrecision(1);
84 
85         if (vrCfg().oculusRift().isReady())
86         {
87             area.add(riftDensity = new VariableSliderWidget(App::config("vr.oculusRift.pixelDensity"),
88                      Ranged(0.5, 1.0), .01));
89             riftDensity->setPrecision(2);
90 
91             area.add(riftReset = new ButtonWidget);
92             riftReset->setText(tr("Recenter Tracking"));
93             riftReset->setAction(new CommandAction("resetriftpose"));
94 
95             area.add(riftSetup = new ButtonWidget);
96             riftSetup->setText(tr("Apply Rift Settings"));
97             riftSetup->setAction(new SignalAction(thisPublic, SLOT(autoConfigForOculusRift())));
98         }
99 
100         area.add(desktopSetup = new ButtonWidget);
101         desktopSetup->setText(tr("Apply Desktop Settings"));
102         desktopSetup->setAction(new SignalAction(thisPublic, SLOT(autoConfigForDesktop())));
103     }
104 
105     void fetch()
106     {
107         foreach (GuiWidget *child, self().area().childWidgets())
108         {
109             if (ICVarWidget *w = maybeAs<ICVarWidget>(child))
110             {
111                 w->updateFromCVar();
112             }
113         }
114     }
115 };
116 
VRSettingsDialog(String const & name)117 VRSettingsDialog::VRSettingsDialog(String const &name)
118     : DialogWidget(name, WithHeading), d(new Impl(this))
119 {
120     heading().setText(tr("3D & VR Settings"));
121     heading().setImage(style().images().image("vr"));
122 
123     LabelWidget *modeLabel     = LabelWidget::newWithText(tr("Stereo Mode:"), &area());
124     LabelWidget *heightLabel   = LabelWidget::newWithText(tr("Height (m):"), &area());
125     LabelWidget *ipdLabel      = LabelWidget::newWithText(tr("IPD (mm):"), &area());
126     LabelWidget *dominantLabel = LabelWidget::newWithText(tr("Dominant Eye:"), &area());
127 
128     // Layout.
129     GridLayout layout(area().contentRule().left(), area().contentRule().top());
130     layout.setGridSize(2, 0);
131     layout.setColumnAlignment(0, ui::AlignRight);
132 
133     layout << *modeLabel     << *d->mode
134            << *heightLabel   << *d->humanHeight
135            << *ipdLabel      << *d->ipd
136            << *dominantLabel << *d->dominantEye
137            << Const(0)       << *d->swapEyes;
138 
139 #ifdef DENG_HAVE_OCULUS_API
140     LabelWidget *ovrLabel    = LabelWidget::newWithText(_E(D) + tr("Oculus Rift"), &area());
141     LabelWidget *sampleLabel = LabelWidget::newWithText(tr("Multisampling:"), &area());
142     ovrLabel->setFont("separator.label");
143     ovrLabel->margins().setTop("gap");
144     sampleLabel->setTextLineAlignment(ui::AlignRight);
145 
146     layout.setCellAlignment(Vector2i(0, 5), ui::AlignLeft);
147     layout.append(*ovrLabel, 2);
148 
149     LabelWidget *utilLabel = LabelWidget::newWithText(tr("Utilities:"), &area());
150     if (vrCfg().oculusRift().isReady())
151     {
152         layout << *sampleLabel << *d->riftSamples
153                << *LabelWidget::newWithText(tr("Pixel Density:"), &area())
154                << *d->riftDensity;
155 
156         layout << *utilLabel << *d->riftReset
157                << Const(0)   << *d->riftSetup
158                << Const(0)   << *d->desktopSetup;
159     }
160     else
161     {
162         layout << *utilLabel << *d->desktopSetup;
163     }
164 #endif
165 
166     area().setContentSize(layout);
167 
168     buttons()
169             << new DialogButtonItem(DialogWidget::Default | DialogWidget::Accept, tr("Close"))
170             << new DialogButtonItem(DialogWidget::Action, tr("Reset to Defaults"),
171                                     new SignalAction(this, SLOT(resetToDefaults())));
172 
173     d->fetch();
174 }
175 
resetToDefaults()176 void VRSettingsDialog::resetToDefaults()
177 {
178     Con_SetInteger("rend-vr-mode",          VRConfig::Mono);
179     Con_SetInteger("rend-vr-swap-eyes",     0);
180     Con_SetFloat  ("rend-vr-dominant-eye",  0);
181     Con_SetFloat  ("rend-vr-player-height", 1.75f);
182     Con_SetFloat  ("rend-vr-ipd",           0.064f);
183     Con_SetFloat  ("rend-vr-rift-latency",  0.030f);
184     Con_SetInteger("rend-vr-rift-samples",  2);
185 
186     d->fetch();
187 }
188 
autoConfigForOculusRift()189 void VRSettingsDialog::autoConfigForOculusRift()
190 {
191     //Con_Execute(CMDS_DDAY, "setfullres 1280 800", false, false);
192 
193     Con_Execute(CMDS_DDAY, "bindcontrol lookpitch head-pitch", false, false);
194     Con_Execute(CMDS_DDAY, "bindcontrol yawbody head-yaw", false, false);
195 
196     /// @todo This would be a good use case for cvar overriding. -jk
197 
198     Con_SetInteger("rend-vr-mode", VRConfig::OculusRift);
199     App::config().set("window.main.fsaa", false);
200     Con_SetFloat  ("vid-gamma", 1.176f);
201     Con_SetFloat  ("vid-contrast", 1.186f);
202     Con_SetFloat  ("vid-bright", .034f);
203     Con_SetFloat  ("view-bob-height", .2f);
204     Con_SetFloat  ("msg-scale", 1);
205     Con_SetFloat  ("hud-scale", 1);
206 
207     d->fetch();
208 
209     ClientApp::vr().oculusRift().moveWindowToScreen(OculusRift::HMDScreen);
210 }
211 
autoConfigForDesktop()212 void VRSettingsDialog::autoConfigForDesktop()
213 {
214     Con_SetInteger("rend-vr-mode", VRConfig::Mono);
215     Con_SetFloat  ("vid-gamma", 1);
216     Con_SetFloat  ("vid-contrast", 1);
217     Con_SetFloat  ("vid-bright", 0);
218     Con_SetFloat  ("view-bob-height", 1);
219     Con_SetFloat  ("msg-scale", .8f);
220     Con_SetFloat  ("hud-scale", .6f);
221 
222     d->fetch();
223 
224     ClientApp::vr().oculusRift().moveWindowToScreen(OculusRift::DefaultScreen);
225 }
226