mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
OpenXR - VR framebuffer class added
This commit is contained in:
parent
8ba87bf9aa
commit
dc977a2eed
3 changed files with 990 additions and 1 deletions
|
@ -118,7 +118,7 @@ add_definitions(-DASSETS_DIR="${CMAKE_INSTALL_FULL_DATADIR}/ppsspp/assets/")
|
|||
|
||||
if(OPENXR)
|
||||
add_definitions(-DOPENXR)
|
||||
include_directories(ext/openxr)
|
||||
include_directories(ext/openxr vr)
|
||||
link_directories(quest/libs/arm64-v8a)
|
||||
message("OpenXR enabled")
|
||||
endif()
|
||||
|
@ -1063,6 +1063,13 @@ if(ANDROID)
|
|||
android/jni/OpenSLContext.cpp
|
||||
android/jni/OpenSLContext.h
|
||||
)
|
||||
|
||||
if (OPENXR)
|
||||
set(nativeExtra ${nativeExtra}
|
||||
VR/VRFramebuffer.c
|
||||
VR/VRFramebuffer.h
|
||||
)
|
||||
endif()
|
||||
# No target
|
||||
elseif(IOS)
|
||||
set(nativeExtra ${nativeExtra}
|
||||
|
|
692
VR/VRFramebuffer.c
Normal file
692
VR/VRFramebuffer.c
Normal file
|
@ -0,0 +1,692 @@
|
|||
#include "VRFramebuffer.h"
|
||||
|
||||
/************************************************************************************
|
||||
|
||||
Original file name : XrCompositor_NativeActivity.c
|
||||
|
||||
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
||||
|
||||
*************************************************************************************/
|
||||
|
||||
#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>
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
ovrEgl
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
void ovrEgl_Clear(ovrEgl* egl) {
|
||||
egl->MajorVersion = 0;
|
||||
egl->MinorVersion = 0;
|
||||
egl->Display = 0;
|
||||
egl->Config = 0;
|
||||
egl->TinySurface = EGL_NO_SURFACE;
|
||||
egl->MainSurface = EGL_NO_SURFACE;
|
||||
egl->Context = EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl) {
|
||||
if (egl->Display != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
egl->Display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
ALOGV(" eglInitialize( Display, &MajorVersion, &MinorVersion )");
|
||||
eglInitialize(egl->Display, &egl->MajorVersion, &egl->MinorVersion);
|
||||
// Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample
|
||||
// flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in
|
||||
// settings, and that is completely wasted for our warp target.
|
||||
const int MAX_CONFIGS = 1024;
|
||||
EGLConfig configs[MAX_CONFIGS];
|
||||
EGLint numConfigs = 0;
|
||||
if (eglGetConfigs(egl->Display, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) {
|
||||
ALOGE(" eglGetConfigs() failed: %d", eglGetError());
|
||||
return;
|
||||
}
|
||||
const EGLint configAttribs[] = {
|
||||
EGL_RED_SIZE,
|
||||
8,
|
||||
EGL_GREEN_SIZE,
|
||||
8,
|
||||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_ALPHA_SIZE,
|
||||
8, // need alpha for the multi-pass timewarp compositor
|
||||
EGL_DEPTH_SIZE,
|
||||
0,
|
||||
EGL_STENCIL_SIZE,
|
||||
0,
|
||||
EGL_SAMPLES,
|
||||
0,
|
||||
EGL_NONE};
|
||||
egl->Config = 0;
|
||||
for (int i = 0; i < numConfigs; i++) {
|
||||
EGLint value = 0;
|
||||
|
||||
eglGetConfigAttrib(egl->Display, configs[i], EGL_RENDERABLE_TYPE, &value);
|
||||
if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The pbuffer config also needs to be compatible with normal window rendering
|
||||
// so it can share textures with the window context.
|
||||
eglGetConfigAttrib(egl->Display, configs[i], EGL_SURFACE_TYPE, &value);
|
||||
if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
for (; configAttribs[j] != EGL_NONE; j += 2) {
|
||||
eglGetConfigAttrib(egl->Display, configs[i], configAttribs[j], &value);
|
||||
if (value != configAttribs[j + 1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (configAttribs[j] == EGL_NONE) {
|
||||
egl->Config = configs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (egl->Config == 0) {
|
||||
ALOGE(" eglChooseConfig() failed: %d", eglGetError());
|
||||
return;
|
||||
}
|
||||
EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE};
|
||||
ALOGV(" Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )");
|
||||
egl->Context = eglCreateContext(
|
||||
egl->Display,
|
||||
egl->Config,
|
||||
(shareEgl != NULL) ? shareEgl->Context : EGL_NO_CONTEXT,
|
||||
contextAttribs);
|
||||
if (egl->Context == EGL_NO_CONTEXT) {
|
||||
ALOGE(" eglCreateContext() failed: %d", eglGetError());
|
||||
return;
|
||||
}
|
||||
const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE};
|
||||
ALOGV(" TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )");
|
||||
egl->TinySurface = eglCreatePbufferSurface(egl->Display, egl->Config, surfaceAttribs);
|
||||
if (egl->TinySurface == EGL_NO_SURFACE) {
|
||||
ALOGE(" eglCreatePbufferSurface() failed: %d", eglGetError());
|
||||
eglDestroyContext(egl->Display, egl->Context);
|
||||
egl->Context = EGL_NO_CONTEXT;
|
||||
return;
|
||||
}
|
||||
ALOGV(" eglMakeCurrent( Display, TinySurface, TinySurface, Context )");
|
||||
if (eglMakeCurrent(egl->Display, egl->TinySurface, egl->TinySurface, egl->Context) ==
|
||||
EGL_FALSE) {
|
||||
ALOGE(" eglMakeCurrent() failed: %d", eglGetError());
|
||||
eglDestroySurface(egl->Display, egl->TinySurface);
|
||||
eglDestroyContext(egl->Display, egl->Context);
|
||||
egl->Context = EGL_NO_CONTEXT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ovrEgl_DestroyContext(ovrEgl* egl) {
|
||||
if (egl->Display != 0) {
|
||||
ALOGE(" eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )");
|
||||
if (eglMakeCurrent(egl->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) ==
|
||||
EGL_FALSE) {
|
||||
ALOGE(" eglMakeCurrent() failed: %d", eglGetError());
|
||||
}
|
||||
}
|
||||
if (egl->Context != EGL_NO_CONTEXT) {
|
||||
ALOGE(" eglDestroyContext( Display, Context )");
|
||||
if (eglDestroyContext(egl->Display, egl->Context) == EGL_FALSE) {
|
||||
ALOGE(" eglDestroyContext() failed: %d", eglGetError());
|
||||
}
|
||||
egl->Context = EGL_NO_CONTEXT;
|
||||
}
|
||||
if (egl->TinySurface != EGL_NO_SURFACE) {
|
||||
ALOGE(" eglDestroySurface( Display, TinySurface )");
|
||||
if (eglDestroySurface(egl->Display, egl->TinySurface) == EGL_FALSE) {
|
||||
ALOGE(" eglDestroySurface() failed: %d", eglGetError());
|
||||
}
|
||||
egl->TinySurface = EGL_NO_SURFACE;
|
||||
}
|
||||
if (egl->Display != 0) {
|
||||
ALOGE(" eglTerminate( Display )");
|
||||
if (eglTerminate(egl->Display) == EGL_FALSE) {
|
||||
ALOGE(" eglTerminate() failed: %d", eglGetError());
|
||||
}
|
||||
egl->Display = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
ovrFramebuffer
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
|
||||
void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) {
|
||||
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;
|
||||
}
|
||||
|
||||
bool ovrFramebuffer_Create(
|
||||
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_DEPTH_COMPONENT24, 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_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;
|
||||
}
|
||||
|
||||
void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) {
|
||||
GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers));
|
||||
GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers));
|
||||
OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle));
|
||||
free(frameBuffer->ColorSwapChainImage);
|
||||
|
||||
free(frameBuffer->DepthBuffers);
|
||||
free(frameBuffer->FrameBuffers);
|
||||
|
||||
ovrFramebuffer_Clear(frameBuffer);
|
||||
}
|
||||
|
||||
void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) {
|
||||
GL(glBindFramebuffer(
|
||||
GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex]));
|
||||
}
|
||||
|
||||
void ovrFramebuffer_SetNone() {
|
||||
GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
||||
}
|
||||
|
||||
void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) {
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) {
|
||||
XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL};
|
||||
OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo));
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
ovrRenderer
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
void ovrRenderer_Clear(ovrRenderer* renderer) {
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
ovrFramebuffer_Clear(&renderer->FrameBuffer[eye]);
|
||||
}
|
||||
}
|
||||
|
||||
void ovrRenderer_Create(
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void ovrRenderer_Destroy(ovrRenderer* renderer) {
|
||||
for (int eye = 0; eye < ovrMaxNumEyes; eye++) {
|
||||
ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
ovrApp
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
void ovrApp_Clear(ovrApp* app) {
|
||||
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;
|
||||
|
||||
ovrEgl_Clear(&app->Egl);
|
||||
ovrRenderer_Clear(&app->Renderer);
|
||||
}
|
||||
|
||||
void ovrApp_Destroy(ovrApp* app) {
|
||||
ovrApp_Clear(app);
|
||||
}
|
||||
|
||||
void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) {
|
||||
if (state == XR_SESSION_STATE_READY) {
|
||||
assert(app->SessionActive == false);
|
||||
|
||||
XrSessionBeginInfo sessionBeginInfo;
|
||||
memset(&sessionBeginInfo, 0, sizeof(sessionBeginInfo));
|
||||
sessionBeginInfo.type = XR_TYPE_SESSION_BEGIN_INFO;
|
||||
sessionBeginInfo.next = NULL;
|
||||
sessionBeginInfo.primaryViewConfigurationType = app->ViewportConfig.viewConfigurationType;
|
||||
|
||||
XrResult result;
|
||||
OXR(result = xrBeginSession(app->Session, &sessionBeginInfo));
|
||||
|
||||
app->SessionActive = (result == XR_SUCCESS);
|
||||
} else if (state == XR_SESSION_STATE_STOPPING) {
|
||||
assert(app->SessionActive);
|
||||
|
||||
OXR(xrEndSession(app->Session));
|
||||
app->SessionActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
GLboolean ovrApp_HandleXrEvents(ovrApp* app) {
|
||||
XrEventDataBuffer eventDataBuffer = {};
|
||||
GLboolean recenter = GL_FALSE;
|
||||
|
||||
// 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 = GL_TRUE;
|
||||
} 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;
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
ovrMatrix4f
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(
|
||||
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;
|
||||
}
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/// Use left-multiplication to accumulate transformations.
|
||||
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b) {
|
||||
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;
|
||||
}
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ) {
|
||||
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);
|
||||
}
|
||||
|
||||
XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v) {
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
================================================================================
|
||||
|
||||
ovrTrackedController
|
||||
|
||||
================================================================================
|
||||
*/
|
||||
|
||||
void ovrTrackedController_Clear(ovrTrackedController* controller) {
|
||||
controller->Active = false;
|
||||
controller->Pose = XrPosef_Identity();
|
||||
}
|
290
VR/VRFramebuffer.h
Normal file
290
VR/VRFramebuffer.h
Normal file
|
@ -0,0 +1,290 @@
|
|||
#ifndef __VR_FRAMEBUFFER
|
||||
#define __VR_FRAMEBUFFER
|
||||
|
||||
//OpenXR
|
||||
#define XR_USE_GRAPHICS_API_OPENGL_ES 1
|
||||
#define XR_USE_PLATFORM_ANDROID 1
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#include <jni.h>
|
||||
#include <math.h>
|
||||
#include <openxr.h>
|
||||
#include <openxr_platform.h>
|
||||
|
||||
#define MATH_PI 3.14159265358979323846f
|
||||
|
||||
#define ALOGE(...) printf(__VA_ARGS__)
|
||||
#define ALOGV(...) printf(__VA_ARGS__)
|
||||
|
||||
typedef union {
|
||||
XrCompositionLayerProjection Projection;
|
||||
XrCompositionLayerCylinderKHR Cylinder;
|
||||
} ovrCompositorLayer_Union;
|
||||
|
||||
enum { ovrMaxLayerCount = 1 };
|
||||
enum { ovrMaxNumEyes = 2 };
|
||||
|
||||
#define GL(func) func;
|
||||
#define OXR(func) func;
|
||||
|
||||
typedef struct {
|
||||
JavaVM* Vm;
|
||||
jobject ActivityObject;
|
||||
JNIEnv* Env;
|
||||
} ovrJava;
|
||||
|
||||
typedef struct {
|
||||
XrSwapchain Handle;
|
||||
uint32_t Width;
|
||||
uint32_t Height;
|
||||
} ovrSwapChain;
|
||||
|
||||
typedef struct {
|
||||
EGLint MajorVersion;
|
||||
EGLint MinorVersion;
|
||||
EGLDisplay Display;
|
||||
EGLConfig Config;
|
||||
EGLSurface TinySurface;
|
||||
EGLSurface MainSurface;
|
||||
EGLContext Context;
|
||||
} ovrEgl;
|
||||
|
||||
typedef struct {
|
||||
int Width;
|
||||
int Height;
|
||||
uint32_t TextureSwapChainLength;
|
||||
uint32_t TextureSwapChainIndex;
|
||||
ovrSwapChain ColorSwapChain;
|
||||
XrSwapchainImageOpenGLESKHR* ColorSwapChainImage;
|
||||
GLuint* DepthBuffers;
|
||||
GLuint* FrameBuffers;
|
||||
} ovrFramebuffer;
|
||||
|
||||
typedef struct {
|
||||
ovrFramebuffer FrameBuffer[ovrMaxNumEyes];
|
||||
} ovrRenderer;
|
||||
|
||||
typedef struct {
|
||||
GLboolean Active;
|
||||
XrPosef Pose;
|
||||
} ovrTrackedController;
|
||||
|
||||
typedef struct {
|
||||
ovrEgl Egl;
|
||||
GLboolean Focused;
|
||||
|
||||
XrInstance Instance;
|
||||
XrSession Session;
|
||||
XrViewConfigurationProperties ViewportConfig;
|
||||
XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes];
|
||||
XrSystemId SystemId;
|
||||
XrSpace HeadSpace;
|
||||
XrSpace StageSpace;
|
||||
XrSpace FakeStageSpace;
|
||||
XrSpace CurrentSpace;
|
||||
GLboolean SessionActive;
|
||||
|
||||
int SwapInterval;
|
||||
// These threads will be marked as performance threads.
|
||||
int MainThreadTid;
|
||||
int RenderThreadTid;
|
||||
ovrCompositorLayer_Union Layers[ovrMaxLayerCount];
|
||||
int LayerCount;
|
||||
|
||||
GLboolean TouchPadDownLastFrame;
|
||||
ovrRenderer Renderer;
|
||||
ovrTrackedController TrackedController[2];
|
||||
} ovrApp;
|
||||
|
||||
|
||||
typedef struct {
|
||||
float M[4][4];
|
||||
} ovrMatrix4f;
|
||||
|
||||
typedef enum ovrButton_ {
|
||||
ovrButton_A = 0x00000001, // Set for trigger pulled on the Gear VR and Go Controllers
|
||||
ovrButton_B = 0x00000002,
|
||||
ovrButton_RThumb = 0x00000004,
|
||||
ovrButton_RShoulder = 0x00000008,
|
||||
|
||||
ovrButton_X = 0x00000100,
|
||||
ovrButton_Y = 0x00000200,
|
||||
ovrButton_LThumb = 0x00000400,
|
||||
ovrButton_LShoulder = 0x00000800,
|
||||
|
||||
ovrButton_Up = 0x00010000,
|
||||
ovrButton_Down = 0x00020000,
|
||||
ovrButton_Left = 0x00040000,
|
||||
ovrButton_Right = 0x00080000,
|
||||
ovrButton_Enter = 0x00100000, //< Set for touchpad click on the Go Controller, menu
|
||||
// button on Left Quest Controller
|
||||
ovrButton_Back = 0x00200000, //< Back button on the Go Controller (only set when
|
||||
// a short press comes up)
|
||||
ovrButton_GripTrigger = 0x04000000, //< grip trigger engaged
|
||||
ovrButton_Trigger = 0x20000000, //< Index Trigger engaged
|
||||
ovrButton_Joystick = 0x80000000, //< Click of the Joystick
|
||||
|
||||
ovrButton_EnumSize = 0x7fffffff
|
||||
} ovrButton;
|
||||
|
||||
typedef struct {
|
||||
uint64_t frameIndex;
|
||||
ovrApp appState;
|
||||
ovrJava java;
|
||||
float predictedDisplayTime;
|
||||
} engine_t;
|
||||
|
||||
void ovrApp_Clear(ovrApp* app);
|
||||
void ovrApp_Destroy(ovrApp* app);
|
||||
GLboolean ovrApp_HandleXrEvents(ovrApp* app);
|
||||
|
||||
void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl);
|
||||
void ovrEgl_DestroyContext(ovrEgl* egl);
|
||||
|
||||
void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer);
|
||||
void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer);
|
||||
void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer);
|
||||
void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer);
|
||||
void ovrFramebuffer_SetNone();
|
||||
|
||||
void ovrRenderer_Create(
|
||||
XrSession session,
|
||||
ovrRenderer* renderer,
|
||||
int suggestedEyeTextureWidth,
|
||||
int suggestedEyeTextureHeight);
|
||||
void ovrRenderer_Destroy(ovrRenderer* renderer);
|
||||
|
||||
void ovrTrackedController_Clear(ovrTrackedController* controller);
|
||||
|
||||
ovrMatrix4f ovrMatrix4f_Multiply(const ovrMatrix4f* a, const ovrMatrix4f* b);
|
||||
ovrMatrix4f ovrMatrix4f_CreateRotation(const float radiansX, const float radiansY, const float radiansZ);
|
||||
ovrMatrix4f ovrMatrix4f_CreateFromQuaternion(const XrQuaternionf* q);
|
||||
ovrMatrix4f ovrMatrix4f_CreateProjectionFov(
|
||||
const float fovDegreesX,
|
||||
const float fovDegreesY,
|
||||
const float offsetX,
|
||||
const float offsetY,
|
||||
const float nearZ,
|
||||
const float farZ);
|
||||
|
||||
XrVector4f XrVector4f_MultiplyMatrix4f(const ovrMatrix4f* a, const XrVector4f* v);
|
||||
|
||||
|
||||
/// THESE METHODS HAVE ORIGIN IN openxr_oculus_helpers.h
|
||||
|
||||
static inline double FromXrTime(const XrTime time) {
|
||||
return (time * 1e-9);
|
||||
}
|
||||
|
||||
static inline XrTime ToXrTime(const double timeInSeconds) {
|
||||
return (timeInSeconds * 1e9);
|
||||
}
|
||||
|
||||
static inline XrPosef XrPosef_Identity() {
|
||||
XrPosef r;
|
||||
r.orientation.x = 0;
|
||||
r.orientation.y = 0;
|
||||
r.orientation.z = 0;
|
||||
r.orientation.w = 1;
|
||||
r.position.x = 0;
|
||||
r.position.y = 0;
|
||||
r.position.z = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline float XrVector3f_LengthSquared(const XrVector3f v) {
|
||||
return v.x * v.x + v.y * v.y + v.z * v.z;;
|
||||
}
|
||||
|
||||
static inline float XrVector3f_Length(const XrVector3f v) {
|
||||
return sqrtf(XrVector3f_LengthSquared(v));
|
||||
}
|
||||
|
||||
static inline XrVector3f XrVector3f_ScalarMultiply(const XrVector3f v, float scale) {
|
||||
XrVector3f u;
|
||||
u.x = v.x * scale;
|
||||
u.y = v.y * scale;
|
||||
u.z = v.z * scale;
|
||||
return u;
|
||||
}
|
||||
|
||||
static inline XrVector3f XrVector3f_Normalized(const XrVector3f v) {
|
||||
float rcpLen = 1.0f / XrVector3f_Length(v);
|
||||
return XrVector3f_ScalarMultiply(v, rcpLen);
|
||||
}
|
||||
|
||||
static inline XrQuaternionf XrQuaternionf_CreateFromVectorAngle(
|
||||
const XrVector3f axis,
|
||||
const float angle) {
|
||||
XrQuaternionf r;
|
||||
if (XrVector3f_LengthSquared(axis) == 0.0f) {
|
||||
r.x = 0;
|
||||
r.y = 0;
|
||||
r.z = 0;
|
||||
r.w = 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
XrVector3f unitAxis = XrVector3f_Normalized(axis);
|
||||
float sinHalfAngle = sinf(angle * 0.5f);
|
||||
|
||||
r.w = cosf(angle * 0.5f);
|
||||
r.x = unitAxis.x * sinHalfAngle;
|
||||
r.y = unitAxis.y * sinHalfAngle;
|
||||
r.z = unitAxis.z * sinHalfAngle;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline XrQuaternionf XrQuaternionf_Multiply(const XrQuaternionf a, const XrQuaternionf b) {
|
||||
XrQuaternionf c;
|
||||
c.x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y;
|
||||
c.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x;
|
||||
c.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w;
|
||||
c.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z;
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline XrQuaternionf XrQuaternionf_Inverse(const XrQuaternionf q) {
|
||||
XrQuaternionf r;
|
||||
r.x = -q.x;
|
||||
r.y = -q.y;
|
||||
r.z = -q.z;
|
||||
r.w = q.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline XrVector3f XrQuaternionf_Rotate(const XrQuaternionf a, const XrVector3f v) {
|
||||
XrVector3f r;
|
||||
XrQuaternionf q = {v.x, v.y, v.z, 0.0f};
|
||||
XrQuaternionf aq = XrQuaternionf_Multiply(a, q);
|
||||
XrQuaternionf aInv = XrQuaternionf_Inverse(a);
|
||||
XrQuaternionf aqaInv = XrQuaternionf_Multiply(aq, aInv);
|
||||
r.x = aqaInv.x;
|
||||
r.y = aqaInv.y;
|
||||
r.z = aqaInv.z;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline XrVector3f XrVector3f_Add(const XrVector3f u, const XrVector3f v) {
|
||||
XrVector3f w;
|
||||
w.x = u.x + v.x;
|
||||
w.y = u.y + v.y;
|
||||
w.z = u.z + v.z;
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline XrVector3f XrPosef_Transform(const XrPosef a, const XrVector3f v) {
|
||||
XrVector3f r0 = XrQuaternionf_Rotate(a.orientation, v);
|
||||
return XrVector3f_Add(r0, a.position);
|
||||
}
|
||||
|
||||
static inline XrPosef XrPosef_Multiply(const XrPosef a, const XrPosef b) {
|
||||
XrPosef c;
|
||||
c.orientation = XrQuaternionf_Multiply(a.orientation, b.orientation);
|
||||
c.position = XrPosef_Transform(a, b.position);
|
||||
return c;
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue