673 lines
20 KiB
C
673 lines
20 KiB
C
#include "LunarWM_render.h"
|
|
|
|
#include "LunarWM_core.h"
|
|
#include "LunarWM_xr.h"
|
|
#include "common.h"
|
|
#include "vec.h"
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
static inline SphericalCoord get_forward_spherical_with_nearest(
|
|
Vector3 fwd, float r)
|
|
{
|
|
if (fabs(fwd.y) < 0.2f) {
|
|
fwd.y = 0;
|
|
}
|
|
Vector3 vec = Vector3Scale(Vector3Normalize(fwd), r);
|
|
return Vector3ToSpherical(vec);
|
|
}
|
|
|
|
static inline Matrix xr_matrix(XrPosef const pose)
|
|
{
|
|
Matrix const translation
|
|
= MatrixTranslate(pose.position.x, pose.position.y, pose.position.z);
|
|
Matrix const rotation = QuaternionToMatrix((Quaternion) {
|
|
pose.orientation.x,
|
|
pose.orientation.y,
|
|
pose.orientation.z,
|
|
pose.orientation.w,
|
|
});
|
|
return MatrixMultiply(rotation, translation);
|
|
}
|
|
|
|
static inline Matrix xr_projection_matrix(XrFovf const fov)
|
|
{
|
|
static_assert(RL_CULL_DISTANCE_FAR > RL_CULL_DISTANCE_NEAR);
|
|
|
|
Matrix matrix = {};
|
|
|
|
auto const near = (float)RL_CULL_DISTANCE_NEAR;
|
|
auto const far = (float)RL_CULL_DISTANCE_FAR;
|
|
|
|
float const tan_angle_left = tanf(fov.angleLeft);
|
|
float const tan_angle_right = tanf(fov.angleRight);
|
|
|
|
float const tan_angle_down = tanf(fov.angleDown);
|
|
float const tan_angle_up = tanf(fov.angleUp);
|
|
|
|
float const tan_angle_width = tan_angle_right - tan_angle_left;
|
|
float const 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 void DrawBillboardNoShear(
|
|
Camera3D const cam, Texture2D tex, Vector3 pos, float scale, Color tint)
|
|
{
|
|
Rectangle const src = { 0, 0, tex.width, tex.height };
|
|
|
|
Vector2 const size = { scale * fabsf(src.width / src.height), -scale };
|
|
Vector2 const origin = { size.x * 0.5f, size.y * 0.5f };
|
|
|
|
DrawBillboardPro(cam, tex, src, pos, cam.up, size, origin, 0.0f, tint);
|
|
}
|
|
|
|
void DrawTextureCyl(
|
|
Texture2D tex, Vector3 center, float radius, float scale, bool y_flip)
|
|
{
|
|
if (!tex.id || scale <= 0.0f || radius == 0.0f)
|
|
return;
|
|
|
|
float r = fabsf(radius);
|
|
float arc_len = (float)tex.width * scale; // arc length in world units
|
|
float theta = arc_len / r; // radians across the panel
|
|
float half_t = 0.5f * theta;
|
|
float half_h = 0.5f * (float)tex.height * scale;
|
|
|
|
// mid-angle around Y so the segment's middle sits at 'center'
|
|
float a0 = atan2f(center.x, center.z);
|
|
|
|
// shift so the cylinder surface midpoint matches 'center'
|
|
Vector3 mid_ref = (Vector3) { sinf(a0) * r, center.y, cosf(a0) * r };
|
|
Vector3 delta = Vector3Subtract(center, mid_ref);
|
|
|
|
// tessellation: about 3° per slice (min 8)
|
|
int slices = (int)ceilf(fmaxf(theta * (180.0f / PI) / 3.0f, 8.0f));
|
|
if (slices > 1024)
|
|
slices = 1024;
|
|
|
|
float vt = y_flip ? 1.0f : 0.0f;
|
|
float vb = y_flip ? 0.0f : 1.0f;
|
|
|
|
rlDrawRenderBatchActive(); // flush any prior state
|
|
rlSetTexture(tex.id);
|
|
rlDisableBackfaceCulling();
|
|
rlColor4ub(255, 255, 255, 255);
|
|
rlBegin(RL_QUADS);
|
|
|
|
for (int i = 0; i < slices; ++i) {
|
|
float u0 = (float)i / (float)slices;
|
|
float u1 = (float)(i + 1) / (float)slices;
|
|
|
|
float aL = a0 - half_t + theta * u0;
|
|
float aR = a0 - half_t + theta * u1;
|
|
|
|
Vector3 nL = (Vector3) { sinf(aL), 0.0f, cosf(aL) };
|
|
Vector3 nR = (Vector3) { sinf(aR), 0.0f, cosf(aR) };
|
|
if (radius < 0.0f) {
|
|
nL = Vector3Negate(nL);
|
|
nR = Vector3Negate(nR);
|
|
}
|
|
|
|
Vector3 pLT = Vector3Add(
|
|
(Vector3) { nL.x * r, center.y + half_h, nL.z * r }, delta);
|
|
Vector3 pLB = Vector3Add(
|
|
(Vector3) { nL.x * r, center.y - half_h, nL.z * r }, delta);
|
|
Vector3 pRT = Vector3Add(
|
|
(Vector3) { nR.x * r, center.y + half_h, nR.z * r }, delta);
|
|
Vector3 pRB = Vector3Add(
|
|
(Vector3) { nR.x * r, center.y - half_h, nR.z * r }, delta);
|
|
|
|
// match your flat-quad U flip (so WL textures look correct)
|
|
float U0 = 1.0f - u0;
|
|
float U1 = 1.0f - u1;
|
|
|
|
// one normal per-vertex (simple cylindrical)
|
|
rlNormal3f(nL.x, nL.y, nL.z);
|
|
rlTexCoord2f(U0, vt);
|
|
rlVertex3f(pLT.x, pLT.y, pLT.z);
|
|
rlNormal3f(nR.x, nR.y, nR.z);
|
|
rlTexCoord2f(U1, vt);
|
|
rlVertex3f(pRT.x, pRT.y, pRT.z);
|
|
rlNormal3f(nR.x, nR.y, nR.z);
|
|
rlTexCoord2f(U1, vb);
|
|
rlVertex3f(pRB.x, pRB.y, pRB.z);
|
|
rlNormal3f(nL.x, nL.y, nL.z);
|
|
rlTexCoord2f(U0, vb);
|
|
rlVertex3f(pLB.x, pLB.y, pLB.z);
|
|
}
|
|
|
|
rlEnd();
|
|
rlSetTexture(0);
|
|
rlEnableBackfaceCulling();
|
|
}
|
|
|
|
static void DrawTextureCyl2(Texture2D tex, Vector3 sphere_center,
|
|
SphericalCoord coord, float rad, float scale, bool y_flip)
|
|
{
|
|
if (!tex.id || scale <= 0.0f || rad == 0.0f)
|
|
return;
|
|
|
|
// midpoint on the sphere where the panel should sit (its center)
|
|
Vector3 fwd = SphericalToVector3(coord);
|
|
if (Vector3Length(fwd) < 1e-6f)
|
|
fwd = (Vector3) { 0, 0, 1 };
|
|
fwd = Vector3Normalize(fwd);
|
|
|
|
// build a local tangent frame at that point (right, up, forward)
|
|
Vector3 worldUp = (Vector3) { 0, 1, 0 };
|
|
if (fabsf(Vector3DotProduct(worldUp, fwd)) > 0.99f)
|
|
worldUp = (Vector3) { 1, 0, 0 };
|
|
Vector3 right = Vector3Normalize(Vector3CrossProduct(worldUp, fwd));
|
|
Vector3 up = Vector3Normalize(Vector3CrossProduct(fwd, right));
|
|
|
|
float r = fabsf(rad);
|
|
float arc_len = (float)tex.width * scale; // world units across
|
|
float theta = arc_len / r; // total horizontal FOV in radians
|
|
float half_t = 0.5f * theta;
|
|
float half_h = 0.5f * (float)tex.height * scale;
|
|
|
|
// shift so cylinder's surface midpoint lands exactly at coord.r from
|
|
// sphere_center
|
|
Vector3 delta = Vector3Add(sphere_center,
|
|
Vector3Add(Vector3Scale(fwd, coord.r - r), (Vector3) { 0, 0, 0 }));
|
|
|
|
// tessellation: about 3° per slice (min 8, max 1024)
|
|
int slices = (int)ceilf(fmaxf(theta * (180.0f / PI) / 3.0f, 8.0f));
|
|
if (slices > 1024)
|
|
slices = 1024;
|
|
|
|
float vt = y_flip ? 1.0f : 0.0f;
|
|
float vb = y_flip ? 0.0f : 1.0f;
|
|
|
|
rlDrawRenderBatchActive();
|
|
rlSetTexture(tex.id);
|
|
rlDisableBackfaceCulling();
|
|
rlColor4ub(255, 255, 255, 255);
|
|
rlBegin(RL_QUADS);
|
|
|
|
for (int i = 0; i < slices; ++i) {
|
|
float u0 = (float)i / (float)slices;
|
|
float u1 = (float)(i + 1) / (float)slices;
|
|
|
|
float aL = -half_t + theta * u0;
|
|
float aR = -half_t + theta * u1;
|
|
|
|
// local outward directions on the cylindrical surface
|
|
Vector3 nL = Vector3Add(
|
|
Vector3Scale(right, sinf(aL)), Vector3Scale(fwd, cosf(aL)));
|
|
Vector3 nR = Vector3Add(
|
|
Vector3Scale(right, sinf(aR)), Vector3Scale(fwd, cosf(aR)));
|
|
|
|
if (rad < 0.0f) {
|
|
nL = Vector3Negate(nL);
|
|
nR = Vector3Negate(nR);
|
|
}
|
|
|
|
// surface points (center band), then top/bottom by +/- up*half_h
|
|
Vector3 cL = Vector3Add(delta, Vector3Scale(nL, r));
|
|
Vector3 cR = Vector3Add(delta, Vector3Scale(nR, r));
|
|
|
|
Vector3 pLT = Vector3Add(cL, Vector3Scale(up, half_h));
|
|
Vector3 pLB = Vector3Add(cL, Vector3Scale(up, -half_h));
|
|
Vector3 pRT = Vector3Add(cR, Vector3Scale(up, half_h));
|
|
Vector3 pRB = Vector3Add(cR, Vector3Scale(up, -half_h));
|
|
|
|
// match the original horizontal flip so Wayland textures look correct
|
|
float U0 = 1.0f - u0;
|
|
float U1 = 1.0f - u1;
|
|
|
|
rlNormal3f(nL.x, nL.y, nL.z);
|
|
rlTexCoord2f(U0, vt);
|
|
rlVertex3f(pLT.x, pLT.y, pLT.z);
|
|
|
|
rlNormal3f(nR.x, nR.y, nR.z);
|
|
rlTexCoord2f(U1, vt);
|
|
rlVertex3f(pRT.x, pRT.y, pRT.z);
|
|
|
|
rlNormal3f(nR.x, nR.y, nR.z);
|
|
rlTexCoord2f(U1, vb);
|
|
rlVertex3f(pRB.x, pRB.y, pRB.z);
|
|
|
|
rlNormal3f(nL.x, nL.y, nL.z);
|
|
rlTexCoord2f(U0, vb);
|
|
rlVertex3f(pLB.x, pLB.y, pLB.z);
|
|
}
|
|
|
|
rlEnd();
|
|
rlSetTexture(0);
|
|
rlEnableBackfaceCulling();
|
|
}
|
|
|
|
static inline Vector3 RecenterPoint(LunarWM *wm, Vector3 p)
|
|
{
|
|
if (!wm->xr.recenter_active)
|
|
return p;
|
|
return Vector3Add(Vector3RotateByQuaternion(p, wm->xr.recenter_rot),
|
|
wm->xr.recenter_trans);
|
|
}
|
|
|
|
static inline Quaternion RecenterOrient(LunarWM *wm, Quaternion q)
|
|
{
|
|
if (!wm->xr.recenter_active)
|
|
return q;
|
|
return QuaternionMultiply(wm->xr.recenter_rot, q);
|
|
}
|
|
|
|
static LunarWM_Toplevel *find_toplevel(LunarWM *this, int id)
|
|
{
|
|
for (size_t i = 0; i < vector_size(this->wayland.v_toplevels); i++) {
|
|
auto *tl = this->wayland.v_toplevels[i];
|
|
if (tl->id == id)
|
|
return tl;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void LunarWM_render_hud(LunarWM *this, float /*dt*/, int hud_size)
|
|
{
|
|
ClearBackground((Color) { 0, 0, 0, 0 });
|
|
|
|
float const text_size = this->cman->cfg.displays.hud.font_size;
|
|
|
|
char const *txt
|
|
= TextFormat("WAYLAND_DISPLAY=%s", getenv("WAYLAND_DISPLAY"));
|
|
auto txt_w = MeasureText(txt, 24);
|
|
DrawText(
|
|
txt, hud_size / 2 - txt_w / 2, hud_size - text_size, text_size, WHITE);
|
|
|
|
txt = TextFormat("DISPLAY=%s", getenv("DISPLAY"));
|
|
txt_w = MeasureText(txt, text_size);
|
|
DrawText(txt, hud_size / 2 - txt_w / 2, hud_size - text_size * 2, text_size,
|
|
WHITE);
|
|
|
|
{
|
|
time_t t = time(NULL);
|
|
struct tm *tm_info = localtime(&t);
|
|
|
|
int hours = tm_info->tm_hour;
|
|
int minutes = tm_info->tm_min;
|
|
txt = TextFormat("%02d:%02d", hours, minutes);
|
|
txt_w = MeasureText(txt, 32);
|
|
DrawText(txt, hud_size / 2 - txt_w / 2, 0, text_size, WHITE);
|
|
}
|
|
}
|
|
|
|
void LunarWM_render_windows(LunarWM *this, bool alpha_check)
|
|
{
|
|
for (size_t i = 0; i
|
|
< vector_size(this->wm.workspaces[this->wm.active_workspace].v_windows);
|
|
i++) {
|
|
auto *window
|
|
= &this->wm.workspaces[this->wm.active_workspace].v_windows[i];
|
|
auto *tl = window->tl;
|
|
if (!tl || !tl->surface) {
|
|
continue;
|
|
}
|
|
if (tl->gles_texture) {
|
|
if (alpha_check && tl->composed_has_alpha) {
|
|
continue;
|
|
}
|
|
Texture2D tex = tl->rl_texture;
|
|
bool y_flip = false;
|
|
if (IsRenderTextureValid(tl->surface_rt)) {
|
|
tex = tl->surface_rt.texture;
|
|
tex.width = tl->rl_texture.width;
|
|
tex.height = tl->rl_texture.height;
|
|
y_flip = true;
|
|
}
|
|
if (!tex.id)
|
|
continue;
|
|
float rad = window->coord.r - 0.01f * (float)i;
|
|
DrawTextureCyl2(tex, Vector3Zero(), window->coord, rad,
|
|
this->cman->cfg.space.window_scale, y_flip);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void render_3d(LunarWM *this, float /*dt*/)
|
|
{
|
|
LunarWM_render_windows(this, true);
|
|
|
|
for (int h = 0; h < 2; ++h) {
|
|
auto *hand_info = &this->xr.hands[h];
|
|
for (size_t k = 0; k < XR_HAND_JOINT_COUNT_EXT; ++k) {
|
|
auto const *jl = &hand_info->joint_locations[k];
|
|
Vector3 pos = {
|
|
jl->pose.position.x,
|
|
jl->pose.position.y,
|
|
jl->pose.position.z,
|
|
};
|
|
pos = RecenterPoint(this, pos);
|
|
DrawSphere(pos, jl->radius, (Color) { 255, 0, 0, 255 });
|
|
}
|
|
}
|
|
|
|
Skybox_draw(this->renderer.skybox, this->renderer.camera.position);
|
|
|
|
rlEnableColorBlend();
|
|
rlDisableDepthMask(); // don't write depth
|
|
LunarWM_render_windows(this, false);
|
|
// TODO: Replace with actual cursor texture.
|
|
{ // Cursor
|
|
rlDrawRenderBatchActive();
|
|
rlDisableDepthTest();
|
|
Vector3 tip = SphericalToVector3(this->wm.pointer);
|
|
|
|
Vector3 n = Vector3Normalize(
|
|
Vector3Subtract(this->renderer.camera.position, tip));
|
|
Vector3 up_hint = (Vector3) { 0, 1, 0 };
|
|
if (fabsf(Vector3DotProduct(up_hint, n)) > 0.98f)
|
|
up_hint = (Vector3) { 1, 0, 0 };
|
|
Vector3 right = Vector3Normalize(Vector3CrossProduct(up_hint, n));
|
|
Vector3 up = Vector3Normalize(Vector3CrossProduct(n, right));
|
|
Vector3 down = Vector3Negate(up);
|
|
|
|
float const s = 0.03f;
|
|
|
|
Vector3 v1 = tip;
|
|
Vector3 v2 = Vector3Add(tip, Vector3Scale(down, s));
|
|
Vector3 v3 = Vector3Add(tip, Vector3Scale(right, s));
|
|
|
|
Vector3 normal = Vector3CrossProduct(
|
|
Vector3Subtract(v2, v1), Vector3Subtract(v3, v1));
|
|
if (Vector3DotProduct(normal, n) < 0.0f) {
|
|
Vector3 tmp = v2;
|
|
v2 = v3;
|
|
v3 = tmp;
|
|
}
|
|
|
|
DrawTriangle3D(v1, v2, v3, RED);
|
|
rlDrawRenderBatchActive();
|
|
rlEnableDepthTest();
|
|
}
|
|
rlEnableDepthMask();
|
|
|
|
if (IsTextureValid(this->renderer.hud_rt.texture)) {
|
|
rlDrawRenderBatchActive();
|
|
rlDisableDepthTest();
|
|
|
|
Vector3 camPos = this->renderer.camera.position;
|
|
Vector3 camDir = Vector3Normalize(
|
|
Vector3Subtract(this->renderer.camera.target, camPos));
|
|
Vector3 up = this->renderer.camera.up;
|
|
Vector3 right = Vector3Normalize(Vector3CrossProduct(camDir, up));
|
|
up = Vector3CrossProduct(right, camDir);
|
|
|
|
Vector3 center = Vector3Add(camPos, Vector3Scale(camDir, 0.6f));
|
|
|
|
float heightMeters = 0.10f;
|
|
|
|
DrawBillboardNoShear(this->renderer.camera,
|
|
this->renderer.hud_rt.texture, center, heightMeters * 5, WHITE);
|
|
rlDrawRenderBatchActive();
|
|
|
|
rlEnableDepthTest();
|
|
}
|
|
}
|
|
|
|
bool LunarWM_render_layer(LunarWM *this, LunarWM_RenderLayerInfo *info, float dt)
|
|
{
|
|
auto const view_count = (uint32_t)this->xr.view_configuration_views_count;
|
|
assert(view_count == 2);
|
|
|
|
XrView views[2] = {};
|
|
for (int i = 0; i < ARRAY_SZ(views); i++) {
|
|
views[i] = (XrView) {
|
|
.type = XR_TYPE_VIEW,
|
|
};
|
|
}
|
|
|
|
XrViewLocateInfo locInfo = {
|
|
.type = XR_TYPE_VIEW_LOCATE_INFO,
|
|
.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO,
|
|
.displayTime = info->predicted_display_time,
|
|
.space = this->xr.local_space,
|
|
};
|
|
|
|
XrViewState viewState = { .type = XR_TYPE_VIEW_STATE };
|
|
uint32_t located = 0;
|
|
if (xrLocateViews(
|
|
this->xr.session, &locInfo, &viewState, view_count, &located, views)
|
|
!= XR_SUCCESS
|
|
|| located != view_count) {
|
|
wlr_log(WLR_ERROR, "Failed to locate views");
|
|
return false;
|
|
}
|
|
|
|
// acquire swapchain images
|
|
auto *color_sc = &this->xr.swapchains.v_color[0];
|
|
auto *depth_sc = &this->xr.swapchains.v_depth[0];
|
|
|
|
uint32_t col_idx = 0;
|
|
uint32_t dep_idx = 0;
|
|
if (!LunarWM_xr_acquire_wait(color_sc->swapchain, &col_idx)
|
|
|| !LunarWM_xr_acquire_wait(depth_sc->swapchain, &dep_idx)) {
|
|
wlr_log(WLR_ERROR, "Swap-chain acquire failed");
|
|
return false;
|
|
}
|
|
|
|
GLuint color_tex = LunarWM_xr_get_swapchain_image(this, 0, col_idx);
|
|
GLuint depth_tex = LunarWM_xr_get_swapchain_image(this, 1, dep_idx);
|
|
|
|
// build FBO
|
|
if (this->renderer.fbo == 0u) {
|
|
glGenFramebuffers(1, &this->renderer.fbo);
|
|
}
|
|
glBindFramebuffer(GL_FRAMEBUFFER, this->renderer.fbo);
|
|
rlFramebufferAttach(this->renderer.fbo, color_tex,
|
|
RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
|
|
rlFramebufferAttach(this->renderer.fbo, depth_tex, RL_ATTACHMENT_DEPTH,
|
|
RL_ATTACHMENT_TEXTURE2D, 0);
|
|
assert(rlFramebufferComplete(this->renderer.fbo));
|
|
|
|
uint32_t const eye_w
|
|
= this->xr.a_view_configuration_views[0].recommendedImageRectWidth;
|
|
uint32_t const eye_h
|
|
= this->xr.a_view_configuration_views[0].recommendedImageRectHeight;
|
|
|
|
this->renderer.tmp_rt = (RenderTexture2D) {
|
|
.id = this->renderer.fbo,
|
|
.texture = { color_tex, (int)eye_w * view_count, (int)eye_h, 1, -1 },
|
|
.depth = { depth_tex, (int)eye_w * view_count, (int)eye_h, 1, -1 },
|
|
};
|
|
|
|
// head-space view matrix (matches rlOpenXR)
|
|
XrSpaceLocation headLoc = { .type = XR_TYPE_SPACE_LOCATION };
|
|
xrLocateSpace(this->xr.view_space, this->xr.local_space,
|
|
info->predicted_display_time, &headLoc);
|
|
auto const head_view = MatrixInvert(xr_matrix(headLoc.pose));
|
|
|
|
// per-eye projection + view-offset
|
|
Matrix const view_off_l
|
|
= MatrixMultiply(xr_matrix(views[0].pose), head_view);
|
|
Matrix const view_off_r
|
|
= MatrixMultiply(xr_matrix(views[1].pose), head_view);
|
|
|
|
Matrix const proj_r = xr_projection_matrix(views[0].fov);
|
|
Matrix const proj_l = xr_projection_matrix(views[1].fov);
|
|
|
|
int const hud_size = this->cman->cfg.displays.hud.size;
|
|
if (!IsTextureValid(this->renderer.hud_rt.texture)) {
|
|
this->renderer.hud_rt = LoadRenderTexture(hud_size, hud_size);
|
|
}
|
|
|
|
if (IsTextureValid(this->renderer.hud_rt.texture)) {
|
|
BeginTextureMode(this->renderer.hud_rt);
|
|
{
|
|
LunarWM_render_hud(this, dt, hud_size);
|
|
}
|
|
EndTextureMode();
|
|
}
|
|
|
|
// draw
|
|
|
|
if (!IsTextureValid(this->renderer.main_rt.texture)) {
|
|
this->renderer.main_rt = LoadRenderTexture(eye_w * view_count, eye_h);
|
|
}
|
|
|
|
BeginTextureMode(this->renderer.main_rt);
|
|
|
|
rlEnableStereoRender();
|
|
rlSetMatrixProjectionStereo(proj_r, proj_l);
|
|
rlSetMatrixViewOffsetStereo(view_off_r, view_off_l);
|
|
|
|
glViewport(0, 0, (GLsizei)eye_w * view_count, (GLsizei)eye_h);
|
|
rlClearColor(0, 0, 10, 255);
|
|
rlClearScreenBuffers();
|
|
|
|
for (int i = 0; i < 1; i++) {
|
|
XrTime const time = info->predicted_display_time;
|
|
|
|
XrSpaceLocation view_location = { .type = XR_TYPE_SPACE_LOCATION };
|
|
XrResult const result = xrLocateSpace(
|
|
this->xr.view_space, this->xr.local_space, time, &view_location);
|
|
if (result != XR_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if ((view_location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)
|
|
!= 0u) {
|
|
auto const pos = view_location.pose.position;
|
|
this->renderer.camera.position = (Vector3) { pos.x, pos.y, pos.z };
|
|
}
|
|
if ((view_location.locationFlags
|
|
& XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)
|
|
!= 0u) {
|
|
auto const rot = view_location.pose.orientation;
|
|
auto const forward
|
|
= Vector3RotateByQuaternion((Vector3) { 0, 0, -1 },
|
|
(Quaternion) { rot.x, rot.y, rot.z, rot.w });
|
|
auto const up = Vector3RotateByQuaternion((Vector3) { 0, 1, 0 },
|
|
(Quaternion) { rot.x, rot.y, rot.z, rot.w });
|
|
this->renderer.camera.target
|
|
= Vector3Add(this->renderer.camera.position, forward);
|
|
this->renderer.camera.up = up;
|
|
}
|
|
|
|
if (this->xr.recenter_active) {
|
|
Vector3 pos = this->renderer.camera.position;
|
|
Vector3 fwd = Vector3Normalize(
|
|
Vector3Subtract(this->renderer.camera.target, pos));
|
|
Vector3 up = this->renderer.camera.up;
|
|
|
|
pos = Vector3Add(
|
|
Vector3RotateByQuaternion(pos, this->xr.recenter_rot),
|
|
this->xr.recenter_trans);
|
|
fwd = Vector3RotateByQuaternion(fwd, this->xr.recenter_rot);
|
|
up = Vector3RotateByQuaternion(up, this->xr.recenter_rot);
|
|
|
|
this->renderer.camera.position = pos;
|
|
this->renderer.camera.target = Vector3Add(pos, fwd);
|
|
this->renderer.camera.up = up;
|
|
}
|
|
}
|
|
|
|
BeginMode3D(this->renderer.camera);
|
|
{
|
|
ClearBackground(RED);
|
|
render_3d(this, dt);
|
|
}
|
|
EndMode3D();
|
|
|
|
rlDisableStereoRender();
|
|
EndTextureMode();
|
|
|
|
if (!IsShaderValid(this->renderer.linear_srgb)) {
|
|
static char const linear_srgb[] = {
|
|
#embed "../assets/linear_srgb.fs"
|
|
, 0
|
|
};
|
|
this->renderer.linear_srgb = LoadShaderFromMemory(NULL, linear_srgb);
|
|
}
|
|
|
|
BeginTextureMode(this->renderer.tmp_rt);
|
|
rlDisableColorBlend();
|
|
ClearBackground(BLACK);
|
|
BeginShaderMode(this->renderer.linear_srgb);
|
|
DrawTexturePro(this->renderer.main_rt.texture,
|
|
(Rectangle) {
|
|
0,
|
|
0,
|
|
this->renderer.main_rt.texture.width,
|
|
-this->renderer.main_rt.texture.height,
|
|
},
|
|
(Rectangle) {
|
|
0,
|
|
0,
|
|
this->renderer.tmp_rt.texture.width,
|
|
this->renderer.tmp_rt.texture.height,
|
|
},
|
|
(Vector2) { 0, 0 }, 0, WHITE);
|
|
EndShaderMode();
|
|
EndTextureMode();
|
|
rlEnableColorBlend();
|
|
|
|
// release swapchain images
|
|
XrSwapchainImageReleaseInfo const ri
|
|
= { .type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
|
|
xrReleaseSwapchainImage(color_sc->swapchain, &ri);
|
|
xrReleaseSwapchainImage(depth_sc->swapchain, &ri);
|
|
|
|
// fill projection layer
|
|
info->layer_projection_views_count = view_count;
|
|
|
|
for (uint32_t i = 0; i < view_count; ++i) {
|
|
int32_t const xOff = i * eye_w;
|
|
auto *pv = &info->layer_projection_views[i];
|
|
pv->pose = views[i].pose;
|
|
pv->fov = views[i].fov;
|
|
pv->subImage.swapchain = color_sc->swapchain;
|
|
pv->subImage.imageRect.offset = (XrOffset2Di) { .x = xOff, .y = 0 };
|
|
pv->subImage.imageRect.extent
|
|
= (XrExtent2Di) { .width = eye_w, .height = eye_h };
|
|
pv->subImage.imageArrayIndex = 0;
|
|
}
|
|
|
|
info->layer_projection = (XrCompositionLayerProjection) {
|
|
.type = XR_TYPE_COMPOSITION_LAYER_PROJECTION,
|
|
.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT
|
|
| XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT,
|
|
.space = this->xr.local_space,
|
|
.viewCount = view_count,
|
|
.views = info->layer_projection_views,
|
|
};
|
|
|
|
info->layers_count = 0;
|
|
info->layers[info->layers_count++]
|
|
= (XrCompositionLayerBaseHeader *)&info->layer_projection;
|
|
|
|
if (this->renderer.first_frame) {
|
|
LunarWM_set_recenter_from_camera(this);
|
|
this->renderer.first_frame = false;
|
|
this->wm.pointer = get_forward_spherical_with_nearest(
|
|
this->renderer.camera.target, this->cman->cfg.space.radius);
|
|
}
|
|
|
|
return true;
|
|
}
|