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