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