2022-07-07 19:30:06 +02:00
|
|
|
#include "VRFramebuffer.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <sys/prctl.h>
|
|
|
|
#include <assert.h>
|
2022-07-08 14:44:20 +02:00
|
|
|
#include <GLES3/gl3.h>
|
|
|
|
#include <GLES3/gl3ext.h>
|
2022-07-07 19:30:06 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrFramebuffer
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) {
|
2022-07-24 14:25:04 +02:00
|
|
|
frameBuffer->Width = 0;
|
|
|
|
frameBuffer->Height = 0;
|
|
|
|
frameBuffer->TextureSwapChainLength = 0;
|
|
|
|
frameBuffer->TextureSwapChainIndex = 0;
|
|
|
|
frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE;
|
|
|
|
frameBuffer->ColorSwapChain.Width = 0;
|
|
|
|
frameBuffer->ColorSwapChain.Height = 0;
|
|
|
|
frameBuffer->ColorSwapChainImage = NULL;
|
|
|
|
frameBuffer->DepthBuffers = NULL;
|
|
|
|
frameBuffer->FrameBuffers = NULL;
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ovrFramebuffer_Create(
|
2022-07-24 14:25:04 +02:00
|
|
|
XrSession session,
|
|
|
|
ovrFramebuffer* frameBuffer,
|
|
|
|
const int width,
|
|
|
|
const int height) {
|
|
|
|
|
|
|
|
frameBuffer->Width = width;
|
|
|
|
frameBuffer->Height = height;
|
|
|
|
|
|
|
|
XrSwapchainCreateInfo swapChainCreateInfo;
|
|
|
|
memset(&swapChainCreateInfo, 0, sizeof(swapChainCreateInfo));
|
|
|
|
swapChainCreateInfo.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
|
|
|
|
swapChainCreateInfo.usageFlags = XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
|
|
|
|
swapChainCreateInfo.format = GL_RGBA8;
|
|
|
|
swapChainCreateInfo.sampleCount = 1;
|
|
|
|
swapChainCreateInfo.width = width;
|
|
|
|
swapChainCreateInfo.height = height;
|
|
|
|
swapChainCreateInfo.faceCount = 1;
|
|
|
|
swapChainCreateInfo.arraySize = 1;
|
|
|
|
swapChainCreateInfo.mipCount = 1;
|
|
|
|
|
|
|
|
frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width;
|
|
|
|
frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height;
|
|
|
|
|
|
|
|
// Create the swapchain.
|
|
|
|
OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle));
|
|
|
|
// Get the number of swapchain images.
|
|
|
|
OXR(xrEnumerateSwapchainImages(
|
|
|
|
frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL));
|
|
|
|
// Allocate the swapchain images array.
|
|
|
|
frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc(
|
|
|
|
frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR));
|
|
|
|
|
|
|
|
// Populate the swapchain image array.
|
|
|
|
for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) {
|
|
|
|
frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR;
|
|
|
|
frameBuffer->ColorSwapChainImage[i].next = NULL;
|
|
|
|
}
|
|
|
|
OXR(xrEnumerateSwapchainImages(
|
|
|
|
frameBuffer->ColorSwapChain.Handle,
|
|
|
|
frameBuffer->TextureSwapChainLength,
|
|
|
|
&frameBuffer->TextureSwapChainLength,
|
|
|
|
(XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage));
|
|
|
|
|
|
|
|
frameBuffer->DepthBuffers =
|
|
|
|
(GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
|
|
|
|
frameBuffer->FrameBuffers =
|
|
|
|
(GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint));
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) {
|
|
|
|
// Create the color buffer texture.
|
|
|
|
const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image;
|
|
|
|
GLenum colorTextureTarget = GL_TEXTURE_2D;
|
|
|
|
GL(glBindTexture(colorTextureTarget, colorTexture));
|
|
|
|
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
|
|
|
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
|
|
|
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
|
|
|
GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
|
|
|
GL(glBindTexture(colorTextureTarget, 0));
|
|
|
|
|
|
|
|
// Create depth buffer.
|
|
|
|
GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height));
|
|
|
|
GL(glBindRenderbuffer(GL_RENDERBUFFER, 0));
|
|
|
|
|
|
|
|
// Create the frame buffer.
|
|
|
|
GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i]));
|
|
|
|
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i]));
|
|
|
|
GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, frameBuffer->DepthBuffers[i]));
|
|
|
|
GL(glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0));
|
|
|
|
GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER));
|
|
|
|
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
|
|
|
if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) {
|
|
|
|
ALOGE(
|
|
|
|
"Incomplete frame buffer object: %d", renderFramebufferStatus);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) {
|
2022-07-24 14:25:04 +02:00
|
|
|
GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers));
|
|
|
|
GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers));
|
|
|
|
OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle));
|
|
|
|
free(frameBuffer->ColorSwapChainImage);
|
2022-07-07 19:30:06 +02:00
|
|
|
|
2022-07-24 14:25:04 +02:00
|
|
|
free(frameBuffer->DepthBuffers);
|
|
|
|
free(frameBuffer->FrameBuffers);
|
2022-07-07 19:30:06 +02:00
|
|
|
|
2022-07-24 14:25:04 +02:00
|
|
|
ovrFramebuffer_Clear(frameBuffer);
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) {
|
2022-07-24 14:25:04 +02:00
|
|
|
GL(glBindFramebuffer(
|
|
|
|
GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex]));
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_SetNone() {
|
2022-07-24 14:25:04 +02:00
|
|
|
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) {
|
2022-07-24 14:25:04 +02:00
|
|
|
// Discard the depth buffer, so the tiler won't need to write it back out to memory.
|
|
|
|
const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT};
|
|
|
|
glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment);
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) {
|
2022-07-24 14:25:04 +02:00
|
|
|
// Acquire the swapchain image
|
|
|
|
XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL};
|
|
|
|
OXR(xrAcquireSwapchainImage(
|
|
|
|
frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex));
|
|
|
|
|
|
|
|
XrSwapchainImageWaitInfo waitInfo;
|
|
|
|
waitInfo.type = XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO;
|
|
|
|
waitInfo.next = NULL;
|
|
|
|
waitInfo.timeout = 1000; /* timeout in nanoseconds */
|
|
|
|
XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo);
|
|
|
|
int i = 0;
|
|
|
|
while (res != XR_SUCCESS) {
|
|
|
|
res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo);
|
|
|
|
i++;
|
|
|
|
ALOGV(
|
|
|
|
" Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f micro seconds)",
|
|
|
|
i,
|
|
|
|
waitInfo.timeout * (1E-9));
|
|
|
|
}
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) {
|
2022-07-24 14:25:04 +02:00
|
|
|
XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL};
|
|
|
|
OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo));
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrRenderer
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void ovrRenderer_Clear(ovrRenderer* renderer) {
|
2022-07-24 14:25:04 +02:00
|
|
|
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
|
|
|
ovrFramebuffer_Clear(&renderer->FrameBuffer[eye]);
|
|
|
|
}
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrRenderer_Create(
|
2022-07-24 14:25:04 +02:00
|
|
|
XrSession session,
|
|
|
|
ovrRenderer* renderer,
|
|
|
|
int suggestedEyeTextureWidth,
|
|
|
|
int suggestedEyeTextureHeight) {
|
|
|
|
// Create the frame buffers.
|
|
|
|
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
|
|
|
ovrFramebuffer_Create(
|
|
|
|
session,
|
|
|
|
&renderer->FrameBuffer[eye],
|
|
|
|
suggestedEyeTextureWidth,
|
|
|
|
suggestedEyeTextureHeight);
|
|
|
|
}
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrRenderer_Destroy(ovrRenderer* renderer) {
|
2022-07-24 14:25:04 +02:00
|
|
|
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
|
|
|
ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]);
|
|
|
|
}
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrApp
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void ovrApp_Clear(ovrApp* app) {
|
2022-07-24 14:25:04 +02:00
|
|
|
app->Focused = false;
|
|
|
|
app->Instance = XR_NULL_HANDLE;
|
|
|
|
app->Session = XR_NULL_HANDLE;
|
|
|
|
memset(&app->ViewportConfig, 0, sizeof(XrViewConfigurationProperties));
|
|
|
|
memset(&app->ViewConfigurationView, 0, ovrMaxNumEyes * sizeof(XrViewConfigurationView));
|
|
|
|
app->SystemId = XR_NULL_SYSTEM_ID;
|
|
|
|
app->HeadSpace = XR_NULL_HANDLE;
|
|
|
|
app->StageSpace = XR_NULL_HANDLE;
|
|
|
|
app->FakeStageSpace = XR_NULL_HANDLE;
|
|
|
|
app->CurrentSpace = XR_NULL_HANDLE;
|
|
|
|
app->SessionActive = false;
|
|
|
|
app->SwapInterval = 1;
|
|
|
|
memset(app->Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount);
|
|
|
|
app->LayerCount = 0;
|
|
|
|
app->MainThreadTid = 0;
|
|
|
|
app->RenderThreadTid = 0;
|
|
|
|
app->TouchPadDownLastFrame = false;
|
|
|
|
|
|
|
|
ovrRenderer_Clear(&app->Renderer);
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrApp_Destroy(ovrApp* app) {
|
2022-07-24 14:25:04 +02:00
|
|
|
ovrApp_Clear(app);
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) {
|
2022-07-24 14:25:04 +02:00
|
|
|
if (state == XR_SESSION_STATE_READY) {
|
|
|
|
assert(app->SessionActive == false);
|
2022-07-07 19:30:06 +02:00
|
|
|
|
2022-07-24 14:25:04 +02:00
|
|
|
XrSessionBeginInfo sessionBeginInfo;
|
|
|
|
memset(&sessionBeginInfo, 0, sizeof(sessionBeginInfo));
|
|
|
|
sessionBeginInfo.type = XR_TYPE_SESSION_BEGIN_INFO;
|
|
|
|
sessionBeginInfo.next = NULL;
|
|
|
|
sessionBeginInfo.primaryViewConfigurationType = app->ViewportConfig.viewConfigurationType;
|
2022-07-07 19:30:06 +02:00
|
|
|
|
2022-07-24 14:25:04 +02:00
|
|
|
XrResult result;
|
|
|
|
OXR(result = xrBeginSession(app->Session, &sessionBeginInfo));
|
2022-07-07 19:30:06 +02:00
|
|
|
|
2022-07-24 14:25:04 +02:00
|
|
|
app->SessionActive = (result == XR_SUCCESS);
|
|
|
|
} else if (state == XR_SESSION_STATE_STOPPING) {
|
|
|
|
assert(app->SessionActive);
|
2022-07-07 19:30:06 +02:00
|
|
|
|
2022-07-24 14:25:04 +02:00
|
|
|
OXR(xrEndSession(app->Session));
|
|
|
|
app->SessionActive = false;
|
|
|
|
}
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
2022-07-08 14:44:20 +02:00
|
|
|
int ovrApp_HandleXrEvents(ovrApp* app) {
|
2022-07-24 14:25:04 +02:00
|
|
|
XrEventDataBuffer eventDataBuffer = {};
|
|
|
|
int recenter = 0;
|
|
|
|
|
|
|
|
// Poll for events
|
|
|
|
for (;;) {
|
|
|
|
XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer);
|
|
|
|
baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER;
|
|
|
|
baseEventHeader->next = NULL;
|
|
|
|
XrResult r;
|
|
|
|
OXR(r = xrPollEvent(app->Instance, &eventDataBuffer));
|
|
|
|
if (r != XR_SUCCESS) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (baseEventHeader->type) {
|
|
|
|
case XR_TYPE_EVENT_DATA_EVENTS_LOST:
|
|
|
|
ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event");
|
|
|
|
break;
|
|
|
|
case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: {
|
|
|
|
const XrEventDataInstanceLossPending* instance_loss_pending_event =
|
|
|
|
(XrEventDataInstanceLossPending*)(baseEventHeader);
|
|
|
|
ALOGV(
|
|
|
|
"xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event: time %f",
|
|
|
|
FromXrTime(instance_loss_pending_event->lossTime));
|
|
|
|
} break;
|
|
|
|
case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
|
|
|
|
ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event");
|
|
|
|
break;
|
|
|
|
case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: {
|
|
|
|
const XrEventDataPerfSettingsEXT* perf_settings_event =
|
|
|
|
(XrEventDataPerfSettingsEXT*)(baseEventHeader);
|
|
|
|
ALOGV(
|
|
|
|
"xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d",
|
|
|
|
perf_settings_event->type,
|
|
|
|
perf_settings_event->subDomain,
|
|
|
|
perf_settings_event->fromLevel,
|
|
|
|
perf_settings_event->toLevel);
|
|
|
|
} break;
|
|
|
|
case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: {
|
|
|
|
XrEventDataReferenceSpaceChangePending* ref_space_change_event =
|
|
|
|
(XrEventDataReferenceSpaceChangePending*)(baseEventHeader);
|
|
|
|
ALOGV(
|
|
|
|
"xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event: changed space: %d for session %p at time %f",
|
|
|
|
ref_space_change_event->referenceSpaceType,
|
|
|
|
(void*)ref_space_change_event->session,
|
|
|
|
FromXrTime(ref_space_change_event->changeTime));
|
|
|
|
recenter = 1;
|
|
|
|
} break;
|
|
|
|
case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: {
|
|
|
|
const XrEventDataSessionStateChanged* session_state_changed_event =
|
|
|
|
(XrEventDataSessionStateChanged*)(baseEventHeader);
|
|
|
|
ALOGV(
|
|
|
|
"xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f",
|
|
|
|
session_state_changed_event->state,
|
|
|
|
(void*)session_state_changed_event->session,
|
|
|
|
FromXrTime(session_state_changed_event->time));
|
|
|
|
|
|
|
|
switch (session_state_changed_event->state) {
|
|
|
|
case XR_SESSION_STATE_FOCUSED:
|
|
|
|
app->Focused = true;
|
|
|
|
break;
|
|
|
|
case XR_SESSION_STATE_VISIBLE:
|
|
|
|
app->Focused = false;
|
|
|
|
break;
|
|
|
|
case XR_SESSION_STATE_READY:
|
|
|
|
case XR_SESSION_STATE_STOPPING:
|
|
|
|
ovrApp_HandleSessionStateChanges(app, session_state_changed_event->state);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
default:
|
|
|
|
ALOGV("xrPollEvent: Unknown event");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return recenter;
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrMatrix4f
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(
|
2022-07-24 14:25:04 +02:00
|
|
|
const float angleLeft,
|
|
|
|
const float angleRight,
|
|
|
|
const float angleUp,
|
|
|
|
const float angleDown,
|
|
|
|
const float nearZ,
|
|
|
|
const float farZ) {
|
|
|
|
|
|
|
|
const float tanAngleLeft = tanf(angleLeft);
|
|
|
|
const float tanAngleRight = tanf(angleRight);
|
|
|
|
|
|
|
|
const float tanAngleDown = tanf(angleDown);
|
|
|
|
const float tanAngleUp = tanf(angleUp);
|
|
|
|
|
|
|
|
const float tanAngleWidth = tanAngleRight - tanAngleLeft;
|
|
|
|
|
|
|
|
// Set to tanAngleDown - tanAngleUp for a clip space with positive Y
|
|
|
|
// down (Vulkan). Set to tanAngleUp - tanAngleDown for a clip space with
|
|
|
|
// positive Y up (OpenGL / D3D / Metal).
|
|
|
|
const float tanAngleHeight = tanAngleUp - tanAngleDown;
|
|
|
|
|
|
|
|
// Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES).
|
|
|
|
// Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal).
|
|
|
|
const float offsetZ = nearZ;
|
|
|
|
|
|
|
|
ovrMatrix4f result;
|
|
|
|
if (farZ <= nearZ) {
|
|
|
|
// place the far plane at infinity
|
|
|
|
result.M[0][0] = 2 / tanAngleWidth;
|
|
|
|
result.M[0][1] = 0;
|
|
|
|
result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
|
|
|
|
result.M[0][3] = 0;
|
|
|
|
|
|
|
|
result.M[1][0] = 0;
|
|
|
|
result.M[1][1] = 2 / tanAngleHeight;
|
|
|
|
result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
|
|
|
|
result.M[1][3] = 0;
|
|
|
|
|
|
|
|
result.M[2][0] = 0;
|
|
|
|
result.M[2][1] = 0;
|
|
|
|
result.M[2][2] = -1;
|
|
|
|
result.M[2][3] = -(nearZ + offsetZ);
|
|
|
|
|
|
|
|
result.M[3][0] = 0;
|
|
|
|
result.M[3][1] = 0;
|
|
|
|
result.M[3][2] = -1;
|
|
|
|
result.M[3][3] = 0;
|
|
|
|
} else {
|
|
|
|
// normal projection
|
|
|
|
result.M[0][0] = 2 / tanAngleWidth;
|
|
|
|
result.M[0][1] = 0;
|
|
|
|
result.M[0][2] = (tanAngleRight + tanAngleLeft) / tanAngleWidth;
|
|
|
|
result.M[0][3] = 0;
|
|
|
|
|
|
|
|
result.M[1][0] = 0;
|
|
|
|
result.M[1][1] = 2 / tanAngleHeight;
|
|
|
|
result.M[1][2] = (tanAngleUp + tanAngleDown) / tanAngleHeight;
|
|
|
|
result.M[1][3] = 0;
|
|
|
|
|
|
|
|
result.M[2][0] = 0;
|
|
|
|
result.M[2][1] = 0;
|
|
|
|
result.M[2][2] = -(farZ + offsetZ) / (farZ - nearZ);
|
|
|
|
result.M[2][3] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ);
|
|
|
|
|
|
|
|
result.M[3][0] = 0;
|
|
|
|
result.M[3][1] = 0;
|
|
|
|
result.M[3][2] = -1;
|
|
|
|
result.M[3][3] = 0;
|
|
|
|
}
|
|
|
|
return result;
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q) {
|
2022-07-24 14:25:04 +02:00
|
|
|
const float ww = q->w * q->w;
|
|
|
|
const float xx = q->x * q->x;
|
|
|
|
const float yy = q->y * q->y;
|
|
|
|
const float zz = q->z * q->z;
|
|
|
|
|
|
|
|
ovrMatrix4f out;
|
|
|
|
out.M[0][0] = ww + xx - yy - zz;
|
|
|
|
out.M[0][1] = 2 * (q->x * q->y - q->w * q->z);
|
|
|
|
out.M[0][2] = 2 * (q->x * q->z + q->w * q->y);
|
|
|
|
out.M[0][3] = 0;
|
|
|
|
|
|
|
|
out.M[1][0] = 2 * (q->x * q->y + q->w * q->z);
|
|
|
|
out.M[1][1] = ww - xx + yy - zz;
|
|
|
|
out.M[1][2] = 2 * (q->y * q->z - q->w * q->x);
|
|
|
|
out.M[1][3] = 0;
|
|
|
|
|
|
|
|
out.M[2][0] = 2 * (q->x * q->z - q->w * q->y);
|
|
|
|
out.M[2][1] = 2 * (q->y * q->z + q->w * q->x);
|
|
|
|
out.M[2][2] = ww - xx - yy + zz;
|
|
|
|
out.M[2][3] = 0;
|
|
|
|
|
|
|
|
out.M[3][0] = 0;
|
|
|
|
out.M[3][1] = 0;
|
|
|
|
out.M[3][2] = 0;
|
|
|
|
out.M[3][3] = 1;
|
|
|
|
return out;
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Use left-multiplication to accumulate transformations.
|
|
|
|
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b) {
|
2022-07-24 14:25:04 +02:00
|
|
|
ovrMatrix4f out;
|
|
|
|
out.M[0][0] = a->M[0][0] * b->M[0][0] + a->M[0][1] * b->M[1][0] + a->M[0][2] * b->M[2][0] +
|
|
|
|
a->M[0][3] * b->M[3][0];
|
|
|
|
out.M[1][0] = a->M[1][0] * b->M[0][0] + a->M[1][1] * b->M[1][0] + a->M[1][2] * b->M[2][0] +
|
|
|
|
a->M[1][3] * b->M[3][0];
|
|
|
|
out.M[2][0] = a->M[2][0] * b->M[0][0] + a->M[2][1] * b->M[1][0] + a->M[2][2] * b->M[2][0] +
|
|
|
|
a->M[2][3] * b->M[3][0];
|
|
|
|
out.M[3][0] = a->M[3][0] * b->M[0][0] + a->M[3][1] * b->M[1][0] + a->M[3][2] * b->M[2][0] +
|
|
|
|
a->M[3][3] * b->M[3][0];
|
|
|
|
|
|
|
|
out.M[0][1] = a->M[0][0] * b->M[0][1] + a->M[0][1] * b->M[1][1] + a->M[0][2] * b->M[2][1] +
|
|
|
|
a->M[0][3] * b->M[3][1];
|
|
|
|
out.M[1][1] = a->M[1][0] * b->M[0][1] + a->M[1][1] * b->M[1][1] + a->M[1][2] * b->M[2][1] +
|
|
|
|
a->M[1][3] * b->M[3][1];
|
|
|
|
out.M[2][1] = a->M[2][0] * b->M[0][1] + a->M[2][1] * b->M[1][1] + a->M[2][2] * b->M[2][1] +
|
|
|
|
a->M[2][3] * b->M[3][1];
|
|
|
|
out.M[3][1] = a->M[3][0] * b->M[0][1] + a->M[3][1] * b->M[1][1] + a->M[3][2] * b->M[2][1] +
|
|
|
|
a->M[3][3] * b->M[3][1];
|
|
|
|
|
|
|
|
out.M[0][2] = a->M[0][0] * b->M[0][2] + a->M[0][1] * b->M[1][2] + a->M[0][2] * b->M[2][2] +
|
|
|
|
a->M[0][3] * b->M[3][2];
|
|
|
|
out.M[1][2] = a->M[1][0] * b->M[0][2] + a->M[1][1] * b->M[1][2] + a->M[1][2] * b->M[2][2] +
|
|
|
|
a->M[1][3] * b->M[3][2];
|
|
|
|
out.M[2][2] = a->M[2][0] * b->M[0][2] + a->M[2][1] * b->M[1][2] + a->M[2][2] * b->M[2][2] +
|
|
|
|
a->M[2][3] * b->M[3][2];
|
|
|
|
out.M[3][2] = a->M[3][0] * b->M[0][2] + a->M[3][1] * b->M[1][2] + a->M[3][2] * b->M[2][2] +
|
|
|
|
a->M[3][3] * b->M[3][2];
|
|
|
|
|
|
|
|
out.M[0][3] = a->M[0][0] * b->M[0][3] + a->M[0][1] * b->M[1][3] + a->M[0][2] * b->M[2][3] +
|
|
|
|
a->M[0][3] * b->M[3][3];
|
|
|
|
out.M[1][3] = a->M[1][0] * b->M[0][3] + a->M[1][1] * b->M[1][3] + a->M[1][2] * b->M[2][3] +
|
|
|
|
a->M[1][3] * b->M[3][3];
|
|
|
|
out.M[2][3] = a->M[2][0] * b->M[0][3] + a->M[2][1] * b->M[1][3] + a->M[2][2] * b->M[2][3] +
|
|
|
|
a->M[2][3] * b->M[3][3];
|
|
|
|
out.M[3][3] = a->M[3][0] * b->M[0][3] + a->M[3][1] * b->M[1][3] + a->M[3][2] * b->M[2][3] +
|
|
|
|
a->M[3][3] * b->M[3][3];
|
|
|
|
return out;
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ) {
|
2022-07-24 14:25:04 +02:00
|
|
|
const float sinX = sinf(radiansX);
|
|
|
|
const float cosX = cosf(radiansX);
|
|
|
|
const ovrMatrix4f rotationX = {
|
|
|
|
{{1, 0, 0, 0}, {0, cosX, -sinX, 0}, {0, sinX, cosX, 0}, {0, 0, 0, 1}}};
|
|
|
|
const float sinY = sinf(radiansY);
|
|
|
|
const float cosY = cosf(radiansY);
|
|
|
|
const ovrMatrix4f rotationY = {
|
|
|
|
{{cosY, 0, sinY, 0}, {0, 1, 0, 0}, {-sinY, 0, cosY, 0}, {0, 0, 0, 1}}};
|
|
|
|
const float sinZ = sinf(radiansZ);
|
|
|
|
const float cosZ = cosf(radiansZ);
|
|
|
|
const ovrMatrix4f rotationZ = {
|
|
|
|
{{cosZ, -sinZ, 0, 0}, {sinZ, cosZ, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}};
|
|
|
|
const ovrMatrix4f rotationXY = ovrMatrix4f_Multiply(&rotationY, &rotationX);
|
|
|
|
return ovrMatrix4f_Multiply(&rotationZ, &rotationXY);
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v) {
|
2022-07-24 14:25:04 +02:00
|
|
|
XrVector4f out;
|
|
|
|
out.x = a->M[0][0] * v->x + a->M[0][1] * v->y + a->M[0][2] * v->z + a->M[0][3] * v->w;
|
|
|
|
out.y = a->M[1][0] * v->x + a->M[1][1] * v->y + a->M[1][2] * v->z + a->M[1][3] * v->w;
|
|
|
|
out.z = a->M[2][0] * v->x + a->M[2][1] * v->y + a->M[2][2] * v->z + a->M[2][3] * v->w;
|
|
|
|
out.w = a->M[3][0] * v->x + a->M[3][1] * v->y + a->M[3][2] * v->z + a->M[3][3] * v->w;
|
|
|
|
return out;
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
================================================================================
|
|
|
|
|
|
|
|
ovrTrackedController
|
|
|
|
|
|
|
|
================================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void ovrTrackedController_Clear(ovrTrackedController* controller) {
|
2022-07-24 14:25:04 +02:00
|
|
|
controller->Active = false;
|
|
|
|
controller->Pose = XrPosef_Identity();
|
2022-07-07 19:30:06 +02:00
|
|
|
}
|