3 * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
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.
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.
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/>.
19 * Based on example code:
21 * Content : Simple minimal VR demo
22 * Created : December 1, 2014
24 * Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
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
30 * http://www.apache.org/licenses/LICENSE-2.0
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.
44 #include "../libsdl/print.h"
45 #include "../libopengl/opengl.h"
49 #include <OVR_CAPI_GL.h>
52 #define GL3_PROTOTYPES 1
57 #include <dxgi.h> // for GetDefaultAdapterLuid
58 #pragma comment(lib, "dxgi.lib")
61 static ovrGraphicsLuid GetDefaultAdapterLuid()
63 ovrGraphicsLuid luid = ovrGraphicsLuid();
66 IDXGIFactory* factory = nullptr;
68 if (SUCCEEDED(CreateDXGIFactory(IID_PPV_ARGS(&factory)))) {
69 IDXGIAdapter* adapter = nullptr;
71 if (SUCCEEDED(factory->EnumAdapters(0, &adapter))) {
72 DXGI_ADAPTER_DESC desc;
74 adapter->GetDesc(&desc);
75 memcpy(&luid, &desc.AdapterLuid, sizeof(luid));
86 static int Compare(const ovrGraphicsLuid lhs, const ovrGraphicsLuid rhs)
88 return memcmp(&lhs, &rhs, sizeof(ovrGraphicsLuid));
92 static int ovr_initialized = 0;
93 static ovrSession session = NULL;
94 static ovrGraphicsLuid luid;
95 static ovrHmdDesc hmdDesc;
96 static ovrSizei TextureSize[2];
97 static ovrTextureSwapChain textureSwapChain[2] = { NULL, NULL };
98 static GLuint fboId[2] = { 0, 0 };
99 static GLuint mirrorFBO = 0;
100 static int mirror_width;
101 static int mirror_height;
102 static ovrEyeRenderDesc eyeRenderDesc[2];
103 static ovrPosef hmdToEyeViewPose[2];
104 static ovrPosef headPose;
105 static ovrPosef handPose;
106 static ovrInputState inputState;
107 static ovrLayerEyeFov layer;
108 static int multisampling;
109 static long long frameIndex = 0;
110 static double observer_x = 0.0;
111 static double observer_x_reset = 0.0;
112 static double hand_x_reset = 0.0;
113 static double observer_y = 0.0;
114 static double observer_y_reset = 0.0;
115 static double hand_y_reset = 0.0;
116 static double observer_z = 0.0;
117 static double observer_z_reset = 0.0;
118 static double hand_z_reset = 0.0;
120 int init_ovr(int _multisampling)
125 multisampling = _multisampling;
127 glewExperimental = GL_TRUE;
128 if (glewInit() != GLEW_OK) {
129 print_error("Failed to init GLEW\n");
133 result = ovr_Initialize(NULL);
134 if (OVR_FAILURE(result)) {
135 print_error("Failed to init OVR (is Oculus Rift service running?)\n");
140 result = ovr_Create(&session, &luid);
141 if (OVR_FAILURE(result)) {
142 print_error("Failed to create OVR session (is the HMD connected?)\n");
147 if (Compare(luid, GetDefaultAdapterLuid())) { // If luid that the Rift is on is not the default adapter LUID...
148 print_error("OpenGL supports only the default graphics adapter.\n");
153 hmdDesc = ovr_GetHmdDesc(session);
155 memset(&layer, 0, sizeof(layer));
156 layer.Header.Type = ovrLayerType_EyeFov;
157 layer.Header.Flags = 0;
159 /* do we need this??? seems to work without */
160 if (multisampling > 1)
161 glEnable(GL_MULTISAMPLE);
163 /* create render buffers */
164 for (eye = 0; eye < 2; eye++) {
165 TextureSize[eye] = ovr_GetFovTextureSize(session, (eye == 0) ? ovrEye_Left : ovrEye_Right, hmdDesc.DefaultEyeFov[eye], 1.0);
166 ovrTextureSwapChainDesc desc;
170 memset(&desc, 0, sizeof(desc));
171 desc.Type = ovrTexture_2D;
173 desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
174 desc.Width = TextureSize[eye].w;
175 desc.Height = TextureSize[eye].h;
177 desc.SampleCount = (multisampling > 1) ? multisampling : 1;
178 desc.StaticImage = ovrFalse;
180 result = ovr_CreateTextureSwapChainGL(session, &desc, &textureSwapChain[eye]);
181 if (OVR_FAILURE(result)) {
182 print_error("ovr_CreateTextureSwapChainGL() failed! (error %d)\n", result);
186 ovr_GetTextureSwapChainLength(session, textureSwapChain[eye], &length);
187 for (i = 0; i < length; i++) {
188 ovr_GetTextureSwapChainBufferGL(session, textureSwapChain[eye], i, &chainTexId);
189 glBindTexture((multisampling > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, chainTexId);
190 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
191 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
192 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
193 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
196 glGenFramebuffers(1, &(fboId[eye]));
198 eyeRenderDesc[eye] = ovr_GetRenderDesc(session, (eye == 0) ? ovrEye_Left : ovrEye_Right, hmdDesc.DefaultEyeFov[eye]);
199 hmdToEyeViewPose[eye] = eyeRenderDesc[eye].HmdToEyePose;
201 layer.ColorTexture[eye] = textureSwapChain[eye];
202 layer.Fov[eye] = eyeRenderDesc[eye].Fov;
203 layer.Viewport[eye].Pos.x = 0;
204 layer.Viewport[eye].Pos.y = 0;
205 layer.Viewport[eye].Size.w = TextureSize[eye].w;
206 layer.Viewport[eye].Size.h = TextureSize[eye].h;
209 ovrMirrorTexture mirrorTexture = NULL;
210 ovrMirrorTextureDesc desc;
211 memset(&desc, 0, sizeof(desc));
212 desc.Width = mirror_width = TextureSize[0].w;
213 desc.Height = mirror_height = TextureSize[0].h;
214 desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
216 // ovrMirrorOption_PostDistortion |
217 ovrMirrorOption_LeftEyeOnly |
218 ovrMirrorOption_IncludeGuardian |
219 ovrMirrorOption_IncludeNotifications |
220 ovrMirrorOption_IncludeSystemGui;
222 /* Create mirror texture and an FBO used to copy mirror texture to back buffer */
223 result = ovr_CreateMirrorTextureWithOptionsGL(session, &desc, &mirrorTexture);
224 if (!OVR_SUCCESS(result)) {
225 print_error("ovr_CreateMirrorTextureWithOptionsGL() failed! (error %d)\n", result);
229 /* Configure the mirror read buffer */
231 ovr_GetMirrorTextureBufferGL(session, mirrorTexture, &texId);
233 glGenFramebuffers(1, &mirrorFBO);
234 glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFBO);
235 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0);
236 glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
245 void get_poses_ovr(int *button_a, int *button_b, int *button_x, int *button_y, int *button_menu, int *button_left_trigger, int *button_right_trigger, int *button_left_thumb, int *button_right_thumb, double *hand_right_x, double *hand_right_y, double *hand_right_z, double *hand_right_yaw, double *hand_right_pitch, double *hand_right_roll, double *stick_left_x, double *stick_left_y, double *stick_right_x, double *stick_right_y, double *head_yaw, double *head_pitch, double *head_roll)
248 float yaw, pitch, roll;
250 unsigned int hand_mask = (ovrStatus_OrientationTracked | ovrStatus_PositionTracked);
251 unsigned int hand_flags = (ovrStatus_OrientationTracked | ovrStatus_PositionTracked);
253 /* Get both eye poses simultaneously, with IPD offset already included. */
254 double displayMidpointSeconds = ovr_GetPredictedDisplayTime(session, frameIndex);
255 ovrTrackingState hmdState = ovr_GetTrackingState(session, displayMidpointSeconds, ovrTrue);
256 ovr_CalcEyePoses(hmdState.HeadPose.ThePose, hmdToEyeViewPose, layer.RenderPose);
257 /* Grab hand poses useful for rendering head/hand or controller representation */
258 headPose = hmdState.HeadPose.ThePose;
260 // if ((hmdState.HandStatusFlags[ovrHand_Left] & hand_mask) == hand_flags)
261 // handPose = hmdState.HandPoses[ovrHand_Left].ThePose;
262 if ((hmdState.HandStatusFlags[ovrHand_Right] & hand_mask) == hand_flags)
263 handPose = hmdState.HandPoses[ovrHand_Right].ThePose;
264 x = handPose.Position.x;
265 y = handPose.Position.y;
266 z = handPose.Position.z;
270 *hand_right_x = x / 0.0254;
271 *hand_right_y = y / 0.0254;
272 *hand_right_z = z / 0.0254;
273 ovrOrientation2yawpitchroll(headPose.Orientation, &yaw, &pitch, &roll);
277 ovrOrientation2yawpitchroll(handPose.Orientation, &yaw, &pitch, &roll);
278 *hand_right_yaw = yaw;
279 *hand_right_pitch = pitch;
280 *hand_right_roll = roll;
282 result = ovr_GetInputState(session, ovrControllerType_Active, &inputState);
283 if (OVR_SUCCESS(result)) {
284 if (inputState.Buttons & ovrButton_A)
288 if (inputState.Buttons & ovrButton_B)
292 if (inputState.Buttons & ovrButton_X)
296 if (inputState.Buttons & ovrButton_Y)
300 if (inputState.Buttons & ovrButton_Enter)
304 if (inputState.Buttons & ovrButton_LThumb)
305 *button_left_thumb = 1;
307 *button_left_thumb = 0;
308 if (inputState.Buttons & ovrButton_RThumb)
309 *button_right_thumb = 1;
311 *button_right_thumb = 0;
312 if (inputState.IndexTrigger[ovrHand_Left] >= 0.8)
313 *button_left_trigger = 1;
315 *button_left_trigger = 0;
316 if (inputState.IndexTrigger[ovrHand_Right] >= 0.8)
317 *button_right_trigger = 1;
319 *button_right_trigger = 0;
320 *stick_left_x = inputState.ThumbstickNoDeadzone[ovrHand_Left].x;
321 *stick_left_y = inputState.ThumbstickNoDeadzone[ovrHand_Left].y;
322 *stick_right_x = inputState.ThumbstickNoDeadzone[ovrHand_Right].x;
323 *stick_right_y = inputState.ThumbstickNoDeadzone[ovrHand_Right].y;
327 void begin_render_ovr(void)
331 result = ovr_WaitToBeginFrame(session, frameIndex);
332 if (!OVR_SUCCESS(result))
333 print_info("Failed to wait to begin frame (error %d)\n", result);
334 result = ovr_BeginFrame(session, frameIndex);
335 if (!OVR_SUCCESS(result))
336 print_info("Failed to begin frame (error %d)\n", result);
339 static int initial_observer_reset = 1;
341 void begin_render_ovr_eye(int eye, double *camera_x, double *camera_y, double *camera_z, int *width, int *height)
345 float yaw, pitch, roll;
348 /* set render surface */
349 ovr_GetTextureSwapChainCurrentIndex(session, textureSwapChain[eye], &curIndex);
350 ovr_GetTextureSwapChainBufferGL(session, textureSwapChain[eye], curIndex, &chainTexId);
351 glBindFramebuffer(GL_FRAMEBUFFER, fboId[eye]);
352 current_framebuffer = fboId[eye];
353 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, (multisampling > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, chainTexId, 0);
355 glViewport(0, 0, TextureSize[eye].w, TextureSize[eye].h);
356 *width = TextureSize[eye].w;
357 *height = TextureSize[eye].h;
358 glMatrixMode(GL_PROJECTION);
361 -layer.Fov[eye].LeftTan,
362 layer.Fov[eye].RightTan,
363 layer.Fov[eye].UpTan,
364 -layer.Fov[eye].DownTan,
367 ovrOrientation2yawpitchroll(layer.RenderPose[eye].Orientation, &yaw, &pitch, &roll);
368 x = layer.RenderPose[eye].Position.x;
369 y = layer.RenderPose[eye].Position.y;
370 z = layer.RenderPose[eye].Position.z;
372 /* reset to game's observer, if requrested by user */
376 if (initial_observer_reset) {
377 initial_observer_reset = 0;
378 reset_observer_ovr();
380 x += observer_x_reset;
381 y += observer_y_reset;
382 z += observer_z_reset;
384 glRotatef(-roll / M_PI * 180.0,0,0,1);
385 glRotatef(-pitch / M_PI * 180.0,1,0,0);
386 glRotatef(-yaw / M_PI * 180.0,0,1,0);
388 *camera_x = x / 0.0254;
389 *camera_y = y / 0.0254;
390 *camera_z = z / 0.0254;
391 glTranslated(-(*camera_x), -(*camera_y), -(*camera_z)); /* convert to inch */
393 glMatrixMode(GL_MODELVIEW);
395 /* DO NOT ENABLE, since our mercenary-textures are not SRGB */
396 //glEnable(GL_FRAMEBUFFER_SRGB);
400 void end_render_ovr_eye(int eye)
402 // unset render surface
403 glBindFramebuffer(GL_FRAMEBUFFER, fboId[eye]);
404 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, (multisampling > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, 0, 0);
405 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, (multisampling > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, 0, 0);
406 glBindFramebuffer(GL_FRAMEBUFFER, 0);
407 current_framebuffer = 0;
409 // Commit the changes to the texture swap chain
410 ovr_CommitTextureSwapChain(session, textureSwapChain[eye]);
413 void end_render_ovr(void)
417 const ovrLayerHeader *layers[] = { &layer.Header };
418 result = ovr_EndFrame(session, frameIndex, NULL, layers, 1);
419 if (!OVR_SUCCESS(result))
420 print_info("Failed to submit frame (error %d)\n", result);
424 void render_mirror_ovr(int view_width, int view_height)
426 int view_x = 0, view_y = 0;
427 int new_height; //, new_width;
429 /* avoid division by zero, if one dimension is too small */
430 if (view_width < 1 || view_height < 1)
433 /* calculate a viewport that has apect of TextureSize */
434 // if (view_height * mirror_width > view_width * mirror_height) {
435 new_height = view_width * mirror_height / mirror_width;
436 view_y = view_y + view_height / 2 - new_height / 2;
437 view_height = new_height;
438 // } else if (view_height * mirror_width < view_width * mirror_height) {
439 // new_width = view_height * mirror_width / mirror_height;
440 // view_x = view_x + view_width / 2 - new_width / 2;
441 // view_width = new_width;
444 /* avoid views that are too small */
445 if (view_width < 1 || view_height < 1)
448 glBindFramebuffer(GL_FRAMEBUFFER, 0);
450 glClearColor(0.0, 0.0, 0.0, 1.0);
451 glClear(GL_COLOR_BUFFER_BIT);
453 /* Blit mirror texture to back buffer */
454 glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFBO);
455 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
456 glBlitFramebuffer(0, mirror_height, mirror_width, 0,
457 view_x, view_y, view_x + view_width, view_y + view_height,
458 GL_COLOR_BUFFER_BIT, GL_LINEAR);
459 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
462 void reset_observer_ovr(void)
464 observer_x_reset = -observer_x;
465 observer_y_reset = -observer_y;
466 observer_z_reset = -observer_z;
467 hand_x_reset = -observer_x;
468 hand_y_reset = -observer_y;
469 hand_z_reset = -observer_z;
472 int should_quit_ovr(void)
474 ovrSessionStatus sessionStatus;
477 result = ovr_GetSessionStatus(session, &sessionStatus);
479 if (OVR_SUCCESS(result))
480 return sessionStatus.ShouldQuit;
488 /* destroy render buffers */
489 for (eye = 0; eye < 2; eye++) {
490 if (textureSwapChain[eye]) {
491 ovr_DestroyTextureSwapChain(session, textureSwapChain[eye]);
492 textureSwapChain[eye] = NULL;
495 glDeleteFramebuffers(1, &fboId[eye]);
500 glDeleteFramebuffers(1, &mirrorFBO);
505 ovr_Destroy(session);
508 if (ovr_initialized) {