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"
48 #include <OVR_CAPI_GL.h>
51 #define GL3_PROTOTYPES 1
56 #include <dxgi.h> // for GetDefaultAdapterLuid
57 #pragma comment(lib, "dxgi.lib")
60 static ovrGraphicsLuid GetDefaultAdapterLuid()
62 ovrGraphicsLuid luid = ovrGraphicsLuid();
65 IDXGIFactory* factory = nullptr;
67 if (SUCCEEDED(CreateDXGIFactory(IID_PPV_ARGS(&factory)))) {
68 IDXGIAdapter* adapter = nullptr;
70 if (SUCCEEDED(factory->EnumAdapters(0, &adapter))) {
71 DXGI_ADAPTER_DESC desc;
73 adapter->GetDesc(&desc);
74 memcpy(&luid, &desc.AdapterLuid, sizeof(luid));
85 static int Compare(const ovrGraphicsLuid lhs, const ovrGraphicsLuid rhs)
87 return memcmp(&lhs, &rhs, sizeof(ovrGraphicsLuid));
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 GLuint mirrorFBO = 0;
99 static int mirror_width;
100 static int mirror_height;
101 static ovrEyeRenderDesc eyeRenderDesc[2];
102 static ovrPosef hmdToEyeViewPose[2];
103 static ovrPosef headPose;
104 static ovrPosef handPoses[2];
105 static ovrInputState inputState;
106 static ovrLayerEyeFov layer;
107 static int multisampling;
108 static long long frameIndex = 0;
109 static double observer_x = 0.0;
110 static double observer_x_reset = 0.0;
111 static double hand_x_reset = 0.0;
112 static double observer_y = 0.0;
113 static double observer_y_reset = 0.0;
114 static double hand_y_reset = 0.0;
115 static double observer_z = 0.0;
116 static double observer_z_reset = 0.0;
117 static double hand_z_reset = 0.0;
119 int init_ovr(int _multisampling)
124 multisampling = _multisampling;
126 glewExperimental = GL_TRUE;
127 if (glewInit() != GLEW_OK) {
128 print_error("Failed to init GLEW\n");
132 result = ovr_Initialize(NULL);
133 if (OVR_FAILURE(result)) {
134 print_error("Failed to init OVR (is Oculus Rift service running?)\n");
139 result = ovr_Create(&session, &luid);
140 if (OVR_FAILURE(result)) {
141 print_error("Failed to create OVR session (is the HMD connected?)\n");
146 if (Compare(luid, GetDefaultAdapterLuid())) { // If luid that the Rift is on is not the default adapter LUID...
147 print_error("OpenGL supports only the default graphics adapter.\n");
152 hmdDesc = ovr_GetHmdDesc(session);
154 memset(&layer, 0, sizeof(layer));
155 layer.Header.Type = ovrLayerType_EyeFov;
156 layer.Header.Flags = 0;
158 /* do we need this??? seems to work without */
159 if (multisampling > 1)
160 glEnable(GL_MULTISAMPLE);
162 /* create render buffers */
163 for (eye = 0; eye < 2; eye++) {
164 TextureSize[eye] = ovr_GetFovTextureSize(session, (eye == 0) ? ovrEye_Left : ovrEye_Right, hmdDesc.DefaultEyeFov[eye], 1.0);
165 ovrTextureSwapChainDesc desc;
169 memset(&desc, 0, sizeof(desc));
170 desc.Type = ovrTexture_2D;
172 desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
173 desc.Width = TextureSize[eye].w;
174 desc.Height = TextureSize[eye].h;
176 desc.SampleCount = (multisampling > 1) ? multisampling : 1;
177 desc.StaticImage = ovrFalse;
179 result = ovr_CreateTextureSwapChainGL(session, &desc, &textureSwapChain[eye]);
180 if (OVR_FAILURE(result)) {
181 print_error("ovr_CreateTextureSwapChainGL() failed! (error %d)\n", result);
185 ovr_GetTextureSwapChainLength(session, textureSwapChain[eye], &length);
186 for (i = 0; i < length; i++) {
187 ovr_GetTextureSwapChainBufferGL(session, textureSwapChain[eye], i, &chainTexId);
188 glBindTexture((multisampling > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, chainTexId);
189 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
190 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
191 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
192 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
195 glGenFramebuffers(1, &(fboId[eye]));
197 eyeRenderDesc[eye] = ovr_GetRenderDesc(session, (eye == 0) ? ovrEye_Left : ovrEye_Right, hmdDesc.DefaultEyeFov[eye]);
198 hmdToEyeViewPose[eye] = eyeRenderDesc[eye].HmdToEyePose;
200 layer.ColorTexture[eye] = textureSwapChain[eye];
201 layer.Fov[eye] = eyeRenderDesc[eye].Fov;
202 layer.Viewport[eye].Pos.x = 0;
203 layer.Viewport[eye].Pos.y = 0;
204 layer.Viewport[eye].Size.w = TextureSize[eye].w;
205 layer.Viewport[eye].Size.h = TextureSize[eye].h;
208 ovrMirrorTexture mirrorTexture = NULL;
209 ovrMirrorTextureDesc desc;
210 memset(&desc, 0, sizeof(desc));
211 desc.Width = mirror_width = TextureSize[0].w;
212 desc.Height = mirror_height = TextureSize[0].h / 2;
213 desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;
215 ovrMirrorOption_PostDistortion |
216 ovrMirrorOption_IncludeGuardian |
217 ovrMirrorOption_IncludeNotifications |
218 ovrMirrorOption_IncludeSystemGui;
220 /* Create mirror texture and an FBO used to copy mirror texture to back buffer */
221 result = ovr_CreateMirrorTextureWithOptionsGL(session, &desc, &mirrorTexture);
222 if (!OVR_SUCCESS(result)) {
223 print_error("ovr_CreateMirrorTextureWithOptionsGL() failed! (error %d)\n", result);
227 /* Configure the mirror read buffer */
229 ovr_GetMirrorTextureBufferGL(session, mirrorTexture, &texId);
231 glGenFramebuffers(1, &mirrorFBO);
232 glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFBO);
233 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0);
234 glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
243 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)
246 float yaw, pitch, roll;
249 /* Get both eye poses simultaneously, with IPD offset already included. */
250 double displayMidpointSeconds = ovr_GetPredictedDisplayTime(session, frameIndex);
251 ovrTrackingState hmdState = ovr_GetTrackingState(session, displayMidpointSeconds, ovrTrue);
252 ovr_CalcEyePoses(hmdState.HeadPose.ThePose, hmdToEyeViewPose, layer.RenderPose);
253 /* Grab hand poses useful for rendering head/hand or controller representation */
254 headPose = hmdState.HeadPose.ThePose;
255 if (hmdState.HandStatusFlags[ovrHand_Left] == (ovrStatus_OrientationTracked | ovrStatus_PositionTracked))
256 handPoses[ovrHand_Left] = hmdState.HandPoses[ovrHand_Left].ThePose;
258 handPoses[ovrHand_Left] = headPose;
259 if (hmdState.HandStatusFlags[ovrHand_Right] == (ovrStatus_OrientationTracked | ovrStatus_PositionTracked))
260 handPoses[ovrHand_Right] = hmdState.HandPoses[ovrHand_Right].ThePose;
262 handPoses[ovrHand_Right] = headPose;
263 x = handPoses[ovrHand_Right].Position.x;
264 y = handPoses[ovrHand_Right].Position.y;
265 z = handPoses[ovrHand_Right].Position.z;
269 *hand_right_x = x / 0.0254;
270 *hand_right_y = y / 0.0254;
271 *hand_right_z = z / 0.0254;
272 ovrOrientation2yawpitchroll(headPose.Orientation, &yaw, &pitch, &roll);
276 ovrOrientation2yawpitchroll(handPoses[ovrHand_Right].Orientation, &yaw, &pitch, &roll);
277 *hand_right_yaw = yaw;
278 *hand_right_pitch = pitch;
279 *hand_right_roll = roll;
281 result = ovr_GetInputState(session, ovrControllerType_Active, &inputState);
282 if (OVR_SUCCESS(result)) {
283 if (inputState.Buttons & ovrButton_A)
287 if (inputState.Buttons & ovrButton_B)
291 if (inputState.Buttons & ovrButton_X)
295 if (inputState.Buttons & ovrButton_Y)
299 if (inputState.Buttons & ovrButton_Enter)
303 if (inputState.Buttons & ovrButton_LThumb)
304 *button_left_thumb = 1;
306 *button_left_thumb = 0;
307 if (inputState.Buttons & ovrButton_RThumb)
308 *button_right_thumb = 1;
310 *button_right_thumb = 0;
311 if (inputState.IndexTrigger[ovrHand_Left] >= 0.8)
312 *button_left_trigger = 1;
314 *button_left_trigger = 0;
315 if (inputState.IndexTrigger[ovrHand_Right] >= 0.8)
316 *button_right_trigger = 1;
318 *button_right_trigger = 0;
319 *stick_left_x = inputState.ThumbstickNoDeadzone[ovrHand_Left].x;
320 *stick_left_y = inputState.ThumbstickNoDeadzone[ovrHand_Left].y;
321 *stick_right_x = inputState.ThumbstickNoDeadzone[ovrHand_Right].x;
322 *stick_right_y = inputState.ThumbstickNoDeadzone[ovrHand_Right].y;
326 void begin_render_ovr(void)
330 result = ovr_WaitToBeginFrame(session, frameIndex);
331 if (!OVR_SUCCESS(result))
332 print_info("Failed to wait to begin frame (error %d)\n", result);
333 result = ovr_BeginFrame(session, frameIndex);
334 if (!OVR_SUCCESS(result))
335 print_info("Failed to begin frame (error %d)\n", result);
338 void begin_render_ovr_eye(int eye, double *camera_x, double *camera_y, double *camera_z)
342 float yaw, pitch, roll;
345 /* set render surface */
346 ovr_GetTextureSwapChainCurrentIndex(session, textureSwapChain[eye], &curIndex);
347 ovr_GetTextureSwapChainBufferGL(session, textureSwapChain[eye], curIndex, &chainTexId);
348 glBindFramebuffer(GL_FRAMEBUFFER, fboId[eye]);
349 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, (multisampling > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, chainTexId, 0);
351 glViewport(0, 0, TextureSize[eye].w, TextureSize[eye].h);
352 glMatrixMode(GL_PROJECTION);
355 -layer.Fov[eye].LeftTan,
356 layer.Fov[eye].RightTan,
357 layer.Fov[eye].UpTan,
358 -layer.Fov[eye].DownTan,
361 ovrOrientation2yawpitchroll(layer.RenderPose[eye].Orientation, &yaw, &pitch, &roll);
362 x = layer.RenderPose[eye].Position.x;
363 y = layer.RenderPose[eye].Position.y;
364 z = layer.RenderPose[eye].Position.z;
366 /* reset to game's observer, if requrested by user */
368 x += observer_x_reset;
370 y += observer_y_reset;
372 z += observer_z_reset;
374 glRotatef(-roll / M_PI * 180.0,0,0,1);
375 glRotatef(-pitch / M_PI * 180.0,1,0,0);
376 glRotatef(-yaw / M_PI * 180.0,0,1,0);
378 *camera_x = x / 0.0254;
379 *camera_y = y / 0.0254;
380 *camera_z = z / 0.0254;
381 glTranslated(-(*camera_x), -(*camera_y), -(*camera_z)); /* convert to inch */
383 glMatrixMode(GL_MODELVIEW);
385 /* DO NOT ENABLE, since our mercenary-textures are not SRGB */
386 //glEnable(GL_FRAMEBUFFER_SRGB);
390 void end_render_ovr_eye(int eye)
392 // unset render surface
393 glBindFramebuffer(GL_FRAMEBUFFER, fboId[eye]);
394 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, (multisampling > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, 0, 0);
395 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, (multisampling > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D, 0, 0);
396 glBindFramebuffer(GL_FRAMEBUFFER, 0);
398 // Commit the changes to the texture swap chain
399 ovr_CommitTextureSwapChain(session, textureSwapChain[eye]);
402 void end_render_ovr(void)
406 const ovrLayerHeader *layers[] = { &layer.Header };
407 result = ovr_EndFrame(session, frameIndex, NULL, layers, 1);
408 if (!OVR_SUCCESS(result))
409 print_info("Failed to submit frame (error %d)\n", result);
413 void render_mirror_ovr(int view_width, int view_height)
415 int view_x = 0, view_y = 0;
416 int new_width, new_height;
418 /* avoid division by zero, if one dimension is too small */
419 if (view_width < 1 || view_height < 1)
422 /* calculate a viewport that has apect of TextureSize */
423 if (view_height * mirror_width > view_width * mirror_height) {
424 new_height = view_width * mirror_height / mirror_width;
425 view_y = view_y + view_height / 2 - new_height / 2;
426 view_height = new_height;
427 } else if (view_height * mirror_width < view_width * mirror_height) {
428 new_width = view_height * mirror_width / mirror_height;
429 view_x = view_x + view_width / 2 - new_width / 2;
430 view_width = new_width;
433 /* avoid views that are too small */
434 if (view_width < 1 || view_height < 1)
437 glBindFramebuffer(GL_FRAMEBUFFER, 0);
439 glClearColor(0.0, 0.0, 0.0, 1.0);
440 glClear(GL_COLOR_BUFFER_BIT);
442 /* Blit mirror texture to back buffer */
443 glBindFramebuffer(GL_READ_FRAMEBUFFER, mirrorFBO);
444 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
445 glBlitFramebuffer(0, mirror_height, mirror_width, 0,
446 view_x, view_y, view_x + view_width, view_y + view_height,
447 GL_COLOR_BUFFER_BIT, GL_NEAREST);
448 glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
451 void reset_observer_ovr(void)
453 observer_x_reset = -observer_x;
454 observer_y_reset = -observer_y;
455 observer_z_reset = -observer_z;
456 hand_x_reset = -observer_x;
457 hand_y_reset = -observer_y;
458 hand_z_reset = -observer_z;
465 /* destroy render buffers */
466 for (eye = 0; eye < 2; eye++) {
467 if (textureSwapChain[eye]) {
468 ovr_DestroyTextureSwapChain(session, textureSwapChain[eye]);
469 textureSwapChain[eye] = NULL;
472 glDeleteFramebuffers(1, &fboId[eye]);
477 glDeleteFramebuffers(1, &mirrorFBO);
482 ovr_Destroy(session);
485 if (ovr_initialized) {