/
vrwindowtransform.cpp
378 lines (322 loc) · 11.7 KB
/
vrwindowtransform.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/** @file vrwindowtransform.cpp Window content transformation for virtual reality.
*
* @authors Copyright (c) 2013 Christopher Bruns <cmbruns@rotatingpenguin.com>
* @authors Copyright (c) 2013 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/
#include "de/VRWindowTransform"
#include "de/VRConfig"
#include "de/BaseGuiApp"
#include "de/BaseWindow"
#include "de/GuiWidget"
#include <de/Drawable>
#include <de/GLFramebuffer>
namespace de {
DENG2_PIMPL(VRWindowTransform)
{
VRConfig &vrCfg;
GLFramebuffer unwarpedFB;
Instance(Public *i)
: Base(i)
, vrCfg(DENG2_BASE_GUI_APP->vr())
{}
~Instance()
{
vrCfg.oculusRift().deinit();
}
Canvas &canvas() const
{
return self.window().canvas();
}
GLTarget &target() const
{
return canvas().renderTarget();
}
int width() const
{
return canvas().width();
}
int height() const
{
return canvas().height();
}
void drawContent() const
{
LIBGUI_ASSERT_GL_OK();
self.window().drawWindowContent();
LIBGUI_ASSERT_GL_OK();
}
/**
* Draws the entire UI in two halves, one for the left eye and one for the right. The
* Oculus Rift optical distortion effect is applied using a shader.
*
* @todo unwarpedTarget and unwarpedTexture should be cleared/deleted when Oculus
* Rift mode is disabled (or whenever they are not needed).
*/
void vrDrawOculusRift()
{
OculusRift &ovr = vrCfg.oculusRift();
vrCfg.enableFrustumShift(false);
// Use a little bit of multisampling to smooth out the magnified jagged edges.
// Note: Independent of the vid-fsaa setting because this is beneficial even when
// vid-fsaa is disabled.
unwarpedFB.setSampleCount(1); //vrCfg.riftFramebufferSampleCount());
// Set render target to offscreen temporarily.
GLState::push()
.setTarget(unwarpedFB.target())
.setViewport(Rectangleui::fromSize(unwarpedFB.size()))
.apply();
unwarpedFB.target().unsetActiveRect(true);
GLFramebuffer::Size const fbSize = unwarpedFB.size();
// Left eye view on left side of screen.
for(int eyeIdx = 0; eyeIdx < 2; ++eyeIdx)
{
ovr.setCurrentEye(eyeIdx);
if(ovr.currentEye() == OculusRift::LeftEye)
{
// Left eye on the left side of the screen.
unwarpedFB.target().setActiveRect(Rectangleui(0, 0, fbSize.x/2, fbSize.y), true);
}
else
{
// Right eye on the right side of screen.
unwarpedFB.target().setActiveRect(Rectangleui(fbSize.x/2, 0, fbSize.x/2, fbSize.y), true);
}
drawContent();
}
unwarpedFB.target().unsetActiveRect(true);
GLState::pop().apply();
vrCfg.enableFrustumShift(); // restore default
}
void draw()
{
switch(vrCfg.mode())
{
// A) Single view type stereo 3D modes here:
case VRConfig::Mono:
// Non-stereoscopic frame.
drawContent();
break;
case VRConfig::LeftOnly:
// Left eye view
vrCfg.setCurrentEye(VRConfig::LeftEye);
drawContent();
break;
case VRConfig::RightOnly:
// Right eye view
vrCfg.setCurrentEye(VRConfig::RightEye);
drawContent();
break;
// B) Split-screen type stereo 3D modes here:
case VRConfig::TopBottom: // Left goes on top
// Left eye view on top of screen.
vrCfg.setCurrentEye(VRConfig::LeftEye);
target().setActiveRect(Rectangleui(0, 0, width(), height()/2), true);
drawContent();
// Right eye view on bottom of screen.
vrCfg.setCurrentEye(VRConfig::RightEye);
target().setActiveRect(Rectangleui(0, height()/2, width(), height()/2), true);
drawContent();
break;
case VRConfig::SideBySide: // Squished aspect
// Left eye view on left side of screen.
vrCfg.setCurrentEye(VRConfig::LeftEye);
target().setActiveRect(Rectangleui(0, 0, width()/2, height()), true);
drawContent();
// Right eye view on right side of screen.
vrCfg.setCurrentEye(VRConfig::RightEye);
target().setActiveRect(Rectangleui(width()/2, 0, width()/2, height()), true);
drawContent();
break;
case VRConfig::Parallel: // Normal aspect
// Left eye view on left side of screen.
vrCfg.setCurrentEye(VRConfig::LeftEye);
target().setActiveRect(Rectangleui(0, 0, width()/2, height()), true);
drawContent();
// Right eye view on right side of screen.
vrCfg.setCurrentEye(VRConfig::RightEye);
target().setActiveRect(Rectangleui(width()/2, 0, width()/2, height()), true);
drawContent();
break;
case VRConfig::CrossEye: // Normal aspect
// Right eye view on left side of screen.
vrCfg.setCurrentEye(VRConfig::RightEye);
target().setActiveRect(Rectangleui(0, 0, width()/2, height()), true);
drawContent();
// Left eye view on right side of screen.
vrCfg.setCurrentEye(VRConfig::LeftEye);
target().setActiveRect(Rectangleui(width()/2, 0, width()/2, height()), true);
drawContent();
break;
case VRConfig::OculusRift:
vrDrawOculusRift();
break;
// Overlaid type stereo 3D modes below:
case VRConfig::GreenMagenta:
// Left eye view
vrCfg.setCurrentEye(VRConfig::LeftEye);
GLState::push().setColorMask(gl::WriteGreen | gl::WriteAlpha).apply(); // Left eye view green
drawContent();
// Right eye view
vrCfg.setCurrentEye(VRConfig::RightEye);
GLState::current().setColorMask(gl::WriteRed | gl::WriteBlue | gl::WriteAlpha).apply(); // Right eye view magenta
drawContent();
GLState::pop().apply();
break;
case VRConfig::RedCyan:
// Left eye view
vrCfg.setCurrentEye(VRConfig::LeftEye);
GLState::push().setColorMask(gl::WriteRed | gl::WriteAlpha).apply(); // Left eye view red
drawContent();
// Right eye view
vrCfg.setCurrentEye(VRConfig::RightEye);
GLState::current().setColorMask(gl::WriteGreen | gl::WriteBlue | gl::WriteAlpha).apply(); // Right eye view cyan
drawContent();
GLState::pop().apply();
break;
case VRConfig::QuadBuffered:
if(canvas().format().stereo())
{
// Left eye view
vrCfg.setCurrentEye(VRConfig::LeftEye);
drawContent();
canvas().framebuffer().swapBuffers(canvas(), gl::SwapStereoLeftBuffer);
// Right eye view
vrCfg.setCurrentEye(VRConfig::RightEye);
drawContent();
canvas().framebuffer().swapBuffers(canvas(), gl::SwapStereoRightBuffer);
}
else
{
// Normal non-stereoscopic frame.
drawContent();
}
break;
case VRConfig::RowInterleaved:
{
// Use absolute screen position of window to determine whether the
// first scan line is odd or even.
QPoint ulCorner(0, 0);
ulCorner = canvas().mapToGlobal(ulCorner); // widget to screen coordinates
bool rowParityIsEven = ((ulCorner.x() % 2) == 0);
DENG2_UNUSED(rowParityIsEven);
/// @todo - use row parity in shader or stencil, to actually interleave rows.
// Left eye view
vrCfg.setCurrentEye(VRConfig::LeftEye);
drawContent();
// Right eye view
vrCfg.setCurrentEye(VRConfig::RightEye);
drawContent();
break;
}
case VRConfig::ColumnInterleaved: /// @todo implement column interleaved stereo 3D after row intleaved is working correctly...
case VRConfig::Checkerboard: /// @todo implement checker stereo 3D after row intleaved is working correctly ...
default:
// Non-stereoscopic frame.
drawContent();
break;
}
// Restore default VR dynamic parameters
target().unsetActiveRect(true);
vrCfg.setCurrentEye(VRConfig::NeitherEye);
LIBGUI_ASSERT_GL_OK();
}
};
VRWindowTransform::VRWindowTransform(BaseWindow &window)
: WindowTransform(window), d(new Instance(this))
{}
void VRWindowTransform::glInit()
{
//d->init();
}
void VRWindowTransform::glDeinit()
{
//d->deinit();
}
Vector2ui VRWindowTransform::logicalRootSize(Vector2ui const &physicalCanvasSize) const
{
Canvas::Size size = physicalCanvasSize;
switch(d->vrCfg.mode())
{
// Left-right screen split modes
case VRConfig::CrossEye:
case VRConfig::Parallel:
// Adjust effective UI size for stereoscopic rendering.
size.y *= 2;
size *= .75f; // Make it a bit bigger.
break;
case VRConfig::OculusRift:
// Adjust effective UI size for stereoscopic rendering.
size.x = size.y * d->vrCfg.oculusRift().aspect();
//size.y *= d->vrCfg.oculusRift().aspect();
size *= GuiWidget::toDevicePixels(1) * .75f;
break;
// Allow UI to squish in top/bottom and SBS mode: 3D hardware will unsquish them
case VRConfig::TopBottom:
case VRConfig::SideBySide:
default:
break;
}
return size;
}
Vector2f VRWindowTransform::windowToLogicalCoords(Vector2i const &winPos) const
{
// We need to map the real window coordinates to logical root view
// coordinates according to the used transformation.
Vector2f pos = winPos;
Vector2f const size = window().canvas().size();
Vector2f const viewSize = window().windowContentSize();
switch(d->vrCfg.mode())
{
// Left-right screen split modes
case VRConfig::SideBySide:
case VRConfig::CrossEye:
case VRConfig::Parallel:
case VRConfig::OculusRift:
// Make it possible to access both frames.
if(pos.x >= size.x/2)
{
pos.x -= size.x/2;
}
pos.x *= 2;
// Scale to logical size.
pos = pos / size * viewSize;
break;
// Top-bottom screen split modes
case VRConfig::TopBottom:
// Make it possible to access both frames.
if(pos.y >= size.y/2)
{
pos.y -= size.y/2;
}
pos.y *= 2;
// Scale to logical size.
pos = pos / size * viewSize;
break;
default:
// Not transformed.
break;
}
return pos;
}
void VRWindowTransform::drawTransformed()
{
d->draw();
}
GLFramebuffer &VRWindowTransform::unwarpedFramebuffer()
{
return d->unwarpedFB;
}
} // namespace de