Fix viewport resize and cleanup code structure of viewport width and height
[mercenary-reloaded.git] / src / libovr / ovr.c
1 /* OVR handling
2  *
3  * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
4  * All Rights Reserved
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Based on example code:
20  * Filename    :   main.cpp
21  * Content     :   Simple minimal VR demo
22  * Created     :   December 1, 2014
23  * Author      :   Tom Heath
24  * Copyright   :   Copyright 2012 Oculus, Inc. All Rights reserved.
25  *
26  * Licensed under the Apache License, Version 2.0 (the "License");
27  * you may not use this file except in compliance with the License.
28  * You may obtain a copy of the License at
29  *
30  * http://www.apache.org/licenses/LICENSE-2.0
31  *
32  * Unless required by applicable law or agreed to in writing, software
33  * distributed under the License is distributed on an "AS IS" BASIS,
34  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35  * See the License for the specific language governing permissions and
36  * limitations under the License.
37  */
38
39 #include <stdio.h>
40 #include <stdint.h>
41 #include <string.h>
42 #include <math.h>
43 #include <errno.h>
44 #include "../libsdl/print.h"
45 #include "ovr.h"
46
47 #include <OVR_CAPI.h>
48 #include <OVR_CAPI_GL.h>
49 #include "helper.h"
50
51 #define GL3_PROTOTYPES 1
52 #include <GL/glew.h>
53
54 #if 0
55 #if defined(_WIN32)
56     #include <dxgi.h> // for GetDefaultAdapterLuid
57     #pragma comment(lib, "dxgi.lib")
58 #endif
59
60 static ovrGraphicsLuid GetDefaultAdapterLuid()
61 {
62         ovrGraphicsLuid luid = ovrGraphicsLuid();
63
64 #if defined(_WIN32)
65         IDXGIFactory* factory = nullptr;
66
67         if (SUCCEEDED(CreateDXGIFactory(IID_PPV_ARGS(&factory)))) {
68                 IDXGIAdapter* adapter = nullptr;
69
70                 if (SUCCEEDED(factory->EnumAdapters(0, &adapter))) {
71                         DXGI_ADAPTER_DESC desc;
72
73                         adapter->GetDesc(&desc);
74                         memcpy(&luid, &desc.AdapterLuid, sizeof(luid));
75                         adapter->Release();
76                 }
77
78                 factory->Release();
79         }
80 #endif
81
82         return luid;
83 }
84
85 static int Compare(const ovrGraphicsLuid lhs, const ovrGraphicsLuid rhs)
86 {
87     return memcmp(&lhs, &rhs, sizeof(ovrGraphicsLuid));
88 }
89 #endif
90
91 static int ovr_initialized = 0;
92 static ovrSession session = NULL;
93 static ovrGraphicsLuid luid;
94 static ovrHmdDesc hmdDesc;
95 static ovrSizei TextureSize[2];
96 static ovrTextureSwapChain textureSwapChain[2] = { NULL, NULL };
97 static GLuint fboId[2] = { 0, 0 };
98 static ovrEyeRenderDesc eyeRenderDesc[2];
99 static ovrPosef hmdToEyeViewPose[2];
100 static ovrLayerEyeFov layer;
101 static long long frameIndex = 0;
102 static double observer_x = 0.0;
103 static double observer_x_normalize = 0.0;
104 static double observer_y = 0.0;
105 static double observer_y_normalize = 0.0;
106 static double observer_z = 0.0;
107 static double observer_z_normalize = 0.0;
108
109 int init_ovr(void)
110 {
111         int sample_count = 1; // FIXME: make MSAA
112         ovrResult result;
113         int eye;
114
115         glewExperimental = GL_TRUE;
116         if (glewInit() != GLEW_OK) {
117                 print_error("Failed to init GLEW\n");
118                 goto error;
119         }
120
121         result = ovr_Initialize(NULL);
122         if (OVR_FAILURE(result)) {
123                 print_error("Failed to init OVR (is Oculus Rift service running?)\n");
124                 goto error;
125         }
126         ovr_initialized = 1;
127
128         result = ovr_Create(&session, &luid);
129         if (OVR_FAILURE(result)) {
130                 print_error("Failed to create OVR session (is the HMD connected?)\n");
131                 goto error;
132         }
133
134 #if 0
135         if (Compare(luid, GetDefaultAdapterLuid())) { // If luid that the Rift is on is not the default adapter LUID...
136                 print_error("OpenGL supports only the default graphics adapter.\n");
137                 goto error;
138         }
139 #endif
140
141         hmdDesc = ovr_GetHmdDesc(session);
142
143         memset(&layer, 0, sizeof(layer));
144         layer.Header.Type      = ovrLayerType_EyeFov;
145         layer.Header.Flags     = 0;
146
147         /* create render buffers */
148         for (eye = 0; eye < 2; eye++) {
149                 TextureSize[eye] = ovr_GetFovTextureSize(session, (eye == 0) ? ovrEye_Left : ovrEye_Right, hmdDesc.DefaultEyeFov[eye], 1.0);
150 #warning hacking resolution
151 //TextureSize[eye].w *= 2;
152 //TextureSize[eye].h *= 2;
153                 ovrTextureSwapChainDesc desc;
154                 int length, i;
155                 GLuint chainTexId;
156
157                 memset(&desc, 0, sizeof(desc));
158                 desc.Type = ovrTexture_2D;
159                 desc.ArraySize = 1;
160                 desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
161                 desc.Width = TextureSize[eye].w;
162                 desc.Height = TextureSize[eye].h;
163                 desc.MipLevels = 1;
164                 desc.SampleCount = sample_count;
165                 desc.StaticImage = ovrFalse;
166
167                 result = ovr_CreateTextureSwapChainGL(session, &desc, &textureSwapChain[eye]);
168                 if (OVR_FAILURE(result)) {
169                         print_error("ovr_CreateTextureSwapChainGL() failed!\n");
170                         goto error;
171                 }
172
173                 ovr_GetTextureSwapChainLength(session, textureSwapChain[eye], &length);
174                 for (i = 0; i < length; i++) {
175                         ovr_GetTextureSwapChainBufferGL(session, textureSwapChain[eye], i, &chainTexId);
176                         glBindTexture(GL_TEXTURE_2D, chainTexId);
177                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
178                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
179                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
180                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
181                 }
182
183                 glGenFramebuffers(1, &(fboId[eye]));
184
185                 eyeRenderDesc[eye] = ovr_GetRenderDesc(session, (eye == 0) ? ovrEye_Left : ovrEye_Right, hmdDesc.DefaultEyeFov[eye]);
186                 hmdToEyeViewPose[eye] = eyeRenderDesc[eye].HmdToEyePose;
187
188                 layer.ColorTexture[eye]  = textureSwapChain[eye];
189                 layer.Fov[eye]           = eyeRenderDesc[eye].Fov;
190                 layer.Viewport[eye].Pos.x = 0;
191                 layer.Viewport[eye].Pos.y = 0;
192                 layer.Viewport[eye].Size.w = TextureSize[eye].w;
193                 layer.Viewport[eye].Size.h = TextureSize[eye].h;
194         }
195
196
197         return 0;
198
199 error:
200         exit_ovr();
201         return -EINVAL;
202 }
203
204 void begin_render_ovr(void)
205 {       
206         ovrResult result;
207
208         /* Get both eye poses simultaneously, with IPD offset already included. */
209         double displayMidpointSeconds = ovr_GetPredictedDisplayTime(session, 0);
210         ovrTrackingState hmdState = ovr_GetTrackingState(session, displayMidpointSeconds, ovrTrue);
211         ovr_CalcEyePoses(hmdState.HeadPose.ThePose, hmdToEyeViewPose, layer.RenderPose);
212
213         result = ovr_WaitToBeginFrame(session, frameIndex);
214         if (!OVR_SUCCESS(result))
215                 print_info("Failed to wait to begin frame (error %d)\n", result);
216         result = ovr_BeginFrame(session, frameIndex);
217         if (!OVR_SUCCESS(result))
218                 print_info("Failed to begin frame (error %d)\n", result);
219 }
220
221 void begin_render_ovr_eye(int eye)
222 {
223         int curIndex;
224         GLuint chainTexId;
225         float yaw, pitch, roll;
226         double x, y, z;
227
228         /* set render surface */
229         ovr_GetTextureSwapChainCurrentIndex(session, textureSwapChain[eye], &curIndex);
230         ovr_GetTextureSwapChainBufferGL(session, textureSwapChain[eye], curIndex, &chainTexId);
231         glBindFramebuffer(GL_FRAMEBUFFER, fboId[eye]);
232         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, chainTexId, 0);
233
234         glViewport(0, 0, TextureSize[eye].w, TextureSize[eye].h);
235         glMatrixMode(GL_PROJECTION);
236         glLoadIdentity();
237         glFrustum(
238                 -layer.Fov[eye].LeftTan,
239                 layer.Fov[eye].RightTan,
240                 layer.Fov[eye].UpTan,
241                 -layer.Fov[eye].DownTan,
242                 1.0, 5000000000.0);
243
244         ovrOrientation2yawpitchroll(layer.RenderPose[eye].Orientation, &yaw, &pitch, &roll);
245         x = layer.RenderPose[eye].Position.x;
246         y = layer.RenderPose[eye].Position.y;
247         z = layer.RenderPose[eye].Position.z;
248
249         /* normalize height to game's observer, if requrested by user */
250         observer_x = x;
251         x += observer_x_normalize;
252         observer_y = y;
253         y += observer_y_normalize;
254         observer_z = z;
255         z += observer_z_normalize;
256
257         glRotatef(-roll / M_PI * 180.0,0,0,1);
258         glRotatef(-pitch / M_PI * 180.0,1,0,0);
259         glRotatef(-yaw / M_PI * 180.0,0,1,0);
260
261         glTranslated(-x / 0.0254, -y / 0.0254, -z / 0.0254); /* convert to inch */
262
263         glMatrixMode(GL_MODELVIEW);
264
265         /* DO NOT ENABLE, since our mercenary-textures are not SRGB */
266         //glEnable(GL_FRAMEBUFFER_SRGB);
267
268 }
269
270 void end_render_ovr_eye(int eye)
271 {
272         // unset render surface
273         glBindFramebuffer(GL_FRAMEBUFFER, fboId[eye]);
274         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
275         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
276         glBindFramebuffer(GL_FRAMEBUFFER, 0);
277
278         // Commit the changes to the texture swap chain
279         ovr_CommitTextureSwapChain(session, textureSwapChain[eye]);
280 }
281
282 void end_render_ovr(void)
283 {
284         ovrResult result;
285
286         const ovrLayerHeader *layers[] = { &layer.Header };
287         result = ovr_EndFrame(session, frameIndex, NULL, layers, 1);
288         if (!OVR_SUCCESS(result))
289                 print_info("Failed to submit frame (error %d)\n", result);
290         frameIndex++;
291 }
292
293 void render_mirror_ovr(int view_width, int view_height)
294 {
295         int view_x = 0, view_y = 0;
296         int new_width, new_height;
297         int eye = 0; /* left eye */
298         int curIndex;
299         GLuint chainTexId;
300
301         /* avoid division by zero, if one dimension is too small */
302         if (view_width < 1 || view_height < 1)
303                 return;
304
305         /* calculate a viewport that has apect of TextureSize */
306         if (view_height * TextureSize[eye].w > view_width * TextureSize[eye].h) {
307                 new_height = view_width * TextureSize[eye].h / TextureSize[eye].w;
308                 view_y = view_y + view_height / 2 - new_height / 2;
309                 view_height = new_height;
310         } else if (view_height * TextureSize[eye].w < view_width * TextureSize[eye].h) {
311                 new_width = view_height * TextureSize[eye].w / TextureSize[eye].h;
312                 view_x = view_x + view_width / 2 - new_width / 2;
313                 view_width = new_width;
314         }
315
316         /* avoid views that are too small */
317         if (view_width < 1 || view_height < 1)
318                 return;
319
320         /* clear screen */
321         glClearColor(0.0, 0.0, 0.0, 1.0);
322         glClear(GL_COLOR_BUFFER_BIT);
323
324         /* orthogonal viewport */
325         glViewport((GLsizei)view_x, (GLsizei)view_y, (GLsizei)view_width, (GLsizei)view_height);
326         glMatrixMode(GL_PROJECTION);
327         glLoadIdentity();
328         glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
329         glMatrixMode(GL_MODELVIEW);
330
331         /* get texture from OVR */
332         ovr_GetTextureSwapChainCurrentIndex(session, textureSwapChain[eye], &curIndex);
333         ovr_GetTextureSwapChainBufferGL(session, textureSwapChain[eye], curIndex, &chainTexId);
334
335         /* render mirror from texture */
336         glEnable(GL_TEXTURE_2D);
337         glBindTexture(GL_TEXTURE_2D, chainTexId);
338         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);  /* no modulation with color */
339         glBegin(GL_QUADS);
340         glTexCoord2f(0, 1);
341         glVertex3f(-1.0, -1.0, 0.0);
342         glTexCoord2f(1, 1);
343         glVertex3f(1.0, -1.0, 0.0);
344         glTexCoord2f(1, 0);
345         glVertex3f(1.0, 1.0, 0.0);
346         glTexCoord2f(0, 0);
347         glVertex3f(-1.0, 1.0, 0.0);
348         glEnd();
349         glDisable(GL_TEXTURE_2D);
350 }
351
352 void normalize_observer_ovr(void)
353 {
354         observer_x_normalize = -observer_x;
355         observer_y_normalize = -observer_y;
356         observer_z_normalize = -observer_z;
357 }
358
359 void exit_ovr(void)
360 {
361         int eye;
362
363         /* destroy render buffers */
364         for (eye = 0; eye < 2; eye++) {
365                 if (textureSwapChain[eye]) {
366                         ovr_DestroyTextureSwapChain(session, textureSwapChain[eye]);
367                         textureSwapChain[eye] = NULL;
368                 }
369                 if (fboId[eye]) {
370                         glDeleteFramebuffers(1, &fboId[eye]);
371                         fboId[eye] = 0;
372                 }
373         }
374
375         if (session) {
376                 ovr_Destroy(session);
377                 session = NULL;
378         }
379         if (ovr_initialized) {
380                 ovr_Shutdown();
381                 ovr_initialized = 0;
382         }
383 }
384