Organization, render cursor above all else
Signed-off-by: Slendi <slendi@socopon.com>
This commit is contained in:
662
src/LunarWM_render.c
Normal file
662
src/LunarWM_render.c
Normal file
@@ -0,0 +1,662 @@
|
||||
#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->gles_texture->has_alpha) {
|
||||
continue;
|
||||
}
|
||||
float rad = window->coord.r - 0.01f * (float)i;
|
||||
DrawTextureCyl2(tl->rl_texture, Vector3Zero(), window->coord, rad,
|
||||
this->cman->cfg.space.window_scale, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user