1 /*
2 * Copyright (C) 2017 Guillaume Chereau
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
17 */
18
19 #include "Oculus.hpp"
20 #include "StelActionMgr.hpp"
21 #include "StelApp.hpp"
22 #include "StelLocaleMgr.hpp"
23 #include "StelMainView.hpp"
24 #include "StelModuleMgr.hpp"
25 #include "StelMovementMgr.hpp"
26 #include "StelOpenGL.hpp"
27 #include "Extras/OVR_Math.h"
28
29 #include <QOpenGLFramebufferObject>
30
31 #include <assert.h>
32
33 #define OVRC(f) do { \
34 ovrResult result = f; \
35 if (OVR_FAILURE(result)) { \
36 qWarning("OVR error %d, line %d", result, __LINE__); \
37 Q_ASSERT(false); \
38 } \
39 } while (0)
40
quat_to_mat4(const float q[4],float out[4][4])41 void quat_to_mat4(const float q[4], float out[4][4])
42 {
43 float x = q[0];
44 float y = q[1];
45 float z = q[2];
46 float w = q[3];
47
48 out[0][0] = 1-2*y*y-2*z*z;
49 out[0][1] = 2*x*y+2*z*w;
50 out[0][2] = 2*x*z-2*y*w;
51 out[0][3] = 0;
52 out[1][0] = 2*x*y-2*z*w;
53 out[1][1] = 1-2*x*x-2*z*z;
54 out[1][2] = 2*y*z+2*x*w;
55 out[1][3] = 0;
56 out[2][0] = 2*x*z+2*y*w;
57 out[2][1] = 2*y*z-2*x*w;
58 out[2][2] = 1-2*x*x-2*y*y;
59 out[2][3] = 0;
60 out[3][0] = 0;
61 out[3][1] = 0;
62 out[3][2] = 0;
63 out[3][3] = 1;
64 }
65
mat4_set_identity(float mat[4][4])66 void mat4_set_identity(float mat[4][4])
67 {
68 int i, j;
69 for (i = 0; i < 4; i++)
70 for (j = 0; j < 4; j++)
71 mat[i][j] = (i == j) ? 1.f : 0.f;
72 }
73
mat4_translate(float m[4][4],float x,float y,float z)74 void mat4_translate(float m[4][4], float x, float y, float z)
75 {
76 int i;
77 for (i = 0; i < 4; i++)
78 m[3][i] += m[0][i] * x + m[1][i] * y + m[2][i] * z;
79 }
80
mat4_mul(float mat[4][4],float b[4][4])81 void mat4_mul(float mat[4][4], float b[4][4])
82 {
83 int i, j, k;
84 float ret[4][4] = {{0}};
85 for (i = 0; i < 4; i++) {
86 for (j = 0; j < 4; j++) {
87 for (k = 0; k < 4; ++k) {
88 ret[j][i] += mat[k][i] * b[j][k];
89 }
90 }
91 }
92 memcpy(mat, ret, sizeof(ret));
93 }
94
mat4_invert(float mat[4][4])95 bool mat4_invert(float mat[4][4])
96 {
97 float det;
98 int i;
99 float *m = (float*)mat;
100 float inv[16];
101
102 #define M(i, j, k) m[i] * m[j] * m[k]
103 inv[0] = M(5, 10, 15) - M(5, 11, 14) - M(9, 6, 15) +
104 M(9, 7, 14) + M(13, 6, 11) - M(13, 7, 10);
105 inv[4] = -M(4, 10, 15) + M( 4, 11, 14) + M( 8, 6, 15) -
106 M(8, 7, 14) - M(12, 6, 11) + M(12, 7, 10);
107 inv[8] = M(4, 9, 15) - M( 4, 11, 13) - M(8, 5, 15) +
108 M(8, 7, 13) + M(12, 5, 11) - M(12, 7, 9);
109 inv[12] = -M(4, 9, 14) + M(4, 10, 13) + M(8, 5, 14) -
110 M(8, 6, 13) - M(12, 5, 10) + M(12, 6, 9);
111 inv[1] = -M(1, 10, 15) + M(1, 11, 14) + M(9, 2, 15) -
112 M(9, 3, 14) - M(13, 2, 11) + M(13, 3, 10);
113 inv[5] = M(0, 10, 15) - M(0, 11, 14) - M(8, 2, 15) +
114 M(8, 3, 14) + M(12, 2, 11) - M(12, 3, 10);
115 inv[9] = -M(0, 9, 15) + M(0, 11, 13) + M(8, 1, 15) -
116 M(8, 3, 13) - M(12, 1, 11) + M(12, 3, 9);
117 inv[13] = M(0, 9, 14) - M(0, 10, 13) - M(8, 1, 14) +
118 M(8, 2, 13) + M(12, 1, 10) - M(12, 2, 9);
119 inv[2] = M(1, 6, 15) - M(1, 7, 14) - M(5, 2, 15) +
120 M(5, 3, 14) + M(13, 2, 7) - M(13, 3, 6);
121 inv[6] = -M(0, 6, 15) + M(0, 7, 14) + M(4, 2, 15) -
122 M(4, 3, 14) - M(12, 2, 7) + M(12, 3, 6);
123 inv[10] = M(0, 5, 15) - M(0, 7, 13) - M(4, 1, 15) +
124 M(4, 3, 13) + M(12, 1, 7) - M(12, 3, 5);
125 inv[14] = -M(0, 5, 14) + M(0, 6, 13) + M(4, 1, 14) -
126 M(4, 2, 13) - M(12, 1, 6) + M(12, 2, 5);
127 inv[3] = -M(1, 6, 11) + M(1, 7, 10) + M(5, 2, 11) -
128 M(5, 3, 10) - M(9, 2, 7) + M(9, 3, 6);
129 inv[7] = M(0, 6, 11) - M(0, 7, 10) - M(4, 2, 11) +
130 M(4, 3, 10) + M(8, 2, 7) - M(8, 3, 6);
131 inv[11] = -M(0, 5, 11) + M(0, 7, 9) + M(4, 1, 11) -
132 M(4, 3, 9) - M(8, 1, 7) + M(8, 3, 5);
133 inv[15] = M(0, 5, 10) - M(0, 6, 9) - M(4, 1, 10) +
134 M(4, 2, 9) + M(8, 1, 6) - M(8, 2, 5);
135 #undef M
136
137 det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
138
139 if (det == 0) {
140 return false;
141 }
142
143 det = 1.0 / det;
144
145 for (i = 0; i < 16; i++)
146 ((float*)mat)[i] = inv[i] * det;
147 return true;
148 }
149
view_from_pose(ovrPosef pose)150 static ovrMatrix4f view_from_pose(ovrPosef pose)
151 {
152 // XXX: use stellarium matrix so that we can remove the mat functions.
153 ovrMatrix4f ret;
154 float rot[4][4];
155
156 quat_to_mat4((float*)&pose.Orientation, rot);
157 mat4_set_identity(ret.M);
158 mat4_translate(ret.M, pose.Position.x, pose.Position.y, pose.Position.z);
159 mat4_mul(ret.M, rot);
160
161 mat4_invert(ret.M);
162 return ret;
163 }
164
getStelModule() const165 StelModule* OculusStelPluginInterface::getStelModule() const
166 {
167 return new Oculus();
168 }
169
getPluginInfo() const170 StelPluginInfo OculusStelPluginInterface::getPluginInfo() const
171 {
172 StelPluginInfo info;
173 info.id = "Oculus";
174 info.displayedName = N_("Oculus");
175 info.authors = "Guillaume Chereau";
176 info.contact = "guillaume@noctua-software.com";
177 info.description = N_("Support for Oculus Rift");
178 info.version = OCULUS_PLUGIN_VERSION;
179 info.startByDefault = true;
180 return info;
181 }
182
183
Oculus()184 Oculus::Oculus()
185 {
186 setObjectName("Oculus");
187 StelActionMgr *actionsMgr = StelApp::getInstance().getStelActionManager();
188 actionsMgr->findAction("actionGoto_Selected_Object")->setShortcut("");
189 actionsMgr->addAction("actionOculus_recenter", "Oculus", "recenter", this, "recenter()", "Space");
190 }
191
~Oculus()192 Oculus::~Oculus()
193 {
194 }
195
init()196 void Oculus::init()
197 {
198 qDebug() << "Oculus plugin.";
199 initializeOpenGLFunctions();
200 OVRC(ovr_Initialize(nullptr));
201 ovrHmdDesc hmd = ovr_GetHmdDesc(nullptr);
202 if (hmd.Type == ovrHmd_None)
203 {
204 qDebug() << "No Oculus connected";
205 return;
206 }
207 OVRC(ovr_Create(&session, &luid));
208
209 // Create the textures and layer.
210 layer.Header.Type = ovrLayerType_EyeFov;
211 layer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;
212 for (int i = 0; i < 2; i++)
213 {
214 ovrEyeRenderDesc eyeRenderDesc = ovr_GetRenderDesc(session, (ovrEyeType)i, hmd.DefaultEyeFov[i]);
215 ovrSizei recommenedSize = ovr_GetFovTextureSize(session, (ovrEyeType)i, hmd.DefaultEyeFov[i], 1.0f);
216 ovrTextureSwapChainDesc desc = {};
217 desc.Type = ovrTexture_2D;
218 desc.ArraySize = 1;
219 desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
220 desc.Width = recommenedSize.w;
221 desc.Height = recommenedSize.h;
222 desc.MipLevels = 1;
223 desc.SampleCount = 1;
224 desc.StaticImage = ovrFalse;
225 OVRC(ovr_CreateTextureSwapChainGL(session, &desc, &chains[i]));
226 layer.Viewport[i] = {{0, 0}, {desc.Width, desc.Height}};
227 layer.Fov[i] = eyeRenderDesc.Fov;
228 layer.ColorTexture[i] = chains[i];
229
230 glGenFramebuffers(1, &fbos[i]);
231 glGenRenderbuffers(1, &depthBuffers[i]);
232 glBindFramebuffer(GL_FRAMEBUFFER, fbos[i]);
233 glBindRenderbuffer(GL_RENDERBUFFER, depthBuffers[i]);
234 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, desc.Width, desc.Height);
235 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffers[i]);
236 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
237 Q_ASSERT(status == GL_FRAMEBUFFER_COMPLETE);
238 glBindFramebuffer(GL_FRAMEBUFFER, 0);
239 }
240 }
241
deinit()242 void Oculus::deinit()
243 {
244 if (!session) return;
245 for (int i = 0; i < 2; i++)
246 {
247 ovr_DestroyTextureSwapChain(session, chains[i]);
248 // XXX: delete fbos!
249 }
250 ovr_Destroy(session);
251 ovr_Shutdown();
252 }
253
update(double deltaTime)254 void Oculus::update(double deltaTime)
255 {
256 Q_UNUSED(deltaTime);
257 if (!session) return;
258 ovrHmdDesc hmd = ovr_GetHmdDesc(session);
259
260 double displayMidpointSeconds = ovr_GetPredictedDisplayTime(session, 0);
261 ovrTrackingState ts = ovr_GetTrackingState(session, displayMidpointSeconds, ovrTrue);
262
263 if (!(ts.StatusFlags & (ovrStatus_OrientationTracked | ovrStatus_PositionTracked)))
264 return;
265 ovrPosef pose = ts.HeadPose.ThePose;
266 StelCore* core = StelApp::getInstance().getCore();
267
268 // Set the view direction.
269 StelMovementMgr* mmgr = GETSTELMODULE(StelMovementMgr);
270 QQuaternion quaternion(pose.Orientation.w, pose.Orientation.x, pose.Orientation.y, pose.Orientation.z);
271 QVector3D qViewDirection = quaternion.rotatedVector(QVector3D(0, 0, -1));
272 Vec3d viewDirection(qViewDirection.x(), -qViewDirection.z(), qViewDirection.y());
273 QVector3D qUp = quaternion.rotatedVector(QVector3D(0, 1, 0));
274 Vec3d up(qUp.x(), -qUp.z(), qUp.y());
275 mmgr->setViewDirectionJ2000(core->altAzToJ2000(viewDirection));
276 mmgr->setViewUpVectorJ2000(core->altAzToJ2000(up));
277 core->setCurrentProjectionType(StelCore::ProjectionPerspective);
278 double hFov = (atan(hmd.DefaultEyeFov[0].LeftTan) + atan(hmd.DefaultEyeFov[0].RightTan)) / M_PI * 180;
279 mmgr->zoomTo(hFov, 0);
280 render();
281 // Make sure we never go into low fps mode.
282 StelMainView::getInstance().thereWasAnEvent();
283 }
284
render()285 void Oculus::render()
286 {
287 ovrPosef hmdToEyeViewOffset[2];
288 ovrHmdDesc hmd = ovr_GetHmdDesc(session);
289 for (int eye = 0; eye < 2; eye++)
290 {
291 ovrEyeRenderDesc eyeRenderDesc = ovr_GetRenderDesc(session, (ovrEyeType)eye, hmd.DefaultEyeFov[eye]);
292 hmdToEyeViewOffset[eye] = eyeRenderDesc.HmdToEyePose;
293 }
294 double displayMidpointSeconds = ovr_GetPredictedDisplayTime(session, 0);
295 ovrTrackingState hmdState = ovr_GetTrackingState(session, displayMidpointSeconds, ovrTrue);
296 ovr_CalcEyePoses(hmdState.HeadPose.ThePose, hmdToEyeViewOffset, layer.RenderPose);
297
298 for (int eye = 0; eye < 2; eye++)
299 {
300 GLuint texid;
301 ovrTextureSwapChainDesc desc;
302 OVRC(ovr_GetTextureSwapChainDesc(session, chains[eye], &desc));
303 OVRC(ovr_GetTextureSwapChainBufferGL(session, chains[eye], -1, &texid));
304 drawEye(eye, texid, desc.Width, desc.Height, layer.Fov[eye]);
305 OVRC(ovr_CommitTextureSwapChain(session, chains[eye]));
306 }
307
308 ovrLayerHeader* layers = &layer.Header;
309 OVRC(ovr_SubmitFrame(session, 0, NULL, &layers, 1));
310 }
311
drawEye(int eye,GLuint texid,int width,int height,const ovrFovPort & fov)312 void Oculus::drawEye(int eye, GLuint texid, int width, int height, const ovrFovPort &fov)
313 {
314 StelApp* stelApp = &StelApp::getInstance();
315 ovrMatrix4f proj, view;
316
317 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[eye]);
318 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texid, 0);
319 stelApp->glWindowHasBeenResized(QRectF(0, 0, width, height));
320 double xoffset = (fov.LeftTan - fov.RightTan) / (fov.LeftTan + fov.RightTan) / 2. * 100.;
321 stelApp->getCore()->setViewportHorizontalOffset(xoffset);
322 double yoffset = (fov.DownTan - fov.UpTan) / (fov.DownTan + fov.UpTan) / 2. * 100.;
323 stelApp->getCore()->setViewportVerticalOffset(yoffset);
324
325 view = view_from_pose(layer.RenderPose[eye]);
326 Mat4d mat = Mat4d(
327 view.M[0][0], view.M[0][1], view.M[0][2], view.M[0][3],
328 view.M[1][0], view.M[1][1], view.M[1][2], view.M[1][3],
329 view.M[2][0], view.M[2][1], view.M[2][2], view.M[2][3],
330 0, 0, 0, 1);
331 mat = mat * Mat4d::xrotation(-M_PI / 2);
332 stelApp->getCore()->setMatAltAzModelView(mat);
333
334 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
335 const QList<StelModule*> modules = stelApp->getModuleMgr().getCallOrders(StelModule::ActionDraw);
336 foreach(StelModule* module, modules)
337 {
338 module->draw(stelApp->getCore());
339 }
340 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
341 }
342
recenter()343 void Oculus::recenter()
344 {
345 OVRC(ovr_RecenterTrackingOrigin(session));
346 }
347