@@ -14,7 +14,7 @@ add_compile_definitions(
|
|||||||
XR_USE_GRAPHICS_API_OPENGL_ES
|
XR_USE_GRAPHICS_API_OPENGL_ES
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET GLOBAL wayland-server)
|
pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET GLOBAL wayland-server)
|
||||||
pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl)
|
pkg_check_modules(EGL REQUIRED IMPORTED_TARGET egl)
|
||||||
@@ -22,6 +22,19 @@ pkg_check_modules(GLES2 REQUIRED IMPORTED_TARGET glesv2)
|
|||||||
pkg_check_modules(WLROOTS REQUIRED IMPORTED_TARGET wlroots-0.19)
|
pkg_check_modules(WLROOTS REQUIRED IMPORTED_TARGET wlroots-0.19)
|
||||||
pkg_check_modules(OPENXR REQUIRED IMPORTED_TARGET openxr)
|
pkg_check_modules(OPENXR REQUIRED IMPORTED_TARGET openxr)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
raylib
|
||||||
|
GIT_REPOSITORY https://github.com/slendidev/raylib.git
|
||||||
|
GIT_TAG "lunar"
|
||||||
|
GIT_SHALLOW 1
|
||||||
|
)
|
||||||
|
set(OPENGL_VERSION "ES 3.0")
|
||||||
|
set(PLATFORM DRM)
|
||||||
|
set(BUILD_EXAMPLES OFF)
|
||||||
|
FetchContent_MakeAvailable(raylib)
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME})
|
add_executable(${PROJECT_NAME})
|
||||||
target_sources(${PROJECT_NAME} PUBLIC
|
target_sources(${PROJECT_NAME} PUBLIC
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
@@ -37,5 +50,6 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
|
|||||||
PkgConfig::GLES2
|
PkgConfig::GLES2
|
||||||
PkgConfig::WLROOTS
|
PkgConfig::WLROOTS
|
||||||
PkgConfig::OPENXR
|
PkgConfig::OPENXR
|
||||||
|
raylib
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,13 @@
|
|||||||
wlroots
|
wlroots
|
||||||
vulkan-loader
|
vulkan-loader
|
||||||
|
|
||||||
|
# For raylib
|
||||||
|
xorg.libXrandr
|
||||||
|
xorg.libXinerama
|
||||||
|
xorg.libXcursor
|
||||||
|
xorg.libXi
|
||||||
|
glfw
|
||||||
|
|
||||||
libffi
|
libffi
|
||||||
wayland
|
wayland
|
||||||
wayland-scanner
|
wayland-scanner
|
||||||
|
|||||||
422
src/LunarWM.cppm
422
src/LunarWM.cppm
@@ -25,12 +25,20 @@ extern "C" {
|
|||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PFNGLDRAWBUFFERSEXTPROC glDrawBuffersEXT = NULL;
|
||||||
|
|
||||||
|
#include <raylib.h>
|
||||||
|
#include <raymath.h>
|
||||||
|
#include <rlgl.h>
|
||||||
|
|
||||||
export module LunarWM.LunarWM;
|
export module LunarWM.LunarWM;
|
||||||
|
|
||||||
import std;
|
import std;
|
||||||
|
|
||||||
import LunarWM.Math;
|
import LunarWM.Math;
|
||||||
|
|
||||||
|
using Clock = std::chrono::high_resolution_clock;
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
template <> struct formatter<XrResult, char> {
|
template <> struct formatter<XrResult, char> {
|
||||||
template <class ParseContext> constexpr auto parse(ParseContext &ctx) {
|
template <class ParseContext> constexpr auto parse(ParseContext &ctx) {
|
||||||
@@ -39,7 +47,8 @@ template <> struct formatter<XrResult, char> {
|
|||||||
|
|
||||||
static constexpr std::string_view to_string(XrResult r) {
|
static constexpr std::string_view to_string(XrResult r) {
|
||||||
switch (r) {
|
switch (r) {
|
||||||
case XR_FRAME_DISCARDED: return "XR_FRAME_DISCARDED";
|
case XR_FRAME_DISCARDED:
|
||||||
|
return "XR_FRAME_DISCARDED";
|
||||||
case XR_ERROR_VALIDATION_FAILURE:
|
case XR_ERROR_VALIDATION_FAILURE:
|
||||||
return "XR_ERROR_VALIDATION_FAILURE: The function usage was invalid in "
|
return "XR_ERROR_VALIDATION_FAILURE: The function usage was invalid in "
|
||||||
"some way.";
|
"some way.";
|
||||||
@@ -539,11 +548,65 @@ private:
|
|||||||
|
|
||||||
namespace LunarWM {
|
namespace LunarWM {
|
||||||
|
|
||||||
|
static Matrix xr_projection_matrix(const XrFovf& fov)
|
||||||
|
{
|
||||||
|
static_assert(RL_CULL_DISTANCE_FAR > RL_CULL_DISTANCE_NEAR);
|
||||||
|
|
||||||
|
Matrix matrix{};
|
||||||
|
|
||||||
|
const float near = (float)RL_CULL_DISTANCE_NEAR;
|
||||||
|
const float far = (float)RL_CULL_DISTANCE_FAR;
|
||||||
|
|
||||||
|
const float tan_angle_left = tanf(fov.angleLeft);
|
||||||
|
const float tan_angle_right = tanf(fov.angleRight);
|
||||||
|
|
||||||
|
const float tan_angle_down = tanf(fov.angleDown);
|
||||||
|
const float tan_angle_up = tanf(fov.angleUp);
|
||||||
|
|
||||||
|
const float tan_angle_width = tan_angle_right - tan_angle_left;
|
||||||
|
const float tan_angle_height = tan_angle_up - tan_angle_down;
|
||||||
|
|
||||||
|
matrix.m0 = 2 / tan_angle_width;
|
||||||
|
matrix.m4 = 0;
|
||||||
|
matrix.m8 = (tan_angle_right + tan_angle_left) / tan_angle_width;
|
||||||
|
matrix.m12 = 0;
|
||||||
|
|
||||||
|
matrix.m1 = 0;
|
||||||
|
matrix.m5 = 2 / tan_angle_height;
|
||||||
|
matrix.m9 = (tan_angle_up + tan_angle_down) / tan_angle_height;
|
||||||
|
matrix.m13 = 0;
|
||||||
|
|
||||||
|
matrix.m2 = 0;
|
||||||
|
matrix.m6 = 0;
|
||||||
|
matrix.m10 = -(far + near) / (far - near);
|
||||||
|
matrix.m14 = -(far * (near + near)) / (far - near);
|
||||||
|
|
||||||
|
matrix.m3 = 0;
|
||||||
|
matrix.m7 = 0;
|
||||||
|
matrix.m11 = -1;
|
||||||
|
matrix.m15 = 0;
|
||||||
|
|
||||||
|
return matrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Matrix xr_matrix(const XrPosef& pose)
|
||||||
|
{
|
||||||
|
Matrix translation = MatrixTranslate(pose.position.x, pose.position.y, pose.position.z);
|
||||||
|
Matrix rotation = QuaternionToMatrix(Quaternion{pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w});
|
||||||
|
return rotation * translation;
|
||||||
|
}
|
||||||
|
|
||||||
enum class SwapchainType {
|
enum class SwapchainType {
|
||||||
Color,
|
Color,
|
||||||
Depth,
|
Depth,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SwapchainInfo {
|
||||||
|
XrSwapchain swapchain{XR_NULL_HANDLE};
|
||||||
|
std::int64_t swapchain_format{};
|
||||||
|
std::vector<GLuint> image_views;
|
||||||
|
};
|
||||||
|
|
||||||
export struct LunarWM {
|
export struct LunarWM {
|
||||||
LunarWM() = default;
|
LunarWM() = default;
|
||||||
~LunarWM();
|
~LunarWM();
|
||||||
@@ -560,7 +623,8 @@ private:
|
|||||||
void init_xr();
|
void init_xr();
|
||||||
|
|
||||||
void poll_events_xr();
|
void poll_events_xr();
|
||||||
bool render_layer(RenderLayerInfo &info);
|
bool render_layer(RenderLayerInfo &info, float dt);
|
||||||
|
void render_3d(float dt);
|
||||||
|
|
||||||
bool m_initialized{};
|
bool m_initialized{};
|
||||||
|
|
||||||
@@ -587,12 +651,6 @@ private:
|
|||||||
} m_wayland;
|
} m_wayland;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct SwapchainInfo {
|
|
||||||
XrSwapchain swapchain{XR_NULL_HANDLE};
|
|
||||||
std::int64_t swapchain_format{};
|
|
||||||
std::vector<GLuint> image_views;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::optional<XrInstance> instance;
|
std::optional<XrInstance> instance;
|
||||||
std::optional<XrSystemId> system_id;
|
std::optional<XrSystemId> system_id;
|
||||||
XrSession session{XR_NULL_HANDLE};
|
XrSession session{XR_NULL_HANDLE};
|
||||||
@@ -611,46 +669,91 @@ private:
|
|||||||
XrEnvironmentBlendMode environment_blend_mode;
|
XrEnvironmentBlendMode environment_blend_mode;
|
||||||
|
|
||||||
XrSpace local_space{XR_NULL_HANDLE};
|
XrSpace local_space{XR_NULL_HANDLE};
|
||||||
|
XrSpace view_space{XR_NULL_HANDLE};
|
||||||
|
|
||||||
std::uint32_t get_swapchain_image(XrSwapchain swapchain, uint32_t index) {
|
std::uint32_t get_swapchain_image(XrSwapchain swapchain, uint32_t index) {
|
||||||
return swapchain_images_map[swapchain].second[index].image;
|
return swapchain_images_map[swapchain].second[index].image;
|
||||||
}
|
}
|
||||||
} m_xr;
|
} m_xr;
|
||||||
|
|
||||||
|
struct RendererFrame {
|
||||||
|
GLuint fbo{0};
|
||||||
|
uint32_t color_idx{UINT32_MAX};
|
||||||
|
uint32_t depth_idx{UINT32_MAX};
|
||||||
|
bool has_depth{};
|
||||||
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
GLuint vertex_array{};
|
GLuint fbo{};
|
||||||
GLuint set_framebuffer{};
|
RenderTexture2D tmp_rt{};
|
||||||
|
|
||||||
void begin_rendering() {
|
Camera3D camera{
|
||||||
glGenVertexArrays(1, &vertex_array);
|
.position = { 0, 0, 0 },
|
||||||
glBindVertexArray(vertex_array);
|
.target = { 0, 0, 1 },
|
||||||
|
.up = { 0, 1, 0 },
|
||||||
|
.fovy = 45,
|
||||||
|
.projection = CAMERA_PERSPECTIVE,
|
||||||
|
};
|
||||||
|
|
||||||
glGenFramebuffers(1, &set_framebuffer);
|
bool begin_render(GLuint color_tex, GLuint depth_tex, uint32_t w, uint32_t h, const XrView& view) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, set_framebuffer);
|
if (!fbo) // create once
|
||||||
|
glGenFramebuffers(1, &fbo);
|
||||||
|
|
||||||
|
rlFramebufferAttach(fbo, color_tex, RL_ATTACHMENT_COLOR_CHANNEL0,
|
||||||
|
RL_ATTACHMENT_TEXTURE2D, 0);
|
||||||
|
|
||||||
|
assert(rlFramebufferComplete(fbo));
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
GLenum fbStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||||
|
if(fbStatus != GL_FRAMEBUFFER_COMPLETE){
|
||||||
|
printf("FBO incomplete: 0x%04x\n", fbStatus);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void end_rendering() {
|
tmp_rt = {.id = fbo,
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
.texture = {color_tex, (int)w, (int)h, 1, -1},
|
||||||
glDeleteFramebuffers(1, &set_framebuffer);
|
.depth = {depth_tex, (int)w, (int)h, 1, -1}};
|
||||||
set_framebuffer = 0;
|
|
||||||
|
|
||||||
glBindVertexArray(0);
|
BeginTextureMode(tmp_rt);
|
||||||
glDeleteVertexArrays(1, &vertex_array);
|
rlEnableStereoRender();
|
||||||
vertex_array = 0;
|
|
||||||
|
const XrFovf &fov = view.fov;
|
||||||
|
Matrix proj = xr_projection_matrix(fov);
|
||||||
|
Matrix viewM = MatrixInvert(xr_matrix(view.pose));
|
||||||
|
|
||||||
|
rlSetMatrixProjectionStereo(proj, proj);
|
||||||
|
rlSetMatrixViewOffsetStereo(viewM, viewM);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_color(GLuint image_view, float r, float g, float b, float a) {
|
void end_render(LunarWM &wm, SwapchainInfo &color_sc,
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)(uint64_t)image_view);
|
SwapchainInfo *depth_sc) {
|
||||||
glClearColor(r, g, b, a);
|
rlDisableStereoRender();
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
EndTextureMode();
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_depth(GLuint image_view, float d) {
|
void update_camera(LunarWM &wm, Camera3D &camera, RenderLayerInfo &info)
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)(uint64_t)image_view);
|
{
|
||||||
glClearDepthf(d);
|
const XrTime time = info.predicted_display_time;
|
||||||
glClear(GL_DEPTH_BUFFER_BIT);
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
XrSpaceLocation view_location{ XR_TYPE_SPACE_LOCATION };
|
||||||
|
XrResult result = xrLocateSpace(wm.m_xr.view_space, wm.m_xr.local_space, time, &view_location);
|
||||||
|
if (result != XR_SUCCESS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view_location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) {
|
||||||
|
const auto& pos = view_location.pose.position;
|
||||||
|
camera.position = Vector3{ pos.x, pos.y, pos.z };
|
||||||
|
}
|
||||||
|
if (view_location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) {
|
||||||
|
const auto& rot = view_location.pose.orientation;
|
||||||
|
const auto forward = Vector3RotateByQuaternion(Vector3{ 0, 0, -1 }, Quaternion{ rot.x, rot.y, rot.z, rot.w });
|
||||||
|
const auto up = Vector3RotateByQuaternion(Vector3{ 0, 1, 0 }, Quaternion{ rot.x, rot.y, rot.z, rot.w });
|
||||||
|
camera.target = camera.position + forward;
|
||||||
|
camera.up = up;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} m_renderer;
|
} m_renderer;
|
||||||
|
|
||||||
@@ -664,6 +767,8 @@ private:
|
|||||||
std::vector<XrCompositionLayerProjectionView> layer_projection_views;
|
std::vector<XrCompositionLayerProjectionView> layer_projection_views;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::chrono::time_point<Clock> m_last_tick;
|
||||||
|
|
||||||
bool m_running{};
|
bool m_running{};
|
||||||
bool m_session_running{};
|
bool m_session_running{};
|
||||||
bool m_session_state{};
|
bool m_session_state{};
|
||||||
@@ -671,14 +776,28 @@ private:
|
|||||||
|
|
||||||
void LunarWM::init() {
|
void LunarWM::init() {
|
||||||
this->init_wayland();
|
this->init_wayland();
|
||||||
|
|
||||||
|
wlr_log(WLR_INFO, "0");
|
||||||
|
auto draw = eglGetCurrentSurface(EGL_DRAW);
|
||||||
|
auto read = eglGetCurrentSurface(EGL_READ);
|
||||||
if (eglMakeCurrent(m_wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
if (eglMakeCurrent(m_wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||||
m_wayland.egl_context) == EGL_FALSE) {
|
m_wayland.egl_context) == EGL_FALSE) {
|
||||||
throw std::runtime_error("Failed to eglMakeCurrent");
|
throw std::runtime_error("Failed to eglMakeCurrent");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_log(WLR_INFO, "1");
|
||||||
this->init_xr();
|
this->init_xr();
|
||||||
|
wlr_log(WLR_INFO, "2");
|
||||||
|
|
||||||
wlr_log(WLR_INFO, "OpenGL ES version: %s", glGetString(GL_VERSION));
|
wlr_log(WLR_INFO, "OpenGL ES version: %s", glGetString(GL_VERSION));
|
||||||
|
InitWindow(0, 0, "");
|
||||||
|
wlr_log(WLR_INFO, "3");
|
||||||
|
|
||||||
|
if (eglMakeCurrent(m_wayland.egl_display, read, draw,
|
||||||
|
m_wayland.egl_context) == EGL_FALSE) {
|
||||||
|
throw std::runtime_error("Failed to eglMakeCurrent");
|
||||||
|
}
|
||||||
|
wlr_log(WLR_INFO, "4");
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,6 +1034,7 @@ void LunarWM::init_xr() {
|
|||||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
|
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
glDrawBuffersEXT = (PFNGLDRAWBUFFERSEXTPROC) eglGetProcAddress("glDrawBuffersEXT");
|
||||||
XrGraphicsBindingEGLMNDX gbind = {
|
XrGraphicsBindingEGLMNDX gbind = {
|
||||||
.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX,
|
.type = XR_TYPE_GRAPHICS_BINDING_EGL_MNDX,
|
||||||
.next = nullptr,
|
.next = nullptr,
|
||||||
@@ -969,7 +1089,8 @@ void LunarWM::init_xr() {
|
|||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Failed to get amount of OpenXR view configuration views");
|
"Failed to get amount of OpenXR view configuration views");
|
||||||
}
|
}
|
||||||
m_xr.view_configuration_views.resize(count, {XR_TYPE_VIEW_CONFIGURATION_VIEW});
|
m_xr.view_configuration_views.resize(count,
|
||||||
|
{XR_TYPE_VIEW_CONFIGURATION_VIEW});
|
||||||
if (xrEnumerateViewConfigurationViews(
|
if (xrEnumerateViewConfigurationViews(
|
||||||
*m_xr.instance, *m_xr.system_id,
|
*m_xr.instance, *m_xr.system_id,
|
||||||
XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, count, &count,
|
XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, count, &count,
|
||||||
@@ -995,9 +1116,9 @@ void LunarWM::init_xr() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::int64_t> swapchain_format_depth_needles{
|
std::vector<std::int64_t> swapchain_format_depth_needles{
|
||||||
GL_DEPTH_COMPONENT32F,
|
|
||||||
GL_DEPTH_COMPONENT24,
|
|
||||||
GL_DEPTH_COMPONENT16,
|
GL_DEPTH_COMPONENT16,
|
||||||
|
// GL_DEPTH_COMPONENT32F,
|
||||||
|
// GL_DEPTH_COMPONENT24,
|
||||||
};
|
};
|
||||||
const std::vector<int64_t>::const_iterator &swapchain_format_depth_it =
|
const std::vector<int64_t>::const_iterator &swapchain_format_depth_it =
|
||||||
std::find_first_of(swapchain_formats.begin(), swapchain_formats.end(),
|
std::find_first_of(swapchain_formats.begin(), swapchain_formats.end(),
|
||||||
@@ -1009,8 +1130,7 @@ void LunarWM::init_xr() {
|
|||||||
}
|
}
|
||||||
auto const swapchain_format_depth = *swapchain_format_depth_it;
|
auto const swapchain_format_depth = *swapchain_format_depth_it;
|
||||||
|
|
||||||
std::vector<std::int64_t> swapchain_format_color_needles{GL_RGBA8,
|
std::vector<std::int64_t> swapchain_format_color_needles{GL_SRGB8_ALPHA8};
|
||||||
GL_RGBA8_SNORM};
|
|
||||||
const std::vector<int64_t>::const_iterator &swapchain_format_color_it =
|
const std::vector<int64_t>::const_iterator &swapchain_format_color_it =
|
||||||
std::find_first_of(swapchain_formats.begin(), swapchain_formats.end(),
|
std::find_first_of(swapchain_formats.begin(), swapchain_formats.end(),
|
||||||
std::begin(swapchain_format_color_needles),
|
std::begin(swapchain_format_color_needles),
|
||||||
@@ -1021,9 +1141,6 @@ void LunarWM::init_xr() {
|
|||||||
}
|
}
|
||||||
auto const swapchain_format_color = *swapchain_format_color_it;
|
auto const swapchain_format_color = *swapchain_format_color_it;
|
||||||
|
|
||||||
m_xr.swapchains.color.resize(m_xr.view_configuration_views.size());
|
|
||||||
m_xr.swapchains.depth.resize(m_xr.view_configuration_views.size());
|
|
||||||
|
|
||||||
auto const AllocateSwapchainImageData =
|
auto const AllocateSwapchainImageData =
|
||||||
[&](XrSwapchain swapchain, SwapchainType type, uint32_t count) {
|
[&](XrSwapchain swapchain, SwapchainType type, uint32_t count) {
|
||||||
m_xr.swapchain_images_map[swapchain].first = type;
|
m_xr.swapchain_images_map[swapchain].first = type;
|
||||||
@@ -1032,10 +1149,19 @@ void LunarWM::init_xr() {
|
|||||||
return reinterpret_cast<XrSwapchainImageBaseHeader *>(
|
return reinterpret_cast<XrSwapchainImageBaseHeader *>(
|
||||||
m_xr.swapchain_images_map[swapchain].second.data());
|
m_xr.swapchain_images_map[swapchain].second.data());
|
||||||
};
|
};
|
||||||
for (std::size_t i = 0; i < m_xr.view_configuration_views.size(); i++) {
|
|
||||||
auto &color_sc = m_xr.swapchains.color[i];
|
const uint32_t view_count = (uint32_t)m_xr.view_configuration_views.size();
|
||||||
auto &depth_sc = m_xr.swapchains.depth[i];
|
const uint32_t eyeW = m_xr.view_configuration_views[0].recommendedImageRectWidth;
|
||||||
auto &vcv = m_xr.view_configuration_views[i];
|
const uint32_t eyeH = m_xr.view_configuration_views[0].recommendedImageRectHeight;
|
||||||
|
const uint32_t bufW = eyeW * view_count;
|
||||||
|
|
||||||
|
m_xr.swapchains.color.resize(1);
|
||||||
|
m_xr.swapchains.depth.resize(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto &color_sc = m_xr.swapchains.color[0];
|
||||||
|
auto &depth_sc = m_xr.swapchains.depth[0];
|
||||||
|
auto &vcv = m_xr.view_configuration_views[0];
|
||||||
|
|
||||||
{ // Create color swapchain
|
{ // Create color swapchain
|
||||||
XrSwapchainCreateInfo ci{
|
XrSwapchainCreateInfo ci{
|
||||||
@@ -1046,8 +1172,8 @@ void LunarWM::init_xr() {
|
|||||||
XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT,
|
XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT,
|
||||||
.format = swapchain_format_color,
|
.format = swapchain_format_color,
|
||||||
.sampleCount = vcv.recommendedSwapchainSampleCount,
|
.sampleCount = vcv.recommendedSwapchainSampleCount,
|
||||||
.width = vcv.recommendedImageRectWidth,
|
.width = bufW,
|
||||||
.height = vcv.recommendedImageRectHeight,
|
.height = eyeH,
|
||||||
.faceCount = 1,
|
.faceCount = 1,
|
||||||
.arraySize = 1,
|
.arraySize = 1,
|
||||||
.mipCount = 1,
|
.mipCount = 1,
|
||||||
@@ -1069,8 +1195,8 @@ void LunarWM::init_xr() {
|
|||||||
XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
|
||||||
.format = swapchain_format_depth,
|
.format = swapchain_format_depth,
|
||||||
.sampleCount = vcv.recommendedSwapchainSampleCount,
|
.sampleCount = vcv.recommendedSwapchainSampleCount,
|
||||||
.width = vcv.recommendedImageRectWidth,
|
.width = bufW,
|
||||||
.height = vcv.recommendedImageRectHeight,
|
.height = eyeH,
|
||||||
.faceCount = 1,
|
.faceCount = 1,
|
||||||
.arraySize = 1,
|
.arraySize = 1,
|
||||||
.mipCount = 1,
|
.mipCount = 1,
|
||||||
@@ -1178,7 +1304,7 @@ void LunarWM::init_xr() {
|
|||||||
throw std::runtime_error("Failed to get XR environment blend modes");
|
throw std::runtime_error("Failed to get XR environment blend modes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<XrEnvironmentBlendMode> requested_environment_blend_modes {
|
std::vector<XrEnvironmentBlendMode> requested_environment_blend_modes{
|
||||||
XR_ENVIRONMENT_BLEND_MODE_OPAQUE,
|
XR_ENVIRONMENT_BLEND_MODE_OPAQUE,
|
||||||
XR_ENVIRONMENT_BLEND_MODE_ADDITIVE,
|
XR_ENVIRONMENT_BLEND_MODE_ADDITIVE,
|
||||||
XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM,
|
XR_ENVIRONMENT_BLEND_MODE_MAX_ENUM,
|
||||||
@@ -1200,7 +1326,7 @@ void LunarWM::init_xr() {
|
|||||||
XrReferenceSpaceCreateInfo ci{
|
XrReferenceSpaceCreateInfo ci{
|
||||||
.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO,
|
.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO,
|
||||||
.next = nullptr,
|
.next = nullptr,
|
||||||
.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL,
|
.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE,
|
||||||
.poseInReferenceSpace = {.orientation = {0.0f, 0.0f, 0.0f, 1.0f},
|
.poseInReferenceSpace = {.orientation = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||||
.position = {0.0f, 0.0f, 0.0f}},
|
.position = {0.0f, 0.0f, 0.0f}},
|
||||||
};
|
};
|
||||||
@@ -1210,6 +1336,21 @@ void LunarWM::init_xr() {
|
|||||||
throw std::runtime_error("Failed to create OpenXR reference space");
|
throw std::runtime_error("Failed to create OpenXR reference space");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // View reference space
|
||||||
|
XrReferenceSpaceCreateInfo ci{
|
||||||
|
.type = XR_TYPE_REFERENCE_SPACE_CREATE_INFO,
|
||||||
|
.next = nullptr,
|
||||||
|
.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW,
|
||||||
|
.poseInReferenceSpace = {.orientation = {0.0f, 0.0f, 0.0f, 1.0f},
|
||||||
|
.position = {0.0f, 0.0f, 0.0f}},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (xrCreateReferenceSpace(m_xr.session, &ci, &m_xr.view_space) !=
|
||||||
|
XR_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create OpenXR reference space");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LunarWM::poll_events_xr() {
|
void LunarWM::poll_events_xr() {
|
||||||
@@ -1305,83 +1446,134 @@ void LunarWM::poll_events_xr() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LunarWM::render_layer(RenderLayerInfo &info) {
|
bool LunarWM::render_layer(RenderLayerInfo &info, float dt)
|
||||||
std::vector<XrView> views(m_xr.view_configuration_views.size(), {XR_TYPE_VIEW});
|
{
|
||||||
|
const uint32_t view_count = (uint32_t)m_xr.view_configuration_views.size();
|
||||||
|
std::vector<XrView> views(view_count, { XR_TYPE_VIEW });
|
||||||
|
|
||||||
XrViewState view_state{XR_TYPE_VIEW_STATE};
|
XrViewLocateInfo locInfo{ XR_TYPE_VIEW_LOCATE_INFO };
|
||||||
XrViewLocateInfo view_locate{XR_TYPE_VIEW_LOCATE_INFO};
|
locInfo.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
||||||
view_locate.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
|
locInfo.displayTime = info.predicted_display_time;
|
||||||
view_locate.displayTime = info.predicted_display_time;
|
locInfo.space = m_xr.local_space;
|
||||||
view_locate.space = m_xr.local_space;
|
|
||||||
|
|
||||||
uint32_t view_count = 0;
|
XrViewState viewState{ XR_TYPE_VIEW_STATE };
|
||||||
if (xrLocateViews(m_xr.session, &view_locate, &view_state,
|
uint32_t located = 0;
|
||||||
(uint32_t)views.size(), &view_count, views.data()) != XR_SUCCESS) {
|
if (xrLocateViews(m_xr.session, &locInfo, &viewState,
|
||||||
|
view_count, &located, views.data()) != XR_SUCCESS ||
|
||||||
|
located != view_count)
|
||||||
|
{
|
||||||
wlr_log(WLR_ERROR, "Failed to locate views");
|
wlr_log(WLR_ERROR, "Failed to locate views");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
info.layer_projection_views.resize(view_count, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW});
|
auto &color_sc = m_xr.swapchains.color[0];
|
||||||
|
auto &depth_sc = m_xr.swapchains.depth[0];
|
||||||
|
|
||||||
auto acquire_wait = [](XrSwapchain sc, uint32_t &idx) -> bool {
|
auto acquire_wait = [](XrSwapchain sc, uint32_t &idx)->bool{
|
||||||
XrSwapchainImageAcquireInfo ai{XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO};
|
XrSwapchainImageAcquireInfo ai{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
|
||||||
if (xrAcquireSwapchainImage(sc, &ai, &idx) != XR_SUCCESS) return false;
|
if (xrAcquireSwapchainImage(sc, &ai, &idx) != XR_SUCCESS) return false;
|
||||||
|
XrSwapchainImageWaitInfo wi{ XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO };
|
||||||
XrSwapchainImageWaitInfo wi{XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO};
|
|
||||||
wi.timeout = XR_INFINITE_DURATION;
|
wi.timeout = XR_INFINITE_DURATION;
|
||||||
return xrWaitSwapchainImage(sc, &wi) == XR_SUCCESS;
|
return xrWaitSwapchainImage(sc, &wi) == XR_SUCCESS;
|
||||||
};
|
};
|
||||||
auto release = [](XrSwapchain sc) {
|
|
||||||
XrSwapchainImageReleaseInfo ri{XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO};
|
|
||||||
xrReleaseSwapchainImage(sc, &ri);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < view_count; ++i) {
|
uint32_t colIdx = 0, depIdx = 0;
|
||||||
auto &color_sc = m_xr.swapchains.color[i];
|
if (!acquire_wait(color_sc.swapchain, colIdx) ||
|
||||||
auto &depth_sc = m_xr.swapchains.depth[i];
|
!acquire_wait(depth_sc.swapchain, depIdx))
|
||||||
|
|
||||||
uint32_t color_idx = 0, depth_idx = 0;
|
|
||||||
if (!acquire_wait(color_sc.swapchain, color_idx) ||
|
|
||||||
!acquire_wait(depth_sc.swapchain, depth_idx)) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to acquire swapchain images");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t w = m_xr.view_configuration_views[i].recommendedImageRectWidth;
|
|
||||||
const uint32_t h = m_xr.view_configuration_views[i].recommendedImageRectHeight;
|
|
||||||
|
|
||||||
m_renderer.begin_rendering();
|
|
||||||
{
|
{
|
||||||
if (m_xr.environment_blend_mode == XR_ENVIRONMENT_BLEND_MODE_OPAQUE)
|
wlr_log(WLR_ERROR, "Swap-chain acquire failed");
|
||||||
m_renderer.clear_color(color_sc.image_views[color_idx], 0.17f, 0.17f, 0.17f, 1.0f);
|
return false;
|
||||||
else
|
|
||||||
m_renderer.clear_color(color_sc.image_views[color_idx], 0.0f, 0.0f, 0.0f, 1.0f);
|
|
||||||
|
|
||||||
m_renderer.clear_depth(depth_sc.image_views[depth_idx], 1.0f);
|
|
||||||
}
|
}
|
||||||
m_renderer.end_rendering();
|
|
||||||
|
GLuint color_tex = m_xr.get_swapchain_image(color_sc.swapchain, colIdx);
|
||||||
|
GLuint depth_tex = m_xr.get_swapchain_image(depth_sc.swapchain, depIdx);
|
||||||
|
|
||||||
|
if (!m_renderer.fbo) glGenFramebuffers(1, &m_renderer.fbo);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, m_renderer.fbo);
|
||||||
|
|
||||||
|
rlFramebufferAttach(m_renderer.fbo, color_tex, RL_ATTACHMENT_COLOR_CHANNEL0,
|
||||||
|
RL_ATTACHMENT_TEXTURE2D, 0);
|
||||||
|
rlFramebufferAttach(m_renderer.fbo, depth_tex, RL_ATTACHMENT_DEPTH,
|
||||||
|
RL_ATTACHMENT_TEXTURE2D, 0);
|
||||||
|
assert(rlFramebufferComplete(m_renderer.fbo));
|
||||||
|
|
||||||
|
const uint32_t eyeW = m_xr.view_configuration_views[0].recommendedImageRectWidth;
|
||||||
|
const uint32_t eyeH = m_xr.view_configuration_views[0].recommendedImageRectHeight;
|
||||||
|
|
||||||
|
m_renderer.tmp_rt = { m_renderer.fbo,
|
||||||
|
{ color_tex, (int)(eyeW*view_count), (int)eyeH, 1, -1 },
|
||||||
|
{ depth_tex, (int)(eyeW*view_count), (int)eyeH, 1, -1 } };
|
||||||
|
|
||||||
|
Matrix projL = xr_projection_matrix(views[0].fov);
|
||||||
|
Matrix projR = xr_projection_matrix(views[1].fov);
|
||||||
|
Matrix viewL = MatrixInvert( xr_matrix(views[0].pose) );
|
||||||
|
Matrix viewR = MatrixInvert( xr_matrix(views[1].pose) );
|
||||||
|
|
||||||
|
BeginTextureMode(m_renderer.tmp_rt);
|
||||||
|
|
||||||
|
rlEnableStereoRender();
|
||||||
|
rlSetMatrixProjectionStereo(projL, projR);
|
||||||
|
rlSetMatrixViewOffsetStereo(viewL, viewR);
|
||||||
|
|
||||||
|
glViewport(0, 0, (GLsizei)(eyeW*view_count), (GLsizei)eyeH);
|
||||||
|
ClearBackground(RAYWHITE);
|
||||||
|
|
||||||
|
BeginMode3D(m_renderer.camera);
|
||||||
|
{
|
||||||
|
this->render_3d(dt);
|
||||||
|
}
|
||||||
|
EndMode3D();
|
||||||
|
|
||||||
|
rlDisableStereoRender();
|
||||||
|
EndTextureMode();
|
||||||
|
|
||||||
|
XrSwapchainImageReleaseInfo ri{ XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
|
||||||
|
xrReleaseSwapchainImage(color_sc.swapchain, &ri);
|
||||||
|
xrReleaseSwapchainImage(depth_sc.swapchain, &ri);
|
||||||
|
|
||||||
|
info.layer_projection_views.resize(view_count,
|
||||||
|
{ XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW });
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < view_count; ++i)
|
||||||
|
{
|
||||||
|
const int32_t xOff = (int32_t)i * (int32_t)eyeW;
|
||||||
|
|
||||||
auto &pv = info.layer_projection_views[i];
|
auto &pv = info.layer_projection_views[i];
|
||||||
pv.pose = views[i].pose;
|
pv.pose = views[i].pose;
|
||||||
pv.fov = views[i].fov;
|
pv.fov = views[i].fov;
|
||||||
pv.subImage.swapchain = color_sc.swapchain;
|
pv.subImage.swapchain = color_sc.swapchain;
|
||||||
pv.subImage.imageRect = {{0, 0}, {(int32_t)w, (int32_t)h}};
|
pv.subImage.imageRect = { { xOff, 0 },
|
||||||
|
{ (int32_t)eyeW, (int32_t)eyeH } };
|
||||||
pv.subImage.imageArrayIndex = 0;
|
pv.subImage.imageArrayIndex = 0;
|
||||||
|
|
||||||
release(color_sc.swapchain);
|
|
||||||
release(depth_sc.swapchain);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info.layer_projection.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION;
|
||||||
|
info.layer_projection.space = m_xr.local_space;
|
||||||
|
info.layer_projection.viewCount = view_count;
|
||||||
|
info.layer_projection.views = info.layer_projection_views.data();
|
||||||
info.layer_projection.layerFlags =
|
info.layer_projection.layerFlags =
|
||||||
XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT |
|
XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT |
|
||||||
XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT;
|
XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT;
|
||||||
info.layer_projection.space = m_xr.local_space;
|
|
||||||
info.layer_projection.viewCount = (uint32_t)info.layer_projection_views.size();
|
info.layers.clear();
|
||||||
info.layer_projection.views = info.layer_projection_views.data();
|
info.layers.push_back(
|
||||||
|
reinterpret_cast<XrCompositionLayerBaseHeader*>(&info.layer_projection));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LunarWM::render_3d(float dt) {
|
||||||
|
static float animT {0.0f};
|
||||||
|
animT += dt;
|
||||||
|
|
||||||
|
DrawGrid(10, 1);
|
||||||
|
|
||||||
|
Vector3 forward = Vector3Normalize(Vector3Subtract(m_renderer.camera.target, m_renderer.camera.position));
|
||||||
|
float distance = 5.0f + sinf(animT) * 3.0f;
|
||||||
|
Vector3 spherePos = Vector3Add(m_renderer.camera.position, Vector3Scale(forward, distance));
|
||||||
|
DrawSphere(spherePos, 0.5f, YELLOW);
|
||||||
|
}
|
||||||
|
|
||||||
LunarWM::~LunarWM() {
|
LunarWM::~LunarWM() {
|
||||||
assert(m_initialized);
|
assert(m_initialized);
|
||||||
|
|
||||||
@@ -1444,9 +1636,20 @@ void LunarWM::run() {
|
|||||||
|
|
||||||
m_running = true;
|
m_running = true;
|
||||||
while (m_running) {
|
while (m_running) {
|
||||||
|
auto now = Clock::now();
|
||||||
|
float dt = std::chrono::duration<float>(now - m_last_tick).count();
|
||||||
|
m_last_tick = now;
|
||||||
|
|
||||||
wl_display_flush_clients(m_wayland.display);
|
wl_display_flush_clients(m_wayland.display);
|
||||||
wl_event_loop_dispatch(m_wayland.event_loop, 0);
|
wl_event_loop_dispatch(m_wayland.event_loop, 0);
|
||||||
|
|
||||||
|
auto draw = eglGetCurrentSurface(EGL_DRAW);
|
||||||
|
auto read = eglGetCurrentSurface(EGL_READ);
|
||||||
|
if (eglMakeCurrent(m_wayland.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||||
|
m_wayland.egl_context) == EGL_FALSE) {
|
||||||
|
throw std::runtime_error("Failed to eglMakeCurrent");
|
||||||
|
}
|
||||||
|
|
||||||
poll_events_xr();
|
poll_events_xr();
|
||||||
|
|
||||||
XrFrameState frame_state{
|
XrFrameState frame_state{
|
||||||
@@ -1471,18 +1674,25 @@ void LunarWM::run() {
|
|||||||
std::format("Failed to begin the OpenXR Frame: {}", res));
|
std::format("Failed to begin the OpenXR Frame: {}", res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WindowShouldClose();
|
||||||
|
|
||||||
RenderLayerInfo render_layer_info{
|
RenderLayerInfo render_layer_info{
|
||||||
.predicted_display_time = frame_state.predictedDisplayTime,
|
.predicted_display_time = frame_state.predictedDisplayTime,
|
||||||
};
|
};
|
||||||
bool session_active = (m_xr.session_state == XR_SESSION_STATE_SYNCHRONIZED || m_xr.session_state == XR_SESSION_STATE_VISIBLE || m_xr.session_state == XR_SESSION_STATE_FOCUSED);
|
bool session_active =
|
||||||
|
(m_xr.session_state == XR_SESSION_STATE_SYNCHRONIZED ||
|
||||||
|
m_xr.session_state == XR_SESSION_STATE_VISIBLE ||
|
||||||
|
m_xr.session_state == XR_SESSION_STATE_FOCUSED);
|
||||||
if (session_active && frame_state.shouldRender) {
|
if (session_active && frame_state.shouldRender) {
|
||||||
auto rendered = this->render_layer(render_layer_info);
|
auto rendered = this->render_layer(render_layer_info, dt);
|
||||||
if (rendered) {
|
if (rendered) {
|
||||||
render_layer_info.layers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader *>(&render_layer_info.layer_projection));
|
render_layer_info.layers.push_back(
|
||||||
|
reinterpret_cast<XrCompositionLayerBaseHeader *>(
|
||||||
|
&render_layer_info.layer_projection));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XrFrameEndInfo frame_end_info {
|
XrFrameEndInfo frame_end_info{
|
||||||
.type = XR_TYPE_FRAME_END_INFO,
|
.type = XR_TYPE_FRAME_END_INFO,
|
||||||
.displayTime = frame_state.predictedDisplayTime,
|
.displayTime = frame_state.predictedDisplayTime,
|
||||||
.environmentBlendMode = m_xr.environment_blend_mode,
|
.environmentBlendMode = m_xr.environment_blend_mode,
|
||||||
@@ -1492,6 +1702,14 @@ void LunarWM::run() {
|
|||||||
if (xrEndFrame(m_xr.session, &frame_end_info) != XR_SUCCESS) {
|
if (xrEndFrame(m_xr.session, &frame_end_info) != XR_SUCCESS) {
|
||||||
throw std::runtime_error("Failed to end OpenXR frame");
|
throw std::runtime_error("Failed to end OpenXR frame");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BeginDrawing();
|
||||||
|
EndDrawing();
|
||||||
|
|
||||||
|
if (eglMakeCurrent(m_wayland.egl_display, read, draw,
|
||||||
|
m_wayland.egl_context) == EGL_FALSE) {
|
||||||
|
throw std::runtime_error("Failed to eglMakeCurrent");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user