bit of a refactoring

This commit is contained in:
Mars 2024-11-17 18:49:23 -05:00
parent 4c82bbe275
commit cf7b42b95c
4 changed files with 227 additions and 166 deletions

View file

@ -1,3 +1,8 @@
[fmt]
src.github = "fmtlib/fmt"
fetch.github = "fmtlib/fmt"
[imgui]
src.git = "https://github.com/ocornut/imgui.git"
src.branch = "docking"
fetch.github = "ocornut/imgui"

View file

@ -23,6 +23,7 @@
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
// Include custom utility headers
#include "util/constants.hpp" // Constants definitions
#include "util/crosshair.hpp" // Crosshair definitions
#include "util/shaders.hpp" // Compiled shader code
#include "util/types.hpp" // Custom type definitions
@ -38,34 +39,7 @@ VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
#define VKFW_NO_STRUCT_CONSTRUCTORS // Use aggregate initialization for GLFW structs
#include "vkfw.hpp" // Include GLFW C++ bindings
// Constants for window dimensions
constexpr i32 WIDTH = 1920;
constexpr i32 HEIGHT = 1080;
// CAMERA_SPEED of camera movement
constexpr f64 CAMERA_SPEED = 1.0;
// Maximum number of frames that can be processed concurrently
constexpr i32 MAX_FRAMES_IN_FLIGHT = 2;
// Shader file paths
constexpr const char* VERTEX_SHADER_PATH = "shaders/vertex.glsl";
constexpr const char* FRAGMENT_SHADER_PATH = "shaders/fragment.glsl";
constexpr const char* CROSSHAIR_VERTEX_SHADER_PATH = "shaders/crosshair_vertex.glsl";
constexpr const char* CROSSHAIR_FRAGMENT_SHADER_PATH = "shaders/crosshair_fragment.glsl";
// Validation layers for debug builds
#ifndef NDEBUG
constexpr std::array<const char*, 1> validationLayers = { "VK_LAYER_KHRONOS_validation" };
#endif
// Required device extensions (platform-specific)
#ifdef __APPLE__
constexpr std::array<const char*, 2> deviceExtensions = { vk::KHRSwapchainExtensionName,
vk::KHRPortabilitySubsetExtensionName };
#else
constexpr std::array<const char*, 1> deviceExtensions = { vk::KHRSwapchainExtensionName };
#endif
using namespace constants;
/**
* @brief The Vulkan application class.
@ -204,9 +178,12 @@ class VulkanApp {
bool mCursorCaptured = true; ///< Flag indicating if cursor is captured
// ImGui-related state
f32 mCameraSpeed = CAMERA_SPEED; ///< Current camera speed
f32 mFieldOfView = 90.0F; ///< Current field of view
bool mWireframeMode = false; ///< Wireframe rendering mode
f32 mCameraSpeed = CAMERA_SPEED; ///< Current camera speed
f32 mFieldOfView = 90.0F; ///< Current field of view
bool mWireframeMode = false; ///< Wireframe rendering mode
f32 mLineWidth = 1.0F; ///< Line width for wireframe mode
f32 mMaxLineWidth = 1.0F; ///< Maximum supported line width
bool mWideLineSupport = false; ///< Whether wide lines are supported
/**
* @brief Struct to store queue family indices.
@ -551,6 +528,10 @@ class VulkanApp {
// copy and the changes won't be saved.
ImGuiIO& imGuiIO = ImGui::GetIO();
// Enable docking and viewports
imGuiIO.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
imGuiIO.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport
// Disable writing imgui.ini
imGuiIO.IniFilename = nullptr;
@ -617,116 +598,12 @@ class VulkanApp {
processInput(mWindow.get(), mCamera, static_cast<f32>(deltaTime), mCameraSpeed);
mView = mCamera.getViewMatrix();
if (currentFrame - lastFpsUpdate > 1.0) {
mWindow->setTitle(
fmt::format("Vulkan - {:.0f}FPS", static_cast<f32>(frameCounter / (currentFrame - lastFpsUpdate)))
);
lastFpsUpdate = currentFrame;
frameCounter = 0;
}
++frameCounter;
updateFrameStats(lastFpsUpdate, frameCounter);
vkfw::pollEvents();
// Start the ImGui frame
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// Create ImGui window with controls
ImGui::Begin("Controls");
// Camera Position
auto pos = mCamera.getPosition();
ImGui::Text("Camera Position: (%.2f, %.2f, %.2f)", pos.x, pos.y, pos.z);
// Camera Settings
if (ImGui::CollapsingHeader("Camera Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::SliderFloat("Camera Speed", &mCameraSpeed, 0.1F, 10.0F, "%.1f");
ImGui::SliderFloat("Field of View", &mFieldOfView, 45.0F, 120.0F, "%.1f");
if (ImGui::Button("Reset Camera")) {
mCamera = Camera(); // Reset to default position
}
}
// Rendering Settings
if (ImGui::CollapsingHeader("Rendering Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
// Wireframe Mode
if (ImGui::Checkbox("Wireframe Mode", &mWireframeMode)) {
recreateSwapChain();
}
// Line Width (only in wireframe mode)
static float LineWidth = 1.0F;
if (mWireframeMode) {
if (ImGui::SliderFloat("Line Width", &LineWidth, 1.0F, 5.0F, "%.1f")) {
// TODO: Update line width in pipeline
}
}
}
// Controls Help
if (ImGui::CollapsingHeader("Controls", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::BulletText("Use mouse to look around");
ImGui::BulletText("WASD to move horizontally");
ImGui::BulletText("Space/Shift to move up/down");
ImGui::BulletText("ESC to toggle mouse capture");
ImGui::BulletText("Tab to toggle this menu");
}
// Performance Metrics
if (ImGui::CollapsingHeader("Performance", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text(
"Application average %.3f ms/frame (%.1f FPS)",
static_cast<f64>(1000.0F / ImGui::GetIO().Framerate),
static_cast<f64>(ImGui::GetIO().Framerate)
);
// Memory Usage
ImGui::Separator();
ImGui::Text("Memory Usage:");
ImGui::Text(
"Vertex Buffer: %.2f MB",
(static_cast<double>(mVertices.size()) * static_cast<double>(sizeof(Vertex))) / (1024.0 * 1024.0)
);
ImGui::Text(
"Index Buffer: %.2f MB",
(static_cast<double>(mIndices.size()) * static_cast<double>(sizeof(uint32_t))) / (1024.0 * 1024.0)
);
// Camera Information
ImGui::Separator();
ImGui::Text("Camera Information:");
ImGui::Text(
"Position: (%.2f, %.2f, %.2f)",
mCamera.getPosition().x,
mCamera.getPosition().y,
mCamera.getPosition().z
);
ImGui::Text(
"Front Vector: (%.2f, %.2f, %.2f)", mCamera.getFront().x, mCamera.getFront().y, mCamera.getFront().z
);
ImGui::Text("Yaw: %.2f°, Pitch: %.2f°", mCamera.getYaw(), mCamera.getPitch());
// Light Controls
ImGui::Separator();
ImGui::Text("Light Controls:");
// Light Position
ImGui::DragFloat3("Light Position", &mLightSettings.position.x, 0.1F, -10.0F, 10.0F);
// Light Color
ImGui::ColorEdit3("Light Color", &mLightSettings.color.x);
// Light Strengths
ImGui::SliderFloat("Ambient Strength", &mLightSettings.ambient_strength, 0.0F, 1.0F);
ImGui::SliderFloat("Specular Strength", &mLightSettings.specular_strength, 0.0F, 1.0F);
}
ImGui::End();
// Render ImGui
ImGui::Render();
setupImGuiFrame();
renderImGuiControls();
finalizeImGuiFrame();
drawFrame();
}
@ -734,6 +611,138 @@ class VulkanApp {
mDevice->waitIdle();
}
fn renderImGuiControls() -> void {
// Camera Settings
if (ImGui::CollapsingHeader("Camera Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::SliderFloat("Camera Speed", &mCameraSpeed, 0.1F, 10.0F, "%.1f");
ImGui::SliderFloat("Field of View", &mFieldOfView, 45.0F, 120.0F, "%.1f");
if (ImGui::Button("Reset Camera")) {
mCamera = Camera();
}
}
// Rendering Settings
if (ImGui::CollapsingHeader("Rendering Settings", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::Checkbox("Wireframe Mode", &mWireframeMode)) {
recreateSwapChain();
}
if (mWireframeMode) {
if (mWideLineSupport) {
if (ImGui::SliderFloat("Line Width", &mLineWidth, 1.0F, mMaxLineWidth, "%.1f")) {
mDevice->waitIdle();
createGraphicsPipeline();
}
} else {
ImGui::TextColored(ImVec4(1.0F, 0.5F, 0.5F, 1.0F), "Wide lines not supported on this device");
mLineWidth = 1.0F;
}
}
}
renderControlsHelp();
renderPerformanceMetrics();
}
static fn renderControlsHelp() -> void {
if (ImGui::CollapsingHeader("Controls", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::BulletText("Use mouse to look around");
ImGui::BulletText("WASD to move horizontally");
ImGui::BulletText("Space/Shift to move up/down");
ImGui::BulletText("ESC to toggle mouse capture");
ImGui::BulletText("Tab to toggle this menu");
}
}
fn renderPerformanceMetrics() -> void {
if (ImGui::CollapsingHeader("Performance", ImGuiTreeNodeFlags_DefaultOpen)) {
renderFrameMetrics();
renderMemoryUsage();
renderCameraInfo();
renderLightControls();
}
}
static fn renderFrameMetrics() -> void {
ImGui::Text(
"Application average %.3f ms/frame (%.1f FPS)",
static_cast<f64>(1000.0F / ImGui::GetIO().Framerate),
static_cast<f64>(ImGui::GetIO().Framerate)
);
}
fn renderMemoryUsage() -> void {
ImGui::Separator();
ImGui::Text("Memory Usage:");
ImGui::Text(
"Vertex Buffer: %.2f MB",
(static_cast<double>(mVertices.size()) * static_cast<double>(sizeof(Vertex))) / (1024.0 * 1024.0)
);
ImGui::Text(
"Index Buffer: %.2f MB",
(static_cast<double>(mIndices.size()) * static_cast<double>(sizeof(uint32_t))) / (1024.0 * 1024.0)
);
}
fn renderCameraInfo() -> void {
ImGui::Separator();
ImGui::Text("Camera Information:");
ImGui::Text(
"Position: (%.2f, %.2f, %.2f)",
mCamera.getPosition().x,
mCamera.getPosition().y,
mCamera.getPosition().z
);
ImGui::Text(
"Front Vector: (%.2f, %.2f, %.2f)", mCamera.getFront().x, mCamera.getFront().y, mCamera.getFront().z
);
ImGui::Text("Yaw: %.2f°, Pitch: %.2f°", mCamera.getYaw(), mCamera.getPitch());
}
fn renderLightControls() -> void {
ImGui::Separator();
ImGui::Text("Light Controls:");
// Light Position
ImGui::DragFloat3("Light Position", &mLightSettings.position.x, 0.1F, -10.0F, 10.0F);
// Light Color
ImGui::ColorEdit3("Light Color", &mLightSettings.color.x);
// Light Strengths
ImGui::SliderFloat("Ambient Strength", &mLightSettings.ambient_strength, 0.0F, 1.0F);
ImGui::SliderFloat("Specular Strength", &mLightSettings.specular_strength, 0.0F, 1.0F);
}
fn updateFrameStats(f64& lastFpsUpdate, i32& frameCounter) -> void {
f64 currentFrame = vkfw::getTime();
if (currentFrame - lastFpsUpdate > 1.0) {
mWindow->setTitle(
fmt::format("Vulkan - {:.0f}FPS", static_cast<f32>(frameCounter / (currentFrame - lastFpsUpdate)))
);
lastFpsUpdate = currentFrame;
frameCounter = 0;
}
++frameCounter;
}
static fn setupImGuiFrame() -> void {
ImGui_ImplVulkan_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGui::Begin("Controls");
}
static fn finalizeImGuiFrame() -> void {
ImGui::End();
ImGui::Render();
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
}
/**
* @brief Cleans up the swap chain resources.
*
@ -912,6 +921,16 @@ class VulkanApp {
if (isDeviceSuitable(device)) {
mPhysicalDevice = device;
mMsaaSamples = getMaxUsableSampleCount();
// Get the device properties for line width limits
vk::PhysicalDeviceProperties deviceProperties = device.getProperties();
mMaxLineWidth = deviceProperties.limits.lineWidthRange[1];
mWideLineSupport = deviceProperties.limits.lineWidthRange[1] > 1.0F;
#ifndef NDEBUG
fmt::println("Maximum supported line width: {}", mMaxLineWidth);
fmt::println("Wide lines supported: {}", mWideLineSupport ? "yes" : "no");
#endif
break;
}
}
@ -1249,7 +1268,7 @@ class VulkanApp {
.cullMode = vk::CullModeFlagBits::eBack,
.frontFace = vk::FrontFace::eCounterClockwise,
.depthBiasEnable = vk::False,
.lineWidth = mWireframeMode ? 2.0F : 1.0F, // Thicker lines in wireframe mode
.lineWidth = mWireframeMode ? mLineWidth : 1.0F, // Thicker lines in wireframe mode
};
vk::PipelineMultisampleStateCreateInfo multisampling {
@ -2045,7 +2064,7 @@ class VulkanApp {
attrib.vertices[static_cast<u32>((3 * index.vertex_index) + 2)],
},
.color = { 1.0F, 1.0F, 1.0F },
.texCoord = {
.tex_coord = {
attrib.texcoords[static_cast<u32>((2 * index.texcoord_index) + 0)],
1.0F - attrib.texcoords[static_cast<u32>((2 * index.texcoord_index) + 1)],
},

37
src/util/constants.hpp Normal file
View file

@ -0,0 +1,37 @@
#pragma once
#include <array>
#include <vulkan/vulkan.hpp>
#include "types.hpp"
namespace constants {
// Window dimensions
constexpr i32 WIDTH = 1920;
constexpr i32 HEIGHT = 1080;
// Camera settings
constexpr f64 CAMERA_SPEED = 1.0;
// Frame processing
constexpr i32 MAX_FRAMES_IN_FLIGHT = 2;
// Shader paths
constexpr const char* VERTEX_SHADER_PATH = "shaders/vertex.glsl";
constexpr const char* FRAGMENT_SHADER_PATH = "shaders/fragment.glsl";
constexpr const char* CROSSHAIR_VERTEX_SHADER_PATH = "shaders/crosshair_vertex.glsl";
constexpr const char* CROSSHAIR_FRAGMENT_SHADER_PATH = "shaders/crosshair_fragment.glsl";
// Validation layers
#ifndef NDEBUG
constexpr std::array<const char*, 1> validationLayers = { "VK_LAYER_KHRONOS_validation" };
#endif
// Device extensions
#ifdef __APPLE__
constexpr std::array<const char*, 2> deviceExtensions = { vk::KHRSwapchainExtensionName,
vk::KHRPortabilitySubsetExtensionName };
#else
constexpr std::array<const char*, 1> deviceExtensions = { vk::KHRSwapchainExtensionName };
#endif
}

View file

@ -27,7 +27,7 @@
struct Vertex {
glm::vec3 pos; ///< Position of the vertex in 3D space (x, y, z)
glm::vec3 color; ///< RGB color values, each component in range [0, 1]
glm::vec2 texCoord; ///< Texture coordinates (u, v) for texture mapping
glm::vec2 tex_coord; ///< Texture coordinates (u, v) for texture mapping
glm::vec3 normal; ///< Normal vector of the vertex
/**
@ -51,7 +51,8 @@ struct Vertex {
/**
* @brief Provides attribute descriptions for vertex data interpretation.
*
* @return std::array<vk::VertexInputAttributeDescription, 4> Array of descriptions for position, color, texture coordinates, and normal.
* @return std::array<vk::VertexInputAttributeDescription, 4> Array of descriptions for position, color,
* texture coordinates, and normal.
*
* The attribute descriptions specify:
* - Location indices (0 for position, 1 for color, 2 for texture coordinates, 3 for normal)
@ -62,29 +63,29 @@ struct Vertex {
static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 4> {
return {
vk::VertexInputAttributeDescription {
.location = 0,
.binding = 0,
.format = vk::Format::eR32G32B32Sfloat,
.offset = offsetof(Vertex, pos),
},
.location = 0,
.binding = 0,
.format = vk::Format::eR32G32B32Sfloat,
.offset = offsetof(Vertex, pos),
},
vk::VertexInputAttributeDescription {
.location = 1,
.binding = 0,
.format = vk::Format::eR32G32B32Sfloat,
.offset = offsetof(Vertex, color),
},
.location = 1,
.binding = 0,
.format = vk::Format::eR32G32B32Sfloat,
.offset = offsetof(Vertex, color),
},
vk::VertexInputAttributeDescription {
.location = 2,
.binding = 0,
.format = vk::Format::eR32G32Sfloat,
.offset = offsetof(Vertex, texCoord),
},
.location = 2,
.binding = 0,
.format = vk::Format::eR32G32Sfloat,
.offset = offsetof(Vertex, tex_coord),
},
vk::VertexInputAttributeDescription {
.location = 3,
.binding = 0,
.format = vk::Format::eR32G32B32Sfloat,
.offset = offsetof(Vertex, normal),
},
.location = 3,
.binding = 0,
.format = vk::Format::eR32G32B32Sfloat,
.offset = offsetof(Vertex, normal),
},
};
}
@ -95,7 +96,7 @@ struct Vertex {
* @return bool True if vertices are identical in position, color, texture coordinates, and normal.
*/
fn operator==(const Vertex& other) const->bool {
return pos == other.pos && color == other.color && texCoord == other.texCoord && normal == other.normal;
return pos == other.pos && color == other.color && tex_coord == other.tex_coord && normal == other.normal;
}
};
@ -117,8 +118,7 @@ namespace std {
*/
fn operator()(Vertex const& vertex) const->size_t {
return ((hash<glm::vec3>()(vertex.pos) ^ (hash<glm::vec3>()(vertex.color) << 1)) >> 1) ^
(hash<glm::vec2>()(vertex.texCoord) << 1) ^
(hash<glm::vec3>()(vertex.normal) << 2);
(hash<glm::vec2>()(vertex.tex_coord) << 1) ^ (hash<glm::vec3>()(vertex.normal) << 2);
}
};
}