mirror of
https://github.com/slendidev/lunar.git
synced 2026-01-30 16:28:58 +02:00
@@ -95,6 +95,7 @@ add_project_arguments(
|
||||
'-Wno-zero-as-null-pointer-constant',
|
||||
'-Wno-unused-macros',
|
||||
'-Wno-reserved-macro-identifier',
|
||||
'-Wno-reserved-identifier',
|
||||
'-Wno-suggest-override',
|
||||
'-Wno-macro-redefined',
|
||||
'-DVULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE',
|
||||
@@ -159,6 +160,7 @@ exe = executable('vr-compositor',
|
||||
'src/Pipeline.cpp',
|
||||
'src/Loader.cpp',
|
||||
'src/DescriptorWriter.cpp',
|
||||
'src/CPUTexture.cpp',
|
||||
'src/VulkanRenderer.cpp',
|
||||
'src/Application.cpp',
|
||||
],
|
||||
|
||||
@@ -19,6 +19,8 @@ shader_sources = files(
|
||||
'triangle_mesh.frag',
|
||||
'triangle_mesh.vert',
|
||||
'tex_image.frag',
|
||||
'skybox.frag',
|
||||
'skybox.vert',
|
||||
)
|
||||
|
||||
spirv_shaders = []
|
||||
|
||||
12
shaders/skybox.frag
Normal file
12
shaders/skybox.frag
Normal file
@@ -0,0 +1,12 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec3 in_dir;
|
||||
|
||||
layout (location = 0) out vec4 out_frag_color;
|
||||
|
||||
layout (set = 0, binding = 0) uniform samplerCube environment_map;
|
||||
|
||||
void main() {
|
||||
vec3 dir = normalize(in_dir);
|
||||
out_frag_color = vec4(texture(environment_map, dir).rgb, 1.0f);
|
||||
}
|
||||
14
shaders/skybox.vert
Normal file
14
shaders/skybox.vert
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 450
|
||||
|
||||
layout (location = 0) in vec3 in_position;
|
||||
layout (location = 0) out vec3 out_dir;
|
||||
|
||||
layout(push_constant) uniform constants {
|
||||
mat4 mvp;
|
||||
} PushConstants;
|
||||
|
||||
void main() {
|
||||
out_dir = in_position;
|
||||
vec4 pos = PushConstants.mvp * vec4(in_position, 1.0f);
|
||||
gl_Position = vec4(pos.xy, pos.w, pos.w);
|
||||
}
|
||||
@@ -5,13 +5,17 @@
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <numbers>
|
||||
#include <optional>
|
||||
#include <print>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__clang__)
|
||||
@@ -32,6 +36,7 @@
|
||||
#endif
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <SDL3/SDL_filesystem.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_mouse.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
@@ -46,6 +51,7 @@
|
||||
|
||||
#include <smath.hpp>
|
||||
|
||||
#include "GraphicsPipelineBuilder.h"
|
||||
#include "Util.h"
|
||||
#include "VulkanRenderer.h"
|
||||
|
||||
@@ -413,6 +419,8 @@ Application::Application()
|
||||
}
|
||||
|
||||
m_renderer = std::make_unique<VulkanRenderer>(m_window, m_logger);
|
||||
init_skybox_pipeline();
|
||||
init_test_meshes();
|
||||
|
||||
init_input();
|
||||
|
||||
@@ -427,6 +435,15 @@ Application::Application()
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
if (m_renderer) {
|
||||
for (auto const &mesh : m_test_meshes) {
|
||||
m_renderer->destroy_buffer(mesh->mesh_buffers.index_buffer);
|
||||
m_renderer->destroy_buffer(mesh->mesh_buffers.vertex_buffer);
|
||||
}
|
||||
}
|
||||
m_test_meshes.clear();
|
||||
|
||||
m_skybox_pipeline.reset();
|
||||
m_renderer.reset();
|
||||
|
||||
shutdown_input();
|
||||
@@ -437,7 +454,143 @@ Application::~Application()
|
||||
m_logger.info("App destroy done!");
|
||||
}
|
||||
|
||||
auto Application::init_skybox_pipeline() -> void
|
||||
{
|
||||
struct SkyboxPushConstants {
|
||||
smath::Mat4 mvp;
|
||||
};
|
||||
|
||||
Pipeline::Builder builder { m_renderer->device(), m_logger };
|
||||
|
||||
uint8_t skybox_vert_shader_data[] {
|
||||
#embed "skybox_vert.spv"
|
||||
};
|
||||
auto skybox_vert_shader
|
||||
= vkutil::load_shader_module(std::span<uint8_t>(skybox_vert_shader_data,
|
||||
sizeof(skybox_vert_shader_data)),
|
||||
m_renderer->device());
|
||||
if (!skybox_vert_shader) {
|
||||
m_logger.err("Failed to load skybox vert shader");
|
||||
}
|
||||
|
||||
uint8_t skybox_frag_shader_data[] {
|
||||
#embed "skybox_frag.spv"
|
||||
};
|
||||
auto skybox_frag_shader
|
||||
= vkutil::load_shader_module(std::span<uint8_t>(skybox_frag_shader_data,
|
||||
sizeof(skybox_frag_shader_data)),
|
||||
m_renderer->device());
|
||||
if (!skybox_frag_shader) {
|
||||
m_logger.err("Failed to load skybox frag shader");
|
||||
}
|
||||
|
||||
vk::PushConstantRange push_constant_range {};
|
||||
push_constant_range.stageFlags = vk::ShaderStageFlagBits::eVertex;
|
||||
push_constant_range.offset = 0;
|
||||
push_constant_range.size = sizeof(SkyboxPushConstants);
|
||||
|
||||
std::array push_constant_ranges { push_constant_range };
|
||||
builder.set_push_constant_ranges(push_constant_ranges);
|
||||
std::array descriptor_set_layouts {
|
||||
m_renderer->single_image_descriptor_layout(),
|
||||
};
|
||||
builder.set_descriptor_set_layouts(descriptor_set_layouts);
|
||||
|
||||
m_skybox_pipeline = builder.build_graphics(
|
||||
[&](GraphicsPipelineBuilder &pipeline_builder)
|
||||
-> GraphicsPipelineBuilder & {
|
||||
return pipeline_builder
|
||||
.set_shaders(skybox_vert_shader.get(), skybox_frag_shader.get())
|
||||
.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
|
||||
.set_polygon_mode(VK_POLYGON_MODE_FILL)
|
||||
.set_cull_mode(
|
||||
VK_CULL_MODE_FRONT_BIT, VK_FRONT_FACE_COUNTER_CLOCKWISE)
|
||||
.set_multisampling(static_cast<VkSampleCountFlagBits>(
|
||||
m_renderer->msaa_samples()))
|
||||
.disable_blending()
|
||||
.enable_depth_testing(false, VK_COMPARE_OP_LESS_OR_EQUAL)
|
||||
.set_color_attachment_format(
|
||||
static_cast<VkFormat>(m_renderer->draw_image_format()))
|
||||
.set_depth_format(
|
||||
static_cast<VkFormat>(m_renderer->depth_image_format()));
|
||||
});
|
||||
}
|
||||
|
||||
auto Application::binary_directory() const -> std::filesystem::path
|
||||
{
|
||||
auto const *base_path = SDL_GetBasePath();
|
||||
if (!base_path) {
|
||||
return std::filesystem::current_path();
|
||||
}
|
||||
std::filesystem::path base_dir { base_path };
|
||||
SDL_free(const_cast<char *>(base_path));
|
||||
return base_dir;
|
||||
}
|
||||
|
||||
auto Application::asset_directory() -> std::filesystem::path
|
||||
{
|
||||
std::vector<std::filesystem::path> candidates;
|
||||
|
||||
auto add_xdg_path = [&](std::filesystem::path const &base) {
|
||||
candidates.emplace_back(base / "lunar" / "assets");
|
||||
};
|
||||
|
||||
if (auto const *xdg_data_home = getenv("XDG_DATA_HOME");
|
||||
xdg_data_home && *xdg_data_home) {
|
||||
add_xdg_path(xdg_data_home);
|
||||
}
|
||||
|
||||
if (auto const *xdg_data_dirs = getenv("XDG_DATA_DIRS");
|
||||
xdg_data_dirs && *xdg_data_dirs) {
|
||||
std::string_view dirs_view { xdg_data_dirs };
|
||||
size_t start = 0;
|
||||
while (start <= dirs_view.size()) {
|
||||
size_t end = dirs_view.find(':', start);
|
||||
if (end == std::string_view::npos) {
|
||||
end = dirs_view.size();
|
||||
}
|
||||
auto segment = dirs_view.substr(start, end - start);
|
||||
if (!segment.empty()) {
|
||||
add_xdg_path(std::filesystem::path { segment });
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
} else {
|
||||
add_xdg_path("/usr/local/share");
|
||||
add_xdg_path("/usr/share");
|
||||
}
|
||||
|
||||
auto base_dir = binary_directory();
|
||||
candidates.emplace_back(base_dir / "assets");
|
||||
candidates.emplace_back(base_dir / "../assets");
|
||||
|
||||
for (auto const &candidate : candidates) {
|
||||
if (std::filesystem::exists(candidate)
|
||||
&& std::filesystem::is_directory(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
m_logger.warn(
|
||||
"Assets directory not found, using {}", (base_dir / "assets").string());
|
||||
return base_dir / "assets";
|
||||
}
|
||||
|
||||
auto Application::init_test_meshes() -> void
|
||||
{
|
||||
auto assets_dir = asset_directory();
|
||||
auto mesh_path = assets_dir / "basicmesh.glb";
|
||||
auto meshes = Mesh::load_gltf_meshes(*m_renderer, mesh_path);
|
||||
if (!meshes) {
|
||||
m_logger.err("Failed to load test mesh: {}", mesh_path.string());
|
||||
return;
|
||||
}
|
||||
|
||||
m_test_meshes = std::move(*meshes);
|
||||
}
|
||||
|
||||
auto Application::run() -> void
|
||||
|
||||
{
|
||||
SDL_Event e;
|
||||
|
||||
@@ -500,9 +653,6 @@ auto Application::run() -> void
|
||||
auto const target_distance { target_offset.magnitude() };
|
||||
auto const target_polar { PolarCoordinate::from_vec3(
|
||||
target_offset) };
|
||||
// Keep cursor angles in sync with externally-updated targets so
|
||||
// view aligns to the target direction. Preserve radius when the
|
||||
// target sits on top of the camera to avoid collapsing it.
|
||||
if (target_distance > 0.0f) {
|
||||
m_cursor.r = target_distance;
|
||||
m_cursor.theta = target_polar.theta;
|
||||
@@ -711,7 +861,7 @@ auto Application::run() -> void
|
||||
gl.set_transform(view_projection);
|
||||
|
||||
gl.set_texture();
|
||||
auto const &meshes { m_renderer->test_meshes() };
|
||||
auto const &meshes { m_test_meshes };
|
||||
if (meshes.size() > 2 && !meshes[2]->surfaces.empty()) {
|
||||
auto const &surface = meshes[2]->surfaces[0];
|
||||
gl.draw_mesh(meshes[2]->mesh_buffers,
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
#include <SDL3/SDL_video.h>
|
||||
#include <imgui.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
#include "Loader.h"
|
||||
#include "Logger.h"
|
||||
#include "Pipeline.h"
|
||||
#include "Types.h"
|
||||
|
||||
struct libinput;
|
||||
@@ -25,6 +28,7 @@ struct Application {
|
||||
~Application();
|
||||
|
||||
auto run() -> void;
|
||||
auto binary_directory() const -> std::filesystem::path;
|
||||
|
||||
auto mouse_captured(bool new_state) -> void;
|
||||
auto mouse_captured() const -> bool { return m_mouse_captured; }
|
||||
@@ -33,6 +37,9 @@ struct Application {
|
||||
|
||||
private:
|
||||
auto init_input() -> void;
|
||||
auto init_skybox_pipeline() -> void;
|
||||
auto init_test_meshes() -> void;
|
||||
auto asset_directory() -> std::filesystem::path;
|
||||
auto shutdown_input() -> void;
|
||||
auto process_libinput_events() -> void;
|
||||
auto handle_keyboard_event(libinput_event_keyboard *event) -> void;
|
||||
@@ -41,6 +48,8 @@ private:
|
||||
SDL_Window *m_window { nullptr };
|
||||
Logger m_logger { "Lunar" };
|
||||
std::unique_ptr<VulkanRenderer> m_renderer;
|
||||
Pipeline m_skybox_pipeline;
|
||||
std::vector<std::shared_ptr<Mesh>> m_test_meshes;
|
||||
|
||||
udev *m_udev { nullptr };
|
||||
libinput *m_libinput { nullptr };
|
||||
|
||||
79
src/CPUTexture.cpp
Normal file
79
src/CPUTexture.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "CPUTexture.h"
|
||||
|
||||
#include <format>
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wreserved-identifier"
|
||||
# pragma clang diagnostic ignored "-Wcast-qual"
|
||||
# pragma clang diagnostic ignored "-Wimplicit-fallthrough"
|
||||
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||
# pragma clang diagnostic ignored "-Wused-but-marked-unused"
|
||||
# pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
# pragma clang diagnostic ignored "-Wextra-semi-stmt"
|
||||
# pragma clang diagnostic ignored "-Wimplicit-int-conversion"
|
||||
# pragma clang diagnostic ignored "-Wsign-conversion"
|
||||
# pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
||||
# pragma clang diagnostic ignored "-Wconversion"
|
||||
# pragma clang diagnostic ignored "-Wcomma"
|
||||
# pragma clang diagnostic ignored "-Wdouble-promotion"
|
||||
# pragma clang diagnostic ignored "-Wimplicit-float-conversion"
|
||||
# pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
# pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
|
||||
# pragma clang diagnostic ignored "-Wsign-compare"
|
||||
# pragma clang diagnostic ignored "-Wfloat-equal"
|
||||
# pragma clang diagnostic ignored "-Wpacked"
|
||||
# pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
# pragma clang diagnostic ignored "-Wexit-time-destructors"
|
||||
# pragma clang diagnostic ignored "-Wglobal-constructors"
|
||||
# pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||
# pragma clang diagnostic ignored "-Wcast-align"
|
||||
# pragma clang diagnostic ignored "-Wcast-qual"
|
||||
# pragma clang diagnostic ignored "-Wshadow"
|
||||
# pragma clang diagnostic ignored "-Wnewline-eof"
|
||||
# pragma clang diagnostic ignored "-Wformat-nonliteral"
|
||||
# pragma clang diagnostic ignored "-Wswitch-default"
|
||||
# pragma clang diagnostic ignored "-Wswitch-enum"
|
||||
# pragma clang diagnostic ignored "-Wcovered-switch-default"
|
||||
# pragma clang diagnostic ignored "-Wdocumentation"
|
||||
# pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
||||
# pragma clang diagnostic ignored "-Wextra-semi"
|
||||
# pragma clang diagnostic ignored "-Wundef"
|
||||
# pragma clang diagnostic ignored "-Wreserved-macro-identifier"
|
||||
# pragma clang diagnostic ignored "-Wc++98-compat"
|
||||
# pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
|
||||
# pragma clang diagnostic ignored "-Wweak-vtables"
|
||||
# pragma clang diagnostic ignored "-Wswitch"
|
||||
# pragma clang diagnostic ignored "-Wunused-macros"
|
||||
# pragma clang diagnostic ignored "-Wextra"
|
||||
#endif
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "../thirdparty/stb/stb_image.h"
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
CPUTexture::CPUTexture(std::filesystem::path const &path)
|
||||
{
|
||||
int width_out = 0;
|
||||
int height_out = 0;
|
||||
int channels_out = 0;
|
||||
stbi_uc *data = stbi_load(path.string().c_str(), &width_out, &height_out,
|
||||
&channels_out, STBI_rgb_alpha);
|
||||
if (!data) {
|
||||
throw std::runtime_error(
|
||||
std::format("Failed to load texture: {}", path.string()));
|
||||
}
|
||||
|
||||
width = static_cast<uint32_t>(width_out);
|
||||
height = static_cast<uint32_t>(height_out);
|
||||
format = vk::Format::eR8G8B8A8Unorm;
|
||||
pixels.assign(data, data + (width * height * 4));
|
||||
stbi_image_free(data);
|
||||
}
|
||||
|
||||
} // namespace Lunar
|
||||
20
src/CPUTexture.h
Normal file
20
src/CPUTexture.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace Lunar {
|
||||
|
||||
struct CPUTexture {
|
||||
std::vector<uint8_t> pixels;
|
||||
uint32_t width { 0 };
|
||||
uint32_t height { 0 };
|
||||
vk::Format format { vk::Format::eR8G8B8A8Unorm };
|
||||
|
||||
explicit CPUTexture(std::filesystem::path const &path);
|
||||
};
|
||||
|
||||
} // namespace Lunar
|
||||
@@ -1302,15 +1302,7 @@ auto VulkanRenderer::default_data_init() -> void
|
||||
|
||||
m_vk.rectangle = upload_mesh(rect_indices, rect_vertices);
|
||||
|
||||
m_vk.test_meshes
|
||||
= Mesh::load_gltf_meshes(*this, "assets/basicmesh.glb").value();
|
||||
|
||||
m_vk.deletion_queue.emplace([&]() {
|
||||
for (auto &mesh : m_vk.test_meshes) {
|
||||
destroy_buffer(mesh->mesh_buffers.index_buffer);
|
||||
destroy_buffer(mesh->mesh_buffers.vertex_buffer);
|
||||
}
|
||||
|
||||
destroy_buffer(m_vk.rectangle.index_buffer);
|
||||
destroy_buffer(m_vk.rectangle.vertex_buffer);
|
||||
});
|
||||
@@ -2162,6 +2154,14 @@ auto VulkanRenderer::create_image(void const *data, vk::Extent3D size,
|
||||
return new_image;
|
||||
}
|
||||
|
||||
auto VulkanRenderer::create_image(CPUTexture const &texture,
|
||||
vk::ImageUsageFlags flags, bool mipmapped) -> AllocatedImage
|
||||
{
|
||||
vk::Extent3D size { texture.width, texture.height, 1 };
|
||||
return create_image(
|
||||
texture.pixels.data(), size, texture.format, flags, mipmapped);
|
||||
}
|
||||
|
||||
auto VulkanRenderer::destroy_image(AllocatedImage const &img) -> void
|
||||
{
|
||||
if (img.image_view) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include "CPUTexture.h"
|
||||
#include "Colors.h"
|
||||
#include "DeletionQueue.h"
|
||||
#include "Loader.h"
|
||||
@@ -140,14 +141,13 @@ struct VulkanRenderer {
|
||||
bool clear_frame_descriptors = true) -> void;
|
||||
auto upload_mesh(std::span<uint32_t> indices, std::span<Vertex> vertices)
|
||||
-> GPUMeshBuffers;
|
||||
auto destroy_buffer(AllocatedBuffer const &buffer) -> void;
|
||||
auto create_image(CPUTexture const &texture, vk::ImageUsageFlags flags,
|
||||
bool mipmapped = false) -> AllocatedImage;
|
||||
auto rectangle_mesh() const -> GPUMeshBuffers const &
|
||||
{
|
||||
return m_vk.rectangle;
|
||||
}
|
||||
auto test_meshes() const -> std::vector<std::shared_ptr<Mesh>> const &
|
||||
{
|
||||
return m_vk.test_meshes;
|
||||
}
|
||||
auto white_texture() const -> AllocatedImage const &
|
||||
{
|
||||
return m_vk.white_image;
|
||||
@@ -167,6 +167,23 @@ struct VulkanRenderer {
|
||||
auto draw_extent() const -> vk::Extent2D { return m_vk.draw_extent; }
|
||||
auto mesh_pipeline() -> Pipeline & { return m_vk.mesh_pipeline; }
|
||||
auto triangle_pipeline() -> Pipeline & { return m_vk.triangle_pipeline; }
|
||||
auto device() const -> vk::Device { return m_device; }
|
||||
auto draw_image_format() const -> vk::Format
|
||||
{
|
||||
return m_vk.draw_image.format;
|
||||
}
|
||||
auto depth_image_format() const -> vk::Format
|
||||
{
|
||||
return m_vk.depth_image.format;
|
||||
}
|
||||
auto msaa_samples() const -> vk::SampleCountFlagBits
|
||||
{
|
||||
return m_vk.msaa_samples;
|
||||
}
|
||||
auto single_image_descriptor_layout() const -> vk::DescriptorSetLayout
|
||||
{
|
||||
return m_vk.single_image_descriptor_layout;
|
||||
}
|
||||
auto gl_api() -> GL & { return gl; }
|
||||
auto get_screenshot() const -> std::optional<AllocatedImage>
|
||||
{
|
||||
@@ -249,7 +266,6 @@ private:
|
||||
|
||||
auto create_buffer(size_t alloc_size, vk::BufferUsageFlags usage,
|
||||
VmaMemoryUsage memory_usage) -> AllocatedBuffer;
|
||||
auto destroy_buffer(AllocatedBuffer const &buffer) -> void;
|
||||
auto enqueue_render_command(RenderCommand &&command) -> void;
|
||||
auto process_render_commands() -> void;
|
||||
auto apply_antialiasing(AntiAliasingKind kind) -> void;
|
||||
@@ -326,8 +342,6 @@ private:
|
||||
|
||||
uint64_t frame_number { 0 };
|
||||
|
||||
std::vector<std::shared_ptr<Mesh>> test_meshes;
|
||||
|
||||
AllocatedImage white_image {};
|
||||
AllocatedImage black_image {};
|
||||
AllocatedImage gray_image {};
|
||||
|
||||
Reference in New Issue
Block a user