@@ -51,6 +51,7 @@ add_executable(${PROJECT_NAME})
|
|||||||
target_sources(${PROJECT_NAME} PUBLIC
|
target_sources(${PROJECT_NAME} PUBLIC
|
||||||
src/vec.c
|
src/vec.c
|
||||||
|
|
||||||
|
src/RayExt.c
|
||||||
src/Config.c
|
src/Config.c
|
||||||
src/LunarWM.c
|
src/LunarWM.c
|
||||||
src/main.c
|
src/main.c
|
||||||
|
|||||||
18
assets/linear_srgb.fs
Normal file
18
assets/linear_srgb.fs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
varying vec2 fragTexCoord;
|
||||||
|
varying vec4 fragColor;
|
||||||
|
uniform sampler2D texture0;
|
||||||
|
|
||||||
|
vec3 srgb_to_linear(vec3 c) {
|
||||||
|
bvec3 cutoff = lessThanEqual(c, vec3(0.04045));
|
||||||
|
vec3 low = c / 12.92;
|
||||||
|
vec3 high = pow((c + 0.055) / 1.055, vec3(2.4));
|
||||||
|
return mix(high, low, vec3(cutoff));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 c = texture2D(texture0, fragTexCoord) * fragColor;
|
||||||
|
c.rgb = srgb_to_linear(c.rgb); // decode to linear
|
||||||
|
gl_FragColor = c;
|
||||||
|
}
|
||||||
31
assets/skybox.fs
Normal file
31
assets/skybox.fs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#version 100
|
||||||
|
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
// Input vertex attributes (from vertex shader)
|
||||||
|
varying vec3 fragPosition;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform samplerCube environmentMap;
|
||||||
|
uniform bool vflipped;
|
||||||
|
uniform bool doGamma;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Fetch color from texture map
|
||||||
|
vec4 texelColor = vec4(0.0);
|
||||||
|
|
||||||
|
if (vflipped) texelColor = textureCube(environmentMap, vec3(fragPosition.x, -fragPosition.y, fragPosition.z));
|
||||||
|
else texelColor = textureCube(environmentMap, fragPosition);
|
||||||
|
|
||||||
|
vec3 color = vec3(texelColor.x, texelColor.y, texelColor.z);
|
||||||
|
|
||||||
|
if (doGamma) // Apply gamma correction
|
||||||
|
{
|
||||||
|
color = color/(color + vec3(1.0));
|
||||||
|
color = pow(color, vec3(1.0/2.2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate final fragment color
|
||||||
|
gl_FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
24
assets/skybox.vs
Normal file
24
assets/skybox.vs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#version 100
|
||||||
|
|
||||||
|
// Input vertex attributes
|
||||||
|
attribute vec3 vertexPosition;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform mat4 matProjection;
|
||||||
|
uniform mat4 matView;
|
||||||
|
|
||||||
|
// Output vertex attributes (to fragment shader)
|
||||||
|
varying vec3 fragPosition;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Calculate fragment position based on model transformations
|
||||||
|
fragPosition = vertexPosition;
|
||||||
|
|
||||||
|
// Remove translation from the view matrix
|
||||||
|
mat4 rotView = mat4(mat3(matView));
|
||||||
|
vec4 clipPos = matProjection*rotView*vec4(vertexPosition, 1.0);
|
||||||
|
|
||||||
|
// Calculate final vertex position
|
||||||
|
gl_Position = clipPos;
|
||||||
|
}
|
||||||
BIN
lunarwm/citrus_orchard_road_puresky_8k.hdr
Normal file
BIN
lunarwm/citrus_orchard_road_puresky_8k.hdr
Normal file
Binary file not shown.
BIN
lunarwm/citrus_orchard_road_puresky_8k.png
Normal file
BIN
lunarwm/citrus_orchard_road_puresky_8k.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 MiB |
BIN
lunarwm/cubemap.png
Normal file
BIN
lunarwm/cubemap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 MiB |
@@ -2,8 +2,6 @@ function main(kbd)
|
|||||||
return "Super-" .. kbd
|
return "Super-" .. kbd
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
input = {
|
input = {
|
||||||
keyboard = {
|
keyboard = {
|
||||||
@@ -29,4 +27,5 @@ return {
|
|||||||
resolution = { 2560, 1440 },
|
resolution = { 2560, 1440 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
cubemap = 'cubemap.png',
|
||||||
}
|
}
|
||||||
|
|||||||
40
src/Config.c
40
src/Config.c
@@ -321,6 +321,15 @@ int config_load_ref(lua_State *L, int idx, Config *out)
|
|||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "cubemap");
|
||||||
|
if (lua_isstring(L, -1)) {
|
||||||
|
char const *s = lua_tostring(L, -1);
|
||||||
|
if (s && s[0]) {
|
||||||
|
(void)dupstr(s, (char **)&out->cubemap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lua_pop(L, 1);
|
||||||
|
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -342,6 +351,11 @@ void config_unref(lua_State *L, Config *cfg)
|
|||||||
|
|
||||||
free(cfg->input.keyboard.xkb_options);
|
free(cfg->input.keyboard.xkb_options);
|
||||||
cfg->input.keyboard.xkb_options = NULL;
|
cfg->input.keyboard.xkb_options = NULL;
|
||||||
|
|
||||||
|
if (cfg->cubemap) {
|
||||||
|
free((void *)cfg->cubemap);
|
||||||
|
cfg->cubemap = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void config_trigger_ref(lua_State *L, Config *cfg, int ref)
|
void config_trigger_ref(lua_State *L, Config *cfg, int ref)
|
||||||
@@ -430,9 +444,33 @@ int config_manager_reload(ConfigManager *cm)
|
|||||||
|
|
||||||
if (load_config_file(cm->L, cm->path) != 0)
|
if (load_config_file(cm->L, cm->path) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int rc = config_load_ref(cm->L, -1, &cm->cfg);
|
int rc = config_load_ref(cm->L, -1, &cm->cfg);
|
||||||
lua_pop(cm->L, 1);
|
lua_pop(cm->L, 1);
|
||||||
return rc;
|
if (rc != 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (cm->cfg.cubemap && cm->cfg.cubemap[0] != '/') {
|
||||||
|
char const *slash = strrchr(cm->path, '/');
|
||||||
|
char const *dir = ".";
|
||||||
|
size_t dirlen = 1;
|
||||||
|
if (slash) {
|
||||||
|
dir = cm->path;
|
||||||
|
dirlen = (size_t)(slash - cm->path);
|
||||||
|
}
|
||||||
|
size_t n = dirlen + 1 + strlen(cm->cfg.cubemap)
|
||||||
|
+ 1; // dir + '/' + hdri + '\0'
|
||||||
|
char *full = (char *)malloc(n);
|
||||||
|
if (full) {
|
||||||
|
memcpy(full, dir, dirlen);
|
||||||
|
full[dirlen] = '/';
|
||||||
|
strcpy(full + dirlen + 1, cm->cfg.cubemap);
|
||||||
|
free((void *)cm->cfg.cubemap);
|
||||||
|
cm->cfg.cubemap = full;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_State *config_manager_lua(ConfigManager *cm) { return cm ? cm->L : NULL; }
|
lua_State *config_manager_lua(ConfigManager *cm) { return cm ? cm->L : NULL; }
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ typedef struct {
|
|||||||
Vector2 resolution;
|
Vector2 resolution;
|
||||||
} virtual;
|
} virtual;
|
||||||
} displays;
|
} displays;
|
||||||
|
|
||||||
|
char const *cubemap;
|
||||||
} Config;
|
} Config;
|
||||||
|
|
||||||
char const *get_config_path(void);
|
char const *get_config_path(void);
|
||||||
|
|||||||
244
src/LunarWM.c
244
src/LunarWM.c
@@ -1,5 +1,6 @@
|
|||||||
#include "LunarWM.h"
|
#include "LunarWM.h"
|
||||||
|
|
||||||
|
#include "RayExt.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
|
|
||||||
|
#include <wlr/backend/headless.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/backend/wayland.h>
|
#include <wlr/backend/wayland.h>
|
||||||
#include <wlr/render/allocator.h>
|
#include <wlr/render/allocator.h>
|
||||||
@@ -662,6 +664,157 @@ static void new_xdg_toplevel_listener_notify(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct vo_client_res {
|
||||||
|
struct wl_resource *res;
|
||||||
|
struct wl_list link; // vo_client_res.link
|
||||||
|
};
|
||||||
|
|
||||||
|
static void vo_send_initial(struct virtual_output *vo, struct wl_resource *res)
|
||||||
|
{
|
||||||
|
// wl_output v1..v4 ordering: geometry, mode, scale (v2+), name/desc (v4),
|
||||||
|
// done (v2+)
|
||||||
|
wl_output_send_geometry(res, vo->x, vo->y, vo->phys_w_mm, vo->phys_h_mm,
|
||||||
|
vo->subpixel, vo->make, vo->model, vo->transform);
|
||||||
|
|
||||||
|
uint32_t flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
|
||||||
|
wl_output_send_mode(res, flags, vo->width, vo->height, vo->refresh_mhz);
|
||||||
|
|
||||||
|
if (wl_resource_get_version(res) >= 2)
|
||||||
|
wl_output_send_scale(res, vo->scale);
|
||||||
|
|
||||||
|
if (wl_resource_get_version(res) >= 4) {
|
||||||
|
wl_output_send_name(res, vo->name);
|
||||||
|
wl_output_send_description(res, vo->desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wl_resource_get_version(res) >= 2)
|
||||||
|
wl_output_send_done(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vo_resource_destroy(struct wl_resource *res)
|
||||||
|
{
|
||||||
|
struct vo_client_res *cr = wl_resource_get_user_data(res);
|
||||||
|
if (!cr)
|
||||||
|
return;
|
||||||
|
wl_list_remove(&cr->link);
|
||||||
|
free(cr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wl_output requests (only release exists in v3+)
|
||||||
|
static void output_release(struct wl_client *client, struct wl_resource *res)
|
||||||
|
{
|
||||||
|
(void)client;
|
||||||
|
wl_resource_destroy(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_output_interface output_impl = {
|
||||||
|
.release = output_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void vo_bind(
|
||||||
|
struct wl_client *client, void *data, uint32_t version, uint32_t id)
|
||||||
|
{
|
||||||
|
struct virtual_output *vo = data;
|
||||||
|
uint32_t ver = version;
|
||||||
|
if (ver > 4)
|
||||||
|
ver = 4; // we implement up to v4
|
||||||
|
|
||||||
|
struct wl_resource *res
|
||||||
|
= wl_resource_create(client, &wl_output_interface, ver, id);
|
||||||
|
if (!res) {
|
||||||
|
wl_client_post_no_memory(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vo_client_res *cr = calloc(1, sizeof *cr);
|
||||||
|
if (!cr) {
|
||||||
|
wl_client_post_no_memory(client);
|
||||||
|
wl_resource_destroy(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cr->res = res;
|
||||||
|
wl_resource_set_implementation(res, &output_impl, cr, vo_resource_destroy);
|
||||||
|
wl_list_insert(&vo->clients, &cr->link);
|
||||||
|
|
||||||
|
vo_send_initial(vo, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vo_broadcast_mode(struct virtual_output *vo)
|
||||||
|
{
|
||||||
|
struct vo_client_res *cr;
|
||||||
|
wl_list_for_each(cr, &vo->clients, link)
|
||||||
|
{
|
||||||
|
struct wl_resource *res = cr->res;
|
||||||
|
uint32_t flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
|
||||||
|
wl_output_send_mode(res, flags, vo->width, vo->height, vo->refresh_mhz);
|
||||||
|
if (wl_resource_get_version(res) >= 2)
|
||||||
|
wl_output_send_done(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vo_broadcast_scale(struct virtual_output *vo)
|
||||||
|
{
|
||||||
|
struct vo_client_res *cr;
|
||||||
|
wl_list_for_each(cr, &vo->clients, link)
|
||||||
|
{
|
||||||
|
struct wl_resource *res = cr->res;
|
||||||
|
if (wl_resource_get_version(res) >= 2) {
|
||||||
|
wl_output_send_scale(res, vo->scale);
|
||||||
|
wl_output_send_done(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vo_destroy(struct virtual_output *vo)
|
||||||
|
{
|
||||||
|
// destroy client resources
|
||||||
|
struct vo_client_res *cr, *tmp;
|
||||||
|
wl_list_for_each_safe(cr, tmp, &vo->clients, link)
|
||||||
|
{
|
||||||
|
wl_resource_destroy(cr->res); // calls vo_resource_destroy
|
||||||
|
}
|
||||||
|
if (vo->global) {
|
||||||
|
wl_global_destroy(vo->global);
|
||||||
|
vo->global = NULL;
|
||||||
|
}
|
||||||
|
free(vo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct virtual_output *vo_create(struct wl_display *display,
|
||||||
|
char const *name, char const *desc, int32_t w, int32_t h,
|
||||||
|
int32_t refresh_mhz, int32_t scale, char const *make, char const *model)
|
||||||
|
{
|
||||||
|
struct virtual_output *vo = calloc(1, sizeof *vo);
|
||||||
|
if (!vo)
|
||||||
|
return NULL;
|
||||||
|
vo->display = display;
|
||||||
|
wl_list_init(&vo->clients);
|
||||||
|
|
||||||
|
vo->x = 0;
|
||||||
|
vo->y = 0;
|
||||||
|
vo->phys_w_mm = 0;
|
||||||
|
vo->phys_h_mm = 0; // unknown; set if you care
|
||||||
|
vo->width = w;
|
||||||
|
vo->height = h;
|
||||||
|
vo->refresh_mhz = refresh_mhz;
|
||||||
|
vo->scale = scale > 0 ? scale : 1;
|
||||||
|
vo->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
|
||||||
|
vo->transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
|
vo->make = make ? make : "Lunar";
|
||||||
|
vo->model = model ? model : "Virtual";
|
||||||
|
vo->name = name;
|
||||||
|
vo->desc = desc ? desc : name;
|
||||||
|
|
||||||
|
vo->global
|
||||||
|
= wl_global_create(display, &wl_output_interface, 4, vo, vo_bind);
|
||||||
|
if (!vo->global) {
|
||||||
|
free(vo);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
static bool init_wayland(LunarWM *this)
|
static bool init_wayland(LunarWM *this)
|
||||||
{
|
{
|
||||||
wlr_log_init(WLR_DEBUG, nullptr);
|
wlr_log_init(WLR_DEBUG, nullptr);
|
||||||
@@ -1413,10 +1566,36 @@ static bool init_xr(LunarWM *this)
|
|||||||
|
|
||||||
static void sync_config(LunarWM *this)
|
static void sync_config(LunarWM *this)
|
||||||
{
|
{
|
||||||
UnloadTexture(this->renderer.hud_rt.texture);
|
if (this->cman->cfg.cubemap) {
|
||||||
this->renderer.hud_rt.texture.id = 0;
|
Skybox_init(&this->renderer.skybox, this->cman->cfg.cubemap);
|
||||||
this->renderer.hud_rt.texture.width = 0;
|
} else {
|
||||||
this->renderer.hud_rt.texture.height = 0;
|
Skybox_destroy(&this->renderer.skybox);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsTextureValid(this->renderer.hud_rt.texture)) {
|
||||||
|
UnloadTexture(this->renderer.hud_rt.texture);
|
||||||
|
this->renderer.hud_rt.texture.id = 0;
|
||||||
|
this->renderer.hud_rt.texture.width = 0;
|
||||||
|
this->renderer.hud_rt.texture.height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->wayland.custom_out_virtual) {
|
||||||
|
vo_destroy(this->wayland.custom_out_virtual);
|
||||||
|
this->wayland.custom_out_virtual = NULL;
|
||||||
|
}
|
||||||
|
if (this->wayland.custom_out_hud) {
|
||||||
|
vo_destroy(this->wayland.custom_out_hud);
|
||||||
|
this->wayland.custom_out_hud = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vw = (int)this->cman->cfg.displays.virtual.resolution.x;
|
||||||
|
int vh = (int)this->cman->cfg.displays.virtual.resolution.y;
|
||||||
|
int hud = this->cman->cfg.displays.hud.size;
|
||||||
|
|
||||||
|
this->wayland.custom_out_virtual = vo_create(this->wayland.display,
|
||||||
|
"Virtual", "Virtual output", vw, vh, 60000, 1, "LunarWM", "Virtual");
|
||||||
|
this->wayland.custom_out_hud = vo_create(this->wayland.display, "HUD",
|
||||||
|
"HUD output", hud, hud, 60000, 1, "LunarWM", "HUD");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern char **environ;
|
extern char **environ;
|
||||||
@@ -1523,7 +1702,6 @@ bool LunarWM_init(LunarWM *this)
|
|||||||
lua_setglobal(L, "lunar");
|
lua_setglobal(L, "lunar");
|
||||||
|
|
||||||
config_manager_reload(this->cman);
|
config_manager_reload(this->cman);
|
||||||
sync_config(this);
|
|
||||||
|
|
||||||
this->renderer.center = this->cman->cfg.space.initial_center;
|
this->renderer.center = this->cman->cfg.space.initial_center;
|
||||||
}
|
}
|
||||||
@@ -1562,6 +1740,8 @@ bool LunarWM_init(LunarWM *this)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sync_config(this);
|
||||||
|
|
||||||
this->initialized = true;
|
this->initialized = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1651,6 +1831,15 @@ static void cleanup_wayland(LunarWM *this)
|
|||||||
{
|
{
|
||||||
assert(this);
|
assert(this);
|
||||||
|
|
||||||
|
if (this->wayland.custom_out_virtual) {
|
||||||
|
vo_destroy(this->wayland.custom_out_virtual);
|
||||||
|
this->wayland.custom_out_virtual = NULL;
|
||||||
|
}
|
||||||
|
if (this->wayland.custom_out_hud) {
|
||||||
|
vo_destroy(this->wayland.custom_out_hud);
|
||||||
|
this->wayland.custom_out_hud = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->wayland.xwayland) {
|
if (this->wayland.xwayland) {
|
||||||
wlr_xwayland_destroy(this->wayland.xwayland);
|
wlr_xwayland_destroy(this->wayland.xwayland);
|
||||||
this->wayland.xwayland = NULL;
|
this->wayland.xwayland = NULL;
|
||||||
@@ -2004,7 +2193,7 @@ void render_hud(LunarWM *this, float /*dt*/, int hud_size)
|
|||||||
|
|
||||||
void render_3d(LunarWM *this, float /*dt*/)
|
void render_3d(LunarWM *this, float /*dt*/)
|
||||||
{
|
{
|
||||||
DrawGrid(10, 1);
|
Skybox_draw(this->renderer.skybox, (Vector3) { 0, 0, 0 });
|
||||||
|
|
||||||
rlDisableBackfaceCulling();
|
rlDisableBackfaceCulling();
|
||||||
for (size_t i = 0; i < vector_size(this->wayland.v_toplevels); i++) {
|
for (size_t i = 0; i < vector_size(this->wayland.v_toplevels); i++) {
|
||||||
@@ -2033,7 +2222,7 @@ void render_3d(LunarWM *this, float /*dt*/)
|
|||||||
jl->pose.position.y,
|
jl->pose.position.y,
|
||||||
jl->pose.position.z,
|
jl->pose.position.z,
|
||||||
};
|
};
|
||||||
DrawSphere(pos, jl->radius, RED);
|
DrawSphere(pos, jl->radius, (Color) { 255, 0, 0, 255 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2133,13 +2322,14 @@ static bool render_layer(LunarWM *this, LunarWM_RenderLayerInfo *info, float dt)
|
|||||||
auto const head_view = MatrixInvert(xr_matrix(headLoc.pose));
|
auto const head_view = MatrixInvert(xr_matrix(headLoc.pose));
|
||||||
|
|
||||||
// per-eye projection + view-offset
|
// per-eye projection + view-offset
|
||||||
Matrix const proj_r = xr_projection_matrix(views[0].fov);
|
|
||||||
Matrix const proj_l = xr_projection_matrix(views[1].fov);
|
|
||||||
Matrix const view_off_l
|
Matrix const view_off_l
|
||||||
= MatrixMultiply(xr_matrix(views[0].pose), head_view);
|
= MatrixMultiply(xr_matrix(views[0].pose), head_view);
|
||||||
Matrix const view_off_r
|
Matrix const view_off_r
|
||||||
= MatrixMultiply(xr_matrix(views[1].pose), head_view);
|
= 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;
|
int const hud_size = this->cman->cfg.displays.hud.size;
|
||||||
if (!IsTextureValid(this->renderer.hud_rt.texture)) {
|
if (!IsTextureValid(this->renderer.hud_rt.texture)) {
|
||||||
this->renderer.hud_rt = LoadRenderTexture(hud_size, hud_size);
|
this->renderer.hud_rt = LoadRenderTexture(hud_size, hud_size);
|
||||||
@@ -2154,10 +2344,15 @@ static bool render_layer(LunarWM *this, LunarWM_RenderLayerInfo *info, float dt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// draw
|
// draw
|
||||||
BeginTextureMode(this->renderer.tmp_rt);
|
|
||||||
|
if (!IsTextureValid(this->renderer.main_rt.texture)) {
|
||||||
|
this->renderer.main_rt = LoadRenderTexture(eye_w * view_count, eye_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
BeginTextureMode(this->renderer.main_rt);
|
||||||
|
|
||||||
rlEnableStereoRender();
|
rlEnableStereoRender();
|
||||||
rlSetMatrixProjectionStereo(proj_r, proj_l); // right, left (yes)
|
rlSetMatrixProjectionStereo(proj_r, proj_l);
|
||||||
rlSetMatrixViewOffsetStereo(view_off_r, view_off_l);
|
rlSetMatrixViewOffsetStereo(view_off_r, view_off_l);
|
||||||
|
|
||||||
glViewport(0, 0, (GLsizei)eye_w * view_count, (GLsizei)eye_h);
|
glViewport(0, 0, (GLsizei)eye_w * view_count, (GLsizei)eye_h);
|
||||||
@@ -2202,6 +2397,33 @@ static bool render_layer(LunarWM *this, LunarWM_RenderLayerInfo *info, float dt)
|
|||||||
rlDisableStereoRender();
|
rlDisableStereoRender();
|
||||||
EndTextureMode();
|
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);
|
||||||
|
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();
|
||||||
|
|
||||||
// release swapchain images
|
// release swapchain images
|
||||||
XrSwapchainImageReleaseInfo const ri
|
XrSwapchainImageReleaseInfo const ri
|
||||||
= { .type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
|
= { .type = XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
|
||||||
|
|||||||
@@ -33,9 +33,27 @@
|
|||||||
#include <rlgl.h>
|
#include <rlgl.h>
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "RayExt.h"
|
||||||
|
|
||||||
struct LunarWM;
|
struct LunarWM;
|
||||||
|
|
||||||
|
typedef struct virtual_output {
|
||||||
|
struct wl_global *global;
|
||||||
|
struct wl_display *display;
|
||||||
|
struct wl_list clients; // vo_client_res.link
|
||||||
|
|
||||||
|
// state we advertise
|
||||||
|
int32_t x, y; // compositor space; keep 0,0 if not relevant
|
||||||
|
int32_t phys_w_mm, phys_h_mm;
|
||||||
|
int32_t width, height; // current mode
|
||||||
|
int32_t refresh_mhz; // mHz
|
||||||
|
int32_t scale;
|
||||||
|
enum wl_output_subpixel subpixel;
|
||||||
|
enum wl_output_transform transform;
|
||||||
|
char const *make, *model;
|
||||||
|
char const *name, *desc;
|
||||||
|
} LunarWM_VirtualOutput;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct LunarWM *server;
|
struct LunarWM *server;
|
||||||
|
|
||||||
@@ -143,6 +161,9 @@ typedef struct LunarWM {
|
|||||||
struct wl_listener xwayland_associate_tmp; // per-surface temp
|
struct wl_listener xwayland_associate_tmp; // per-surface temp
|
||||||
struct wl_listener xwayland_dissociate_tmp; // per-surface temp
|
struct wl_listener xwayland_dissociate_tmp; // per-surface temp
|
||||||
|
|
||||||
|
LunarWM_VirtualOutput *custom_out_virtual;
|
||||||
|
LunarWM_VirtualOutput *custom_out_hud;
|
||||||
|
|
||||||
LunarWM_Toplevel **v_toplevels;
|
LunarWM_Toplevel **v_toplevels;
|
||||||
int current_focus;
|
int current_focus;
|
||||||
} wayland;
|
} wayland;
|
||||||
@@ -175,10 +196,14 @@ typedef struct LunarWM {
|
|||||||
struct {
|
struct {
|
||||||
GLuint fbo;
|
GLuint fbo;
|
||||||
RenderTexture2D tmp_rt;
|
RenderTexture2D tmp_rt;
|
||||||
|
RenderTexture2D main_rt;
|
||||||
RenderTexture2D hud_rt;
|
RenderTexture2D hud_rt;
|
||||||
Camera3D camera;
|
Camera3D camera;
|
||||||
|
Shader linear_srgb;
|
||||||
|
|
||||||
Vector3 center;
|
Vector3 center;
|
||||||
|
|
||||||
|
Skybox skybox;
|
||||||
} renderer;
|
} renderer;
|
||||||
|
|
||||||
ConfigManager *cman;
|
ConfigManager *cman;
|
||||||
|
|||||||
109
src/RayExt.c
Normal file
109
src/RayExt.c
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#include "RayExt.h"
|
||||||
|
|
||||||
|
#include <raylib.h>
|
||||||
|
#include <raymath.h>
|
||||||
|
#include <rlgl.h>
|
||||||
|
|
||||||
|
#if defined(GRAPHICS_API_OPENGL_ES3)
|
||||||
|
static char const *SKYBOX_VS = "#version 300 es\n"
|
||||||
|
"precision mediump float;\n"
|
||||||
|
"layout(location=0) in vec3 vertexPosition;\n"
|
||||||
|
"uniform mat4 mvp;\n"
|
||||||
|
"out vec3 vDir;\n"
|
||||||
|
"void main(){ vDir=vertexPosition; "
|
||||||
|
"gl_Position=mvp*vec4(vertexPosition,1.0); }\n";
|
||||||
|
static char const *SKYBOX_FS = "#version 300 es\n"
|
||||||
|
"precision highp float;\n"
|
||||||
|
"in vec3 vDir;\n"
|
||||||
|
"uniform samplerCube environmentMap;\n"
|
||||||
|
"out vec4 finalColor;\n"
|
||||||
|
"void main(){ vec3 dir=normalize(vDir); "
|
||||||
|
"finalColor=texture(environmentMap, dir); }\n";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Skybox_init(Skybox *skybox, char const *fp)
|
||||||
|
{
|
||||||
|
if (skybox->ok) {
|
||||||
|
Skybox_destroy(skybox);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Load cubemap from a 3x4 cross image
|
||||||
|
Image img = LoadImage(fp);
|
||||||
|
if (img.width == 0 || img.height == 0) {
|
||||||
|
TraceLog(LOG_ERROR, "Skybox: failed to load image: %s", fp);
|
||||||
|
skybox->ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureCubemap cubemap
|
||||||
|
= LoadTextureCubemap(img, CUBEMAP_LAYOUT_AUTO_DETECT);
|
||||||
|
UnloadImage(img);
|
||||||
|
if (cubemap.id == 0) {
|
||||||
|
TraceLog(LOG_ERROR, "Skybox: failed to create cubemap from %s", fp);
|
||||||
|
skybox->ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Make an inward-facing cube mesh
|
||||||
|
Mesh m = GenMeshCube(
|
||||||
|
2.0f, 2.0f, 2.0f); // size doesn't matter; depth writes off
|
||||||
|
// Invert winding so we see the inside
|
||||||
|
for (int i = 0; i < m.triangleCount; ++i) {
|
||||||
|
unsigned short *idx = &m.indices[i * 3];
|
||||||
|
unsigned short tmp = idx[1];
|
||||||
|
idx[1] = idx[2];
|
||||||
|
idx[2] = tmp;
|
||||||
|
}
|
||||||
|
UploadMesh(&m, false);
|
||||||
|
Model cube = LoadModelFromMesh(m);
|
||||||
|
|
||||||
|
Shader sh = LoadShaderFromMemory(SKYBOX_VS, SKYBOX_FS);
|
||||||
|
|
||||||
|
// make raylib aware which sampler is the cubemap
|
||||||
|
sh.locs[SHADER_LOC_MAP_CUBEMAP] = GetShaderLocation(sh, "environmentMap");
|
||||||
|
|
||||||
|
cube.materials[0].shader = sh;
|
||||||
|
|
||||||
|
// put the cubemap in the expected material slot
|
||||||
|
cube.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = cubemap;
|
||||||
|
|
||||||
|
// nicer defaults
|
||||||
|
SetTextureWrap(cubemap, TEXTURE_WRAP_CLAMP);
|
||||||
|
SetTextureFilter(cubemap, TEXTURE_FILTER_BILINEAR);
|
||||||
|
|
||||||
|
skybox->cubemap = cubemap;
|
||||||
|
skybox->shader = sh;
|
||||||
|
skybox->cube = cube;
|
||||||
|
skybox->ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skybox_destroy(Skybox *skybox)
|
||||||
|
{
|
||||||
|
if (!skybox->ok)
|
||||||
|
return;
|
||||||
|
|
||||||
|
UnloadModel(skybox->cube); // also unloads Mesh
|
||||||
|
UnloadTexture(skybox->cubemap);
|
||||||
|
UnloadShader(skybox->shader);
|
||||||
|
|
||||||
|
*skybox = (Skybox) { 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skybox_draw(Skybox const skybox, Vector3 position)
|
||||||
|
{
|
||||||
|
if (!skybox.ok)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Render behind everything without writing depth; cull disabled so we see
|
||||||
|
// inside faces
|
||||||
|
rlDisableColorBlend();
|
||||||
|
rlDisableBackfaceCulling();
|
||||||
|
rlDisableDepthMask();
|
||||||
|
|
||||||
|
DrawModel(skybox.cube, position, 60.0f, (Color) { 255, 255, 255, 255 });
|
||||||
|
|
||||||
|
rlDrawRenderBatchActive();
|
||||||
|
rlEnableDepthMask();
|
||||||
|
rlEnableBackfaceCulling();
|
||||||
|
rlEnableColorBlend();
|
||||||
|
}
|
||||||
18
src/RayExt.h
Normal file
18
src/RayExt.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef RAYEXT_H
|
||||||
|
#define RAYEXT_H
|
||||||
|
|
||||||
|
#include <raylib.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool ok;
|
||||||
|
Model cube;
|
||||||
|
Shader shader;
|
||||||
|
TextureCubemap cubemap;
|
||||||
|
} Skybox;
|
||||||
|
|
||||||
|
void Skybox_init(Skybox *skybox, char const *fp);
|
||||||
|
void Skybox_destroy(Skybox *skybox);
|
||||||
|
|
||||||
|
void Skybox_draw(Skybox const skybox, Vector3 position);
|
||||||
|
|
||||||
|
#endif // RAYEXT_H
|
||||||
Reference in New Issue
Block a user