wegh
This commit is contained in:
parent
2f779d28ac
commit
4c82bbe275
8
shaders/crosshair_fragment.glsl
Normal file
8
shaders/crosshair_fragment.glsl
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 fragColor;
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
outColor = vec4(fragColor, 1.0);
|
||||||
|
}
|
11
shaders/crosshair_vertex.glsl
Normal file
11
shaders/crosshair_vertex.glsl
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 inPosition;
|
||||||
|
layout(location = 1) in vec3 inColor;
|
||||||
|
|
||||||
|
layout(location = 0) out vec3 fragColor;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(inPosition, 0.0, 1.0);
|
||||||
|
fragColor = inColor;
|
||||||
|
}
|
|
@ -1,12 +1,47 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(binding = 1) uniform sampler2D texSampler;
|
layout(binding = 3) uniform sampler2D texSampler;
|
||||||
|
|
||||||
|
layout(binding = 1) uniform LightInfo {
|
||||||
|
vec3 position;
|
||||||
|
vec3 color;
|
||||||
|
float ambient_strength;
|
||||||
|
float specular_strength;
|
||||||
|
} light;
|
||||||
|
|
||||||
|
layout(binding = 2) uniform CameraInfo {
|
||||||
|
vec3 position;
|
||||||
|
} camera;
|
||||||
|
|
||||||
layout(location = 0) in vec3 fragColor;
|
layout(location = 0) in vec3 fragColor;
|
||||||
layout(location = 1) in vec2 fragTexCoord;
|
layout(location = 1) in vec2 fragTexCoord;
|
||||||
|
layout(location = 2) in vec3 fragNormal;
|
||||||
|
layout(location = 3) in vec3 fragWorldPos;
|
||||||
|
|
||||||
layout(location = 0) out vec4 outColor;
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
outColor = texture(texSampler, fragTexCoord);
|
// Get base color from texture
|
||||||
|
vec4 texColor = texture(texSampler, fragTexCoord);
|
||||||
|
|
||||||
|
// Normalize vectors
|
||||||
|
vec3 normal = normalize(fragNormal);
|
||||||
|
vec3 lightDir = normalize(light.position - fragWorldPos);
|
||||||
|
vec3 viewDir = normalize(camera.position - fragWorldPos);
|
||||||
|
vec3 reflectDir = reflect(-lightDir, normal);
|
||||||
|
|
||||||
|
// Ambient
|
||||||
|
vec3 ambient = light.ambient_strength * light.color;
|
||||||
|
|
||||||
|
// Diffuse
|
||||||
|
float diff = max(dot(normal, lightDir), 0.0);
|
||||||
|
vec3 diffuse = diff * light.color;
|
||||||
|
|
||||||
|
// Specular
|
||||||
|
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
|
||||||
|
vec3 specular = light.specular_strength * spec * light.color;
|
||||||
|
|
||||||
|
// Combine lighting components
|
||||||
|
vec3 lighting = (ambient + diffuse + specular);
|
||||||
|
outColor = vec4(lighting * texColor.rgb, texColor.a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,20 @@ layout(binding = 0) uniform UniformBufferObject {
|
||||||
layout(location = 0) in vec3 inPosition;
|
layout(location = 0) in vec3 inPosition;
|
||||||
layout(location = 1) in vec3 inColor;
|
layout(location = 1) in vec3 inColor;
|
||||||
layout(location = 2) in vec2 inTexCoord;
|
layout(location = 2) in vec2 inTexCoord;
|
||||||
|
layout(location = 3) in vec3 inNormal;
|
||||||
|
|
||||||
layout(location = 0) out vec3 fragColor;
|
layout(location = 0) out vec3 fragColor;
|
||||||
layout(location = 1) out vec2 fragTexCoord;
|
layout(location = 1) out vec2 fragTexCoord;
|
||||||
|
layout(location = 2) out vec3 fragNormal;
|
||||||
|
layout(location = 3) out vec3 fragWorldPos;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0);
|
vec4 worldPos = ubo.model * vec4(inPosition, 1.0);
|
||||||
|
gl_Position = ubo.proj * ubo.view * worldPos;
|
||||||
|
|
||||||
|
// Transform normal to world space
|
||||||
|
fragNormal = mat3(transpose(inverse(ubo.model))) * inNormal;
|
||||||
|
fragWorldPos = worldPos.xyz;
|
||||||
fragColor = inColor;
|
fragColor = inColor;
|
||||||
fragTexCoord = inTexCoord;
|
fragTexCoord = inTexCoord;
|
||||||
}
|
}
|
||||||
|
|
545
src/main.cpp
545
src/main.cpp
|
@ -23,6 +23,7 @@
|
||||||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||||
|
|
||||||
// Include custom utility headers
|
// Include custom utility headers
|
||||||
|
#include "util/crosshair.hpp" // Crosshair definitions
|
||||||
#include "util/shaders.hpp" // Compiled shader code
|
#include "util/shaders.hpp" // Compiled shader code
|
||||||
#include "util/types.hpp" // Custom type definitions
|
#include "util/types.hpp" // Custom type definitions
|
||||||
#include "util/unique_image.hpp" // Custom image handling utilities
|
#include "util/unique_image.hpp" // Custom image handling utilities
|
||||||
|
@ -50,6 +51,8 @@ constexpr i32 MAX_FRAMES_IN_FLIGHT = 2;
|
||||||
// Shader file paths
|
// Shader file paths
|
||||||
constexpr const char* VERTEX_SHADER_PATH = "shaders/vertex.glsl";
|
constexpr const char* VERTEX_SHADER_PATH = "shaders/vertex.glsl";
|
||||||
constexpr const char* FRAGMENT_SHADER_PATH = "shaders/fragment.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
|
// Validation layers for debug builds
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
@ -127,6 +130,13 @@ class VulkanApp {
|
||||||
vk::UniquePipeline mGraphicsPipeline; ///< Graphics pipeline
|
vk::UniquePipeline mGraphicsPipeline; ///< Graphics pipeline
|
||||||
vk::UniquePipeline mOldPipeline; ///< Previous graphics pipeline for safe deletion
|
vk::UniquePipeline mOldPipeline; ///< Previous graphics pipeline for safe deletion
|
||||||
|
|
||||||
|
vk::UniquePipelineLayout mCrosshairPipelineLayout;
|
||||||
|
vk::UniquePipeline mCrosshairPipeline;
|
||||||
|
vk::UniqueBuffer mCrosshairVertexBuffer;
|
||||||
|
vk::UniqueDeviceMemory mCrosshairVertexBufferMemory;
|
||||||
|
vk::UniqueBuffer mCrosshairIndexBuffer;
|
||||||
|
vk::UniqueDeviceMemory mCrosshairIndexBufferMemory;
|
||||||
|
|
||||||
vk::UniqueCommandPool mCommandPool; ///< Command pool for allocating command buffers
|
vk::UniqueCommandPool mCommandPool; ///< Command pool for allocating command buffers
|
||||||
|
|
||||||
vk::UniqueImage mColorImage; ///< Color image
|
vk::UniqueImage mColorImage; ///< Color image
|
||||||
|
@ -154,11 +164,27 @@ class VulkanApp {
|
||||||
std::vector<vk::UniqueDeviceMemory> mUniformBuffersMemory; ///< Memory for uniform buffers
|
std::vector<vk::UniqueDeviceMemory> mUniformBuffersMemory; ///< Memory for uniform buffers
|
||||||
std::vector<void*> mUniformBuffersMapped; ///< Mapped uniform buffers
|
std::vector<void*> mUniformBuffersMapped; ///< Mapped uniform buffers
|
||||||
|
|
||||||
|
std::vector<vk::UniqueBuffer> mLightUniformBuffers; ///< Uniform buffers for light parameters
|
||||||
|
std::vector<vk::UniqueDeviceMemory> mLightUniformBuffersMemory; ///< Memory for light uniform buffers
|
||||||
|
std::vector<void*> mLightUniformBuffersMapped; ///< Mapped light uniform buffers
|
||||||
|
|
||||||
|
std::vector<vk::UniqueBuffer> mCameraUniformBuffers; ///< Uniform buffers for camera parameters
|
||||||
|
std::vector<vk::UniqueDeviceMemory> mCameraUniformBuffersMemory; ///< Memory for camera uniform buffers
|
||||||
|
std::vector<void*> mCameraUniformBuffersMapped; ///< Mapped camera uniform buffers
|
||||||
|
|
||||||
vk::UniqueDescriptorPool mDescriptorPool; ///< Descriptor pool for the application
|
vk::UniqueDescriptorPool mDescriptorPool; ///< Descriptor pool for the application
|
||||||
vk::UniqueDescriptorPool mImGuiDescriptorPool; ///< Separate descriptor pool for ImGui
|
vk::UniqueDescriptorPool mImGuiDescriptorPool; ///< Separate descriptor pool for ImGui
|
||||||
std::vector<vk::DescriptorSet> mDescriptorSets; ///< Descriptor sets for binding resources
|
std::vector<vk::DescriptorSet> mDescriptorSets; ///< Descriptor sets for binding resources
|
||||||
|
|
||||||
std::vector<vk::UniqueCommandBuffer> mCommandBuffers; ///< Command buffers for drawing
|
std::vector<vk::UniqueCommandBuffer> mCommandBuffers; ///< Command buffers for drawing commands
|
||||||
|
|
||||||
|
// Light settings
|
||||||
|
struct {
|
||||||
|
glm::vec3 position = glm::vec3(2.0F, 2.0F, 2.0F);
|
||||||
|
glm::vec3 color = glm::vec3(1.0F, 1.0F, 1.0F);
|
||||||
|
float ambient_strength = 0.1F;
|
||||||
|
float specular_strength = 0.5F;
|
||||||
|
} mLightSettings;
|
||||||
|
|
||||||
std::vector<vk::UniqueSemaphore>
|
std::vector<vk::UniqueSemaphore>
|
||||||
mImageAvailableSemaphores; ///< Signals that an image is available for rendering
|
mImageAvailableSemaphores; ///< Signals that an image is available for rendering
|
||||||
|
@ -222,6 +248,17 @@ class VulkanApp {
|
||||||
alignas(16) glm::mat4 proj; ///< Projection matrix
|
alignas(16) glm::mat4 proj; ///< Projection matrix
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LightInfo {
|
||||||
|
alignas(16) glm::vec3 position; ///< Light position
|
||||||
|
alignas(16) glm::vec3 color; ///< Light color
|
||||||
|
alignas(4) float ambient_strength; ///< Ambient strength
|
||||||
|
alignas(4) float specular_strength; ///< Specular strength
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CameraInfo {
|
||||||
|
alignas(16) glm::vec3 position; ///< Camera position
|
||||||
|
};
|
||||||
|
|
||||||
struct Camera {
|
struct Camera {
|
||||||
glm::dvec3 position;
|
glm::dvec3 position;
|
||||||
glm::dvec3 front;
|
glm::dvec3 front;
|
||||||
|
@ -231,19 +268,23 @@ class VulkanApp {
|
||||||
f64 pitch;
|
f64 pitch;
|
||||||
|
|
||||||
Camera()
|
Camera()
|
||||||
: position(2.0, 2.0, 2.0),
|
: position(2.0, 2.0, 0.5),
|
||||||
front(glm::normalize(glm::dvec3(-2.0, -2.0, -2.0))),
|
front(glm::normalize(glm::dvec3(0.0, 1.0, 0.0))),
|
||||||
up(0.0, 0.0, 1.0),
|
up(0.0, 0.0, 1.0),
|
||||||
right(glm::normalize(glm::cross(front, up))),
|
right(glm::normalize(glm::cross(front, glm::dvec3(0.0, 0.0, 1.0)))),
|
||||||
yaw(-135.0) // -135 degrees to match the initial front vector
|
yaw(180.0),
|
||||||
,
|
pitch(0.0) {
|
||||||
pitch(-35.26) // -35.26 degrees to match the initial front vector
|
|
||||||
{
|
|
||||||
updateCameraVectors();
|
updateCameraVectors();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] fn getPosition() const -> glm::dvec3 { return position; }
|
[[nodiscard]] fn getPosition() const -> glm::dvec3 { return position; }
|
||||||
|
|
||||||
|
[[nodiscard]] fn getFront() const -> glm::dvec3 { return front; }
|
||||||
|
|
||||||
|
[[nodiscard]] fn getYaw() const -> f64 { return yaw; }
|
||||||
|
|
||||||
|
[[nodiscard]] fn getPitch() const -> f64 { return pitch; }
|
||||||
|
|
||||||
[[nodiscard]] fn getViewMatrix() const -> glm::mat4 {
|
[[nodiscard]] fn getViewMatrix() const -> glm::mat4 {
|
||||||
return glm::lookAt(position, position + front, up);
|
return glm::lookAt(position, position + front, up);
|
||||||
}
|
}
|
||||||
|
@ -289,6 +330,12 @@ class VulkanApp {
|
||||||
yaw += xoffset * sensitivity;
|
yaw += xoffset * sensitivity;
|
||||||
pitch += yoffset * sensitivity;
|
pitch += yoffset * sensitivity;
|
||||||
|
|
||||||
|
// Clamp yaw to [-180, 180] range
|
||||||
|
if (yaw > 180.0)
|
||||||
|
yaw -= 360.0;
|
||||||
|
if (yaw < -180.0)
|
||||||
|
yaw += 360.0;
|
||||||
|
|
||||||
// Constrain pitch to avoid camera flipping
|
// Constrain pitch to avoid camera flipping
|
||||||
if (pitch > 89.0)
|
if (pitch > 89.0)
|
||||||
pitch = 89.0;
|
pitch = 89.0;
|
||||||
|
@ -348,9 +395,20 @@ class VulkanApp {
|
||||||
// Set window creation hints
|
// Set window creation hints
|
||||||
vkfw::WindowHints hints { .clientAPI = vkfw::ClientAPI::eNone }; // No OpenGL context
|
vkfw::WindowHints hints { .clientAPI = vkfw::ClientAPI::eNone }; // No OpenGL context
|
||||||
|
|
||||||
|
// Get the primary monitor and its resolution
|
||||||
|
vkfw::Monitor primaryMonitor = vkfw::getPrimaryMonitor();
|
||||||
|
const GLFWvidmode* videoMode = primaryMonitor.getVideoMode();
|
||||||
|
|
||||||
|
// Calculate window position to center it
|
||||||
|
i32 xpos = (videoMode->width - WIDTH) / 2;
|
||||||
|
i32 ypos = (videoMode->height - HEIGHT) / 2;
|
||||||
|
|
||||||
// Create the window
|
// Create the window
|
||||||
mWindow = vkfw::createWindowUnique(WIDTH, HEIGHT, "Vulkan", hints);
|
mWindow = vkfw::createWindowUnique(WIDTH, HEIGHT, "Vulkan", hints);
|
||||||
|
|
||||||
|
// Set window position
|
||||||
|
mWindow->setPos(xpos, ypos);
|
||||||
|
|
||||||
// Set the user pointer to this instance, allowing us to access it in callbacks
|
// Set the user pointer to this instance, allowing us to access it in callbacks
|
||||||
mWindow->setUserPointer(this);
|
mWindow->setUserPointer(this);
|
||||||
|
|
||||||
|
@ -359,22 +417,22 @@ class VulkanApp {
|
||||||
|
|
||||||
// Set up mouse callback
|
// Set up mouse callback
|
||||||
mWindow->callbacks()->on_cursor_move =
|
mWindow->callbacks()->on_cursor_move =
|
||||||
[this](const vkfw::Window& /*window*/, f64 xpos, f64 ypos) -> void {
|
[this](const vkfw::Window& /*window*/, f64 mouseX, f64 mouseY) -> void {
|
||||||
if (!mCursorCaptured)
|
if (!mCursorCaptured)
|
||||||
return; // Skip camera movement when cursor is not captured
|
return; // Skip camera movement when cursor is not captured
|
||||||
|
|
||||||
if (mFirstMouse) {
|
if (mFirstMouse) {
|
||||||
mLastX = xpos;
|
mLastX = mouseX;
|
||||||
mLastY = ypos;
|
mLastY = mouseY;
|
||||||
mFirstMouse = false;
|
mFirstMouse = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
f64 xoffset = xpos - mLastX;
|
f64 xoffset = mouseX - mLastX;
|
||||||
f64 yoffset = mLastY - ypos; // Reversed since y-coordinates range from bottom to top
|
f64 yoffset = mLastY - mouseY; // Reversed since y-coordinates range from bottom to top
|
||||||
|
|
||||||
mLastX = xpos;
|
mLastX = mouseX;
|
||||||
mLastY = ypos;
|
mLastY = mouseY;
|
||||||
|
|
||||||
mCamera.rotate(-xoffset, yoffset); // Invert xoffset for correct horizontal movement
|
mCamera.rotate(-xoffset, yoffset); // Invert xoffset for correct horizontal movement
|
||||||
};
|
};
|
||||||
|
@ -391,6 +449,7 @@ class VulkanApp {
|
||||||
mCursorCaptured = false;
|
mCursorCaptured = false;
|
||||||
window.set<vkfw::InputMode::eCursor>(vkfw::CursorMode::eNormal);
|
window.set<vkfw::InputMode::eCursor>(vkfw::CursorMode::eNormal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key == vkfw::Key::eR && action == vkfw::KeyAction::ePress) {
|
if (key == vkfw::Key::eR && action == vkfw::KeyAction::ePress) {
|
||||||
try {
|
try {
|
||||||
mDevice->waitIdle();
|
mDevice->waitIdle();
|
||||||
|
@ -454,6 +513,7 @@ class VulkanApp {
|
||||||
createRenderPass(); // Set up the render pass
|
createRenderPass(); // Set up the render pass
|
||||||
createDescriptorSetLayout(); // Create the descriptor set layout
|
createDescriptorSetLayout(); // Create the descriptor set layout
|
||||||
createGraphicsPipeline(); // Create the graphics pipeline
|
createGraphicsPipeline(); // Create the graphics pipeline
|
||||||
|
createCrosshairPipeline(); // Create the crosshair pipeline
|
||||||
createCommandPool(); // Create a command pool for allocating command buffers
|
createCommandPool(); // Create a command pool for allocating command buffers
|
||||||
createColorResources(); // Create resources for multisampling
|
createColorResources(); // Create resources for multisampling
|
||||||
createDepthResources(); // Create resources for depth testing
|
createDepthResources(); // Create resources for depth testing
|
||||||
|
@ -465,9 +525,12 @@ class VulkanApp {
|
||||||
createVertexBuffer(); // Create a buffer for vertex data
|
createVertexBuffer(); // Create a buffer for vertex data
|
||||||
createIndexBuffer(); // Create a buffer for index data
|
createIndexBuffer(); // Create a buffer for index data
|
||||||
createUniformBuffers(); // Create uniform buffers for shader parameters
|
createUniformBuffers(); // Create uniform buffers for shader parameters
|
||||||
|
createLightUniformBuffers(); // Create uniform buffers for light parameters
|
||||||
|
createCameraUniformBuffers(); // Create uniform buffers for camera parameters
|
||||||
createDescriptorPool(); // Create a descriptor pool
|
createDescriptorPool(); // Create a descriptor pool
|
||||||
createDescriptorSets(); // Allocate and update descriptor sets
|
createDescriptorSets(); // Allocate and update descriptor sets
|
||||||
createCommandBuffers(); // Create command buffers for rendering commands
|
createCommandBuffers(); // Create command buffers for rendering commands
|
||||||
|
createCrosshairBuffers(); // Create crosshair buffers
|
||||||
createSyncObjects(); // Create synchronization objects (semaphores and fences)
|
createSyncObjects(); // Create synchronization objects (semaphores and fences)
|
||||||
initImGui(); // Initialize Dear ImGui for GUI rendering
|
initImGui(); // Initialize Dear ImGui for GUI rendering
|
||||||
}
|
}
|
||||||
|
@ -564,6 +627,107 @@ class VulkanApp {
|
||||||
++frameCounter;
|
++frameCounter;
|
||||||
|
|
||||||
vkfw::pollEvents();
|
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();
|
||||||
|
|
||||||
drawFrame();
|
drawFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,13 +767,17 @@ class VulkanApp {
|
||||||
createImageViews();
|
createImageViews();
|
||||||
createRenderPass();
|
createRenderPass();
|
||||||
createGraphicsPipeline();
|
createGraphicsPipeline();
|
||||||
|
createCrosshairPipeline();
|
||||||
createColorResources();
|
createColorResources();
|
||||||
createDepthResources();
|
createDepthResources();
|
||||||
createFramebuffers();
|
createFramebuffers();
|
||||||
createUniformBuffers();
|
createUniformBuffers();
|
||||||
|
createLightUniformBuffers();
|
||||||
|
createCameraUniformBuffers();
|
||||||
createDescriptorPool();
|
createDescriptorPool();
|
||||||
createDescriptorSets();
|
createDescriptorSets();
|
||||||
createCommandBuffers();
|
createCommandBuffers();
|
||||||
|
createCrosshairBuffers();
|
||||||
|
|
||||||
mImagesInFlight.resize(mSwapChainImages.size(), nullptr);
|
mImagesInFlight.resize(mSwapChainImages.size(), nullptr);
|
||||||
}
|
}
|
||||||
|
@ -981,15 +1149,36 @@ class VulkanApp {
|
||||||
.pImmutableSamplers = nullptr,
|
.pImmutableSamplers = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::DescriptorSetLayoutBinding samplerLayoutBinding {
|
vk::DescriptorSetLayoutBinding lightLayoutBinding {
|
||||||
.binding = 1,
|
.binding = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.pImmutableSamplers = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::DescriptorSetLayoutBinding cameraLayoutBinding {
|
||||||
|
.binding = 2,
|
||||||
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.pImmutableSamplers = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::DescriptorSetLayoutBinding samplerLayoutBinding {
|
||||||
|
.binding = 3,
|
||||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||||
.descriptorCount = 1,
|
.descriptorCount = 1,
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
||||||
.pImmutableSamplers = nullptr,
|
.pImmutableSamplers = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
|
std::array<vk::DescriptorSetLayoutBinding, 4> bindings = {
|
||||||
|
uboLayoutBinding,
|
||||||
|
lightLayoutBinding,
|
||||||
|
cameraLayoutBinding,
|
||||||
|
samplerLayoutBinding,
|
||||||
|
};
|
||||||
|
|
||||||
vk::DescriptorSetLayoutCreateInfo layoutInfo {
|
vk::DescriptorSetLayoutCreateInfo layoutInfo {
|
||||||
.bindingCount = static_cast<u32>(bindings.size()),
|
.bindingCount = static_cast<u32>(bindings.size()),
|
||||||
|
@ -1033,7 +1222,7 @@ class VulkanApp {
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::VertexInputBindingDescription bindingDescription = Vertex::getBindingDescription();
|
vk::VertexInputBindingDescription bindingDescription = Vertex::getBindingDescription();
|
||||||
std::array<vk::VertexInputAttributeDescription, 3> attributeDescriptions =
|
std::array<vk::VertexInputAttributeDescription, 4> attributeDescriptions =
|
||||||
Vertex::getAttributeDescriptions();
|
Vertex::getAttributeDescriptions();
|
||||||
|
|
||||||
vk::PipelineVertexInputStateCreateInfo vertexInputInfo {
|
vk::PipelineVertexInputStateCreateInfo vertexInputInfo {
|
||||||
|
@ -1132,6 +1321,152 @@ class VulkanApp {
|
||||||
mGraphicsPipeline = std::move(graphicsPipelineValue);
|
mGraphicsPipeline = std::move(graphicsPipelineValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn createCrosshairPipeline() -> void {
|
||||||
|
// Create pipeline layout (no descriptor sets or push constants needed)
|
||||||
|
vk::PipelineLayoutCreateInfo pipelineLayoutInfo {};
|
||||||
|
mCrosshairPipelineLayout = mDevice->createPipelineLayoutUnique(pipelineLayoutInfo);
|
||||||
|
|
||||||
|
// Load shaders
|
||||||
|
auto vertShaderCode =
|
||||||
|
ShaderCompiler::getCompiledShader(CROSSHAIR_VERTEX_SHADER_PATH, shaderc_vertex_shader);
|
||||||
|
auto fragShaderCode =
|
||||||
|
ShaderCompiler::getCompiledShader(CROSSHAIR_FRAGMENT_SHADER_PATH, shaderc_fragment_shader);
|
||||||
|
|
||||||
|
vk::UniqueShaderModule vertShaderModule = createShaderModule(vertShaderCode);
|
||||||
|
vk::UniqueShaderModule fragShaderModule = createShaderModule(fragShaderCode);
|
||||||
|
|
||||||
|
vk::PipelineShaderStageCreateInfo vertShaderStageInfo { .stage = vk::ShaderStageFlagBits::eVertex,
|
||||||
|
.module = vertShaderModule.get(),
|
||||||
|
.pName = "main" };
|
||||||
|
|
||||||
|
vk::PipelineShaderStageCreateInfo fragShaderStageInfo { .stage = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.module = fragShaderModule.get(),
|
||||||
|
.pName = "main" };
|
||||||
|
|
||||||
|
std::array shaderStages = { vertShaderStageInfo, fragShaderStageInfo };
|
||||||
|
|
||||||
|
// Vertex input
|
||||||
|
auto bindingDescription = CrosshairVertex::getBindingDescription();
|
||||||
|
auto attributeDescriptions = CrosshairVertex::getAttributeDescriptions();
|
||||||
|
|
||||||
|
vk::PipelineVertexInputStateCreateInfo vertexInputInfo {
|
||||||
|
.vertexBindingDescriptionCount = 1,
|
||||||
|
.pVertexBindingDescriptions = &bindingDescription,
|
||||||
|
.vertexAttributeDescriptionCount = static_cast<u32>(attributeDescriptions.size()),
|
||||||
|
.pVertexAttributeDescriptions = attributeDescriptions.data()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Input assembly
|
||||||
|
vk::PipelineInputAssemblyStateCreateInfo inputAssembly { .topology = vk::PrimitiveTopology::eLineList,
|
||||||
|
.primitiveRestartEnable = false };
|
||||||
|
|
||||||
|
// Viewport and scissor
|
||||||
|
vk::PipelineViewportStateCreateInfo viewportState { .viewportCount = 1, .scissorCount = 1 };
|
||||||
|
|
||||||
|
// Rasterization
|
||||||
|
vk::PipelineRasterizationStateCreateInfo rasterizer { .depthClampEnable = false,
|
||||||
|
.rasterizerDiscardEnable = false,
|
||||||
|
.polygonMode = vk::PolygonMode::eFill,
|
||||||
|
.cullMode = vk::CullModeFlagBits::eNone,
|
||||||
|
.frontFace = vk::FrontFace::eCounterClockwise,
|
||||||
|
.depthBiasEnable = false,
|
||||||
|
.lineWidth = 1.0F };
|
||||||
|
|
||||||
|
// Multisampling
|
||||||
|
vk::PipelineMultisampleStateCreateInfo multisampling { .rasterizationSamples = mMsaaSamples,
|
||||||
|
.sampleShadingEnable = false };
|
||||||
|
|
||||||
|
// Color blending
|
||||||
|
vk::PipelineColorBlendAttachmentState colorBlendAttachment {
|
||||||
|
.blendEnable = false,
|
||||||
|
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||||
|
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::PipelineColorBlendStateCreateInfo colorBlending { .logicOpEnable = false,
|
||||||
|
.attachmentCount = 1,
|
||||||
|
.pAttachments = &colorBlendAttachment };
|
||||||
|
|
||||||
|
// Dynamic state
|
||||||
|
std::array dynamicStates = { vk::DynamicState::eViewport, vk::DynamicState::eScissor };
|
||||||
|
|
||||||
|
vk::PipelineDynamicStateCreateInfo dynamicState { .dynamicStateCount =
|
||||||
|
static_cast<u32>(dynamicStates.size()),
|
||||||
|
.pDynamicStates = dynamicStates.data() };
|
||||||
|
|
||||||
|
// Depth and stencil
|
||||||
|
vk::PipelineDepthStencilStateCreateInfo depthStencil { .depthTestEnable = false,
|
||||||
|
.depthWriteEnable = false,
|
||||||
|
.depthCompareOp = vk::CompareOp::eLess,
|
||||||
|
.depthBoundsTestEnable = false,
|
||||||
|
.stencilTestEnable = false };
|
||||||
|
|
||||||
|
// Create the pipeline
|
||||||
|
vk::GraphicsPipelineCreateInfo pipelineInfo { .stageCount = static_cast<u32>(shaderStages.size()),
|
||||||
|
.pStages = shaderStages.data(),
|
||||||
|
.pVertexInputState = &vertexInputInfo,
|
||||||
|
.pInputAssemblyState = &inputAssembly,
|
||||||
|
.pViewportState = &viewportState,
|
||||||
|
.pRasterizationState = &rasterizer,
|
||||||
|
.pMultisampleState = &multisampling,
|
||||||
|
.pDepthStencilState = &depthStencil,
|
||||||
|
.pColorBlendState = &colorBlending,
|
||||||
|
.pDynamicState = &dynamicState,
|
||||||
|
.layout = mCrosshairPipelineLayout.get(),
|
||||||
|
.renderPass = mRenderPass.get(),
|
||||||
|
.subpass = 0 };
|
||||||
|
|
||||||
|
mCrosshairPipeline = mDevice->createGraphicsPipelineUnique(nullptr, pipelineInfo).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn createCrosshairBuffers() -> void {
|
||||||
|
// Create vertex buffer
|
||||||
|
vk::DeviceSize bufferSize = sizeof(crosshairVertices[0]) * crosshairVertices.size();
|
||||||
|
|
||||||
|
auto stagingBuffer = createBuffer(
|
||||||
|
bufferSize,
|
||||||
|
vk::BufferUsageFlagBits::eTransferSrc,
|
||||||
|
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
|
||||||
|
);
|
||||||
|
|
||||||
|
void* data = mDevice->mapMemory(stagingBuffer.second.get(), 0, bufferSize);
|
||||||
|
memcpy(data, crosshairVertices.data(), bufferSize);
|
||||||
|
mDevice->unmapMemory(stagingBuffer.second.get());
|
||||||
|
|
||||||
|
auto [vertexBuffer, vertexBufferMemory] = createBuffer(
|
||||||
|
bufferSize,
|
||||||
|
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eVertexBuffer,
|
||||||
|
vk::MemoryPropertyFlagBits::eDeviceLocal
|
||||||
|
);
|
||||||
|
|
||||||
|
copyBuffer(stagingBuffer.first.get(), vertexBuffer.get(), bufferSize);
|
||||||
|
mCrosshairVertexBuffer = std::move(vertexBuffer);
|
||||||
|
mCrosshairVertexBufferMemory = std::move(vertexBufferMemory);
|
||||||
|
|
||||||
|
// Create index buffer
|
||||||
|
bufferSize = sizeof(crosshairIndices[0]) * crosshairIndices.size();
|
||||||
|
|
||||||
|
auto stagingBufferIndices = createBuffer(
|
||||||
|
bufferSize,
|
||||||
|
vk::BufferUsageFlagBits::eTransferSrc,
|
||||||
|
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
|
||||||
|
);
|
||||||
|
|
||||||
|
data = mDevice->mapMemory(stagingBufferIndices.second.get(), 0, bufferSize);
|
||||||
|
memcpy(data, crosshairIndices.data(), bufferSize);
|
||||||
|
mDevice->unmapMemory(stagingBufferIndices.second.get());
|
||||||
|
|
||||||
|
auto [indexBuffer, indexBufferMemory] = createBuffer(
|
||||||
|
bufferSize,
|
||||||
|
vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eIndexBuffer,
|
||||||
|
vk::MemoryPropertyFlagBits::eDeviceLocal
|
||||||
|
);
|
||||||
|
|
||||||
|
copyBuffer(stagingBufferIndices.first.get(), indexBuffer.get(), bufferSize);
|
||||||
|
mCrosshairIndexBuffer = std::move(indexBuffer);
|
||||||
|
mCrosshairIndexBufferMemory = std::move(indexBufferMemory);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates framebuffers for the swap chain images.
|
* @brief Creates framebuffers for the swap chain images.
|
||||||
*
|
*
|
||||||
|
@ -1710,10 +2045,15 @@ class VulkanApp {
|
||||||
attrib.vertices[static_cast<u32>((3 * index.vertex_index) + 2)],
|
attrib.vertices[static_cast<u32>((3 * index.vertex_index) + 2)],
|
||||||
},
|
},
|
||||||
.color = { 1.0F, 1.0F, 1.0F },
|
.color = { 1.0F, 1.0F, 1.0F },
|
||||||
.tex_coord = {
|
.texCoord = {
|
||||||
attrib.texcoords[static_cast<u32>((2 * index.texcoord_index) + 0)],
|
attrib.texcoords[static_cast<u32>((2 * index.texcoord_index) + 0)],
|
||||||
1.0F - attrib.texcoords[static_cast<u32>((2 * index.texcoord_index) + 1)],
|
1.0F - attrib.texcoords[static_cast<u32>((2 * index.texcoord_index) + 1)],
|
||||||
},
|
},
|
||||||
|
.normal = {
|
||||||
|
attrib.normals[static_cast<u32>((3 * index.normal_index) + 0)],
|
||||||
|
attrib.normals[static_cast<u32>((3 * index.normal_index) + 1)],
|
||||||
|
attrib.normals[static_cast<u32>((3 * index.normal_index) + 2)],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!uniqueVertices.contains(vertex)) {
|
if (!uniqueVertices.contains(vertex)) {
|
||||||
|
@ -1803,14 +2143,52 @@ class VulkanApp {
|
||||||
mUniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);
|
mUniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
mUniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT);
|
mUniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
|
||||||
for (usize i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
for (usize idx = 0; idx < MAX_FRAMES_IN_FLIGHT; idx++) {
|
||||||
std::tie(mUniformBuffers[i], mUniformBuffersMemory[i]) = createBuffer(
|
std::tie(mUniformBuffers[idx], mUniformBuffersMemory[idx]) = createBuffer(
|
||||||
bufferSize,
|
bufferSize,
|
||||||
vk::BufferUsageFlagBits::eUniformBuffer,
|
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||||
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
|
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
|
||||||
);
|
);
|
||||||
|
|
||||||
mUniformBuffersMapped[i] = mDevice->mapMemory(mUniformBuffersMemory[i].get(), 0, bufferSize);
|
mUniformBuffersMapped[idx] = mDevice->mapMemory(mUniformBuffersMemory[idx].get(), 0, bufferSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn createLightUniformBuffers() -> void {
|
||||||
|
vk::DeviceSize bufferSize = sizeof(LightInfo);
|
||||||
|
|
||||||
|
mLightUniformBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
mLightUniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
mLightUniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
|
||||||
|
for (usize idx = 0; idx < MAX_FRAMES_IN_FLIGHT; idx++) {
|
||||||
|
std::tie(mLightUniformBuffers[idx], mLightUniformBuffersMemory[idx]) = createBuffer(
|
||||||
|
bufferSize,
|
||||||
|
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||||
|
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
|
||||||
|
);
|
||||||
|
|
||||||
|
mLightUniformBuffersMapped[idx] =
|
||||||
|
mDevice->mapMemory(mLightUniformBuffersMemory[idx].get(), 0, bufferSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn createCameraUniformBuffers() -> void {
|
||||||
|
vk::DeviceSize bufferSize = sizeof(CameraInfo);
|
||||||
|
|
||||||
|
mCameraUniformBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
mCameraUniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
mCameraUniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT);
|
||||||
|
|
||||||
|
for (usize idx = 0; idx < MAX_FRAMES_IN_FLIGHT; idx++) {
|
||||||
|
std::tie(mCameraUniformBuffers[idx], mCameraUniformBuffersMemory[idx]) = createBuffer(
|
||||||
|
bufferSize,
|
||||||
|
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||||
|
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent
|
||||||
|
);
|
||||||
|
|
||||||
|
mCameraUniformBuffersMapped[idx] =
|
||||||
|
mDevice->mapMemory(mCameraUniformBuffersMemory[idx].get(), 0, bufferSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1821,7 +2199,15 @@ class VulkanApp {
|
||||||
* The pool is sized to accommodate the number of frames in flight.
|
* The pool is sized to accommodate the number of frames in flight.
|
||||||
*/
|
*/
|
||||||
fn createDescriptorPool() -> void {
|
fn createDescriptorPool() -> void {
|
||||||
std::array<vk::DescriptorPoolSize, 2> poolSizes = {
|
std::array<vk::DescriptorPoolSize, 4> poolSizes = {
|
||||||
|
vk::DescriptorPoolSize {
|
||||||
|
.type = vk::DescriptorType::eUniformBuffer,
|
||||||
|
.descriptorCount = MAX_FRAMES_IN_FLIGHT,
|
||||||
|
},
|
||||||
|
vk::DescriptorPoolSize {
|
||||||
|
.type = vk::DescriptorType::eUniformBuffer,
|
||||||
|
.descriptorCount = MAX_FRAMES_IN_FLIGHT,
|
||||||
|
},
|
||||||
vk::DescriptorPoolSize {
|
vk::DescriptorPoolSize {
|
||||||
.type = vk::DescriptorType::eUniformBuffer,
|
.type = vk::DescriptorType::eUniformBuffer,
|
||||||
.descriptorCount = MAX_FRAMES_IN_FLIGHT,
|
.descriptorCount = MAX_FRAMES_IN_FLIGHT,
|
||||||
|
@ -1858,33 +2244,61 @@ class VulkanApp {
|
||||||
|
|
||||||
mDescriptorSets = mDevice->allocateDescriptorSets(allocInfo);
|
mDescriptorSets = mDevice->allocateDescriptorSets(allocInfo);
|
||||||
|
|
||||||
for (usize i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
for (usize idx = 0; idx < MAX_FRAMES_IN_FLIGHT; idx++) {
|
||||||
vk::DescriptorBufferInfo bufferInfo {
|
vk::DescriptorBufferInfo uboBufferInfo {
|
||||||
.buffer = mUniformBuffers[i].get(),
|
.buffer = mUniformBuffers[idx].get(),
|
||||||
.offset = 0,
|
.offset = 0,
|
||||||
.range = sizeof(UniformBufferObject),
|
.range = sizeof(UniformBufferObject),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vk::DescriptorBufferInfo lightBufferInfo {
|
||||||
|
.buffer = mLightUniformBuffers[idx].get(),
|
||||||
|
.offset = 0,
|
||||||
|
.range = sizeof(LightInfo),
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::DescriptorBufferInfo cameraBufferInfo {
|
||||||
|
.buffer = mCameraUniformBuffers[idx].get(),
|
||||||
|
.offset = 0,
|
||||||
|
.range = sizeof(CameraInfo),
|
||||||
|
};
|
||||||
|
|
||||||
vk::DescriptorImageInfo imageInfo {
|
vk::DescriptorImageInfo imageInfo {
|
||||||
.sampler = mTextureSampler.get(),
|
.sampler = mTextureSampler.get(),
|
||||||
.imageView = mTextureImageView.get(),
|
.imageView = mTextureImageView.get(),
|
||||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<vk::WriteDescriptorSet, 2> descriptorWrites = {
|
std::array<vk::WriteDescriptorSet, 4> descriptorWrites = {
|
||||||
vk::WriteDescriptorSet {
|
vk::WriteDescriptorSet {
|
||||||
.dstSet = mDescriptorSets[i],
|
.dstSet = mDescriptorSets[idx],
|
||||||
.dstBinding = 0,
|
.dstBinding = 0,
|
||||||
.dstArrayElement = 0,
|
.dstArrayElement = 0,
|
||||||
.descriptorCount = 1,
|
.descriptorCount = 1,
|
||||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||||
.pBufferInfo = &bufferInfo,
|
.pBufferInfo = &uboBufferInfo,
|
||||||
},
|
},
|
||||||
vk::WriteDescriptorSet {
|
vk::WriteDescriptorSet {
|
||||||
.dstSet = mDescriptorSets[i],
|
.dstSet = mDescriptorSets[idx],
|
||||||
.dstBinding = 1,
|
.dstBinding = 1,
|
||||||
.dstArrayElement = 0,
|
.dstArrayElement = 0,
|
||||||
.descriptorCount = 1,
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||||
|
.pBufferInfo = &lightBufferInfo,
|
||||||
|
},
|
||||||
|
vk::WriteDescriptorSet {
|
||||||
|
.dstSet = mDescriptorSets[idx],
|
||||||
|
.dstBinding = 2,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||||
|
.pBufferInfo = &cameraBufferInfo,
|
||||||
|
},
|
||||||
|
vk::WriteDescriptorSet {
|
||||||
|
.dstSet = mDescriptorSets[idx],
|
||||||
|
.dstBinding = 3,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||||
.pImageInfo = &imageInfo,
|
.pImageInfo = &imageInfo,
|
||||||
},
|
},
|
||||||
|
@ -2170,6 +2584,23 @@ class VulkanApp {
|
||||||
// Copy the uniform buffer object to the mapped memory
|
// Copy the uniform buffer object to the mapped memory
|
||||||
memcpy(mUniformBuffersMapped[mCurrentFrame], &ubo, sizeof(ubo));
|
memcpy(mUniformBuffersMapped[mCurrentFrame], &ubo, sizeof(ubo));
|
||||||
|
|
||||||
|
LightInfo lightInfo {
|
||||||
|
.position = mLightSettings.position,
|
||||||
|
.color = mLightSettings.color,
|
||||||
|
.ambient_strength = mLightSettings.ambient_strength,
|
||||||
|
.specular_strength = mLightSettings.specular_strength,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy the light uniform buffer object to the mapped memory
|
||||||
|
memcpy(mLightUniformBuffersMapped[mCurrentFrame], &lightInfo, sizeof(lightInfo));
|
||||||
|
|
||||||
|
CameraInfo cameraInfo {
|
||||||
|
.position = glm::vec3(mCamera.getPosition()), // Use actual camera position
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy the camera uniform buffer object to the mapped memory
|
||||||
|
memcpy(mCameraUniformBuffersMapped[mCurrentFrame], &cameraInfo, sizeof(cameraInfo));
|
||||||
|
|
||||||
// Example: Add extra clones with different translations
|
// Example: Add extra clones with different translations
|
||||||
std::vector<glm::mat4> modelMatrices = { glm::translate(glm::mat4(1.0F), glm::vec3(2.0F, 0.0F, 0.0F)),
|
std::vector<glm::mat4> modelMatrices = { glm::translate(glm::mat4(1.0F), glm::vec3(2.0F, 0.0F, 0.0F)),
|
||||||
glm::translate(glm::mat4(1.0F), glm::vec3(-2.0F, 0.0F, 0.0F)),
|
glm::translate(glm::mat4(1.0F), glm::vec3(-2.0F, 0.0F, 0.0F)),
|
||||||
|
@ -2195,53 +2626,19 @@ class VulkanApp {
|
||||||
commandBuffer.drawIndexed(static_cast<u32>(mIndices.size()), 1, 0, 0, 0);
|
commandBuffer.drawIndexed(static_cast<u32>(mIndices.size()), 1, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only render ImGui when cursor is not captured
|
// Draw the crosshair
|
||||||
if (!mCursorCaptured) {
|
commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mCrosshairPipeline.get());
|
||||||
ImGui_ImplVulkan_NewFrame();
|
|
||||||
ImGui_ImplGlfw_NewFrame();
|
|
||||||
ImGui::NewFrame();
|
|
||||||
|
|
||||||
// Create ImGui window with useful controls
|
std::array<vk::Buffer, 1> vertexBuffers = { mCrosshairVertexBuffer.get() };
|
||||||
ImGui::Begin("Settings", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
|
std::array<vk::DeviceSize, 1> offsets = { 0 };
|
||||||
|
commandBuffer.bindVertexBuffers(0, 1, vertexBuffers.data(), offsets.data());
|
||||||
|
commandBuffer.bindIndexBuffer(mCrosshairIndexBuffer.get(), 0, vk::IndexType::eUint16);
|
||||||
|
|
||||||
// Set initial window size (this will be the minimum size due to AlwaysAutoResize)
|
// Draw the crosshair
|
||||||
ImGui::SetWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
|
commandBuffer.drawIndexed(static_cast<u32>(crosshairIndices.size()), 1, 0, 0, 0);
|
||||||
|
|
||||||
// Camera settings
|
// Render ImGui if we have a draw data (ImGui::Render was called)
|
||||||
if (ImGui::CollapsingHeader("Camera")) {
|
if (ImGui::GetDrawData()) {
|
||||||
ImGui::SliderFloat("Camera Speed", &mCameraSpeed, 0.1F, 5.0F);
|
|
||||||
|
|
||||||
glm::dvec3 pos = mCamera.getPosition();
|
|
||||||
ImGui::Text("Position: (%.2f, %.2f, %.2f)", pos.x, pos.y, pos.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rendering settings
|
|
||||||
if (ImGui::CollapsingHeader("Rendering")) {
|
|
||||||
if (ImGui::Checkbox("Wireframe Mode", &mWireframeMode)) {
|
|
||||||
// Wait for all operations to complete
|
|
||||||
mDevice->waitIdle();
|
|
||||||
|
|
||||||
// Store the old pipeline for deletion after the next frame
|
|
||||||
if (mGraphicsPipeline)
|
|
||||||
mOldPipeline = std::move(mGraphicsPipeline);
|
|
||||||
|
|
||||||
// Recreate the pipeline
|
|
||||||
createGraphicsPipeline();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SliderFloat("Field of View", &mFieldOfView, 45.0F, 120.0F);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Performance metrics
|
|
||||||
if (ImGui::CollapsingHeader("Performance")) {
|
|
||||||
const f32 framerate = ImGui::GetIO().Framerate;
|
|
||||||
ImGui::Text("%.1f FPS", static_cast<f64>(framerate));
|
|
||||||
ImGui::Text("%.3f ms/frame", static_cast<f64>(1000.0F / framerate));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
|
|
||||||
ImGui::Render();
|
|
||||||
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commandBuffer);
|
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commandBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
49
src/util/crosshair.hpp
Normal file
49
src/util/crosshair.hpp
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
struct CrosshairVertex {
|
||||||
|
vec2 pos;
|
||||||
|
vec3 color;
|
||||||
|
|
||||||
|
static fn getBindingDescription() -> vk::VertexInputBindingDescription {
|
||||||
|
return { .binding = 0, .stride = sizeof(CrosshairVertex), .inputRate = vk::VertexInputRate::eVertex };
|
||||||
|
}
|
||||||
|
|
||||||
|
static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 2> {
|
||||||
|
return {
|
||||||
|
vk::VertexInputAttributeDescription { .location = 0,
|
||||||
|
.binding = 0,
|
||||||
|
.format = vk::Format::eR32G32Sfloat,
|
||||||
|
.offset = offsetof(CrosshairVertex, pos) },
|
||||||
|
vk::VertexInputAttributeDescription { .location = 1,
|
||||||
|
.binding = 0,
|
||||||
|
.format = vk::Format::eR32G32B32Sfloat,
|
||||||
|
.offset = offsetof(CrosshairVertex, color) }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Crosshair vertices (in normalized device coordinates)
|
||||||
|
constexpr f32 CROSSHAIR_SIZE = 0.02F;
|
||||||
|
constexpr std::array<CrosshairVertex, 4> crosshairVertices = {
|
||||||
|
// Horizontal line
|
||||||
|
CrosshairVertex { { -CROSSHAIR_SIZE * (9.0F / 16.0F), 0.0F }, { 1.0F, 1.0F, 1.0F } }, // Left
|
||||||
|
CrosshairVertex { { CROSSHAIR_SIZE * (9.0F / 16.0F), 0.0F }, { 1.0F, 1.0F, 1.0F } }, // Right
|
||||||
|
// Vertical line
|
||||||
|
CrosshairVertex { { 0.0F, -CROSSHAIR_SIZE }, { 1.0F, 1.0F, 1.0F } }, // Bottom
|
||||||
|
CrosshairVertex { { 0.0F, CROSSHAIR_SIZE }, { 1.0F, 1.0F, 1.0F } } // Top
|
||||||
|
};
|
||||||
|
|
||||||
|
// Indices for drawing the crosshair lines
|
||||||
|
constexpr std::array<u16, 4> crosshairIndices = {
|
||||||
|
0,
|
||||||
|
1, // Horizontal line
|
||||||
|
2,
|
||||||
|
3 // Vertical line
|
||||||
|
};
|
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#define fn auto
|
#define fn auto
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
|
@ -37,7 +38,7 @@ namespace stb {
|
||||||
|
|
||||||
// Prevent copying to maintain single ownership
|
// Prevent copying to maintain single ownership
|
||||||
UniqueImage(const UniqueImage&) = delete;
|
UniqueImage(const UniqueImage&) = delete;
|
||||||
fn operator=(const UniqueImage&) -> UniqueImage& = delete;
|
fn operator=(const UniqueImage&)->UniqueImage& = delete;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move constructor for transferring image ownership.
|
* @brief Move constructor for transferring image ownership.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* @brief Defines the vertex structure and its associated utilities for 3D rendering.
|
* @brief Defines the vertex structure and its associated utilities for 3D rendering.
|
||||||
*
|
*
|
||||||
* This file contains the Vertex structure used for 3D model representation in the Vulkan
|
* This file contains the Vertex structure used for 3D model representation in the Vulkan
|
||||||
* graphics pipeline. It includes position, color, and texture coordinate data, along with
|
* graphics pipeline. It includes position, color, texture coordinate, and normal data, along with
|
||||||
* Vulkan-specific descriptors for vertex input handling.
|
* Vulkan-specific descriptors for vertex input handling.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -18,16 +18,17 @@
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represents a vertex in 3D space with color and texture information.
|
* @brief Represents a vertex in 3D space with color, texture, and normal information.
|
||||||
*
|
*
|
||||||
* This structure defines a vertex with all its attributes required for rendering,
|
* This structure defines a vertex with all its attributes required for rendering,
|
||||||
* including position in 3D space, RGB color, and texture coordinates. It also
|
* including position in 3D space, RGB color, texture coordinates, and normal vector. It also
|
||||||
* provides methods for Vulkan vertex input configuration.
|
* provides methods for Vulkan vertex input configuration.
|
||||||
*/
|
*/
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
vec3 pos; ///< Position of the vertex in 3D space (x, y, z)
|
glm::vec3 pos; ///< Position of the vertex in 3D space (x, y, z)
|
||||||
vec3 color; ///< RGB color values, each component in range [0, 1]
|
glm::vec3 color; ///< RGB color values, each component in range [0, 1]
|
||||||
vec2 tex_coord; ///< Texture coordinates (u, v) for texture mapping
|
glm::vec2 texCoord; ///< Texture coordinates (u, v) for texture mapping
|
||||||
|
glm::vec3 normal; ///< Normal vector of the vertex
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Provides the vertex binding description for Vulkan.
|
* @brief Provides the vertex binding description for Vulkan.
|
||||||
|
@ -40,25 +41,50 @@ struct Vertex {
|
||||||
* - Input rate (per-vertex data)
|
* - Input rate (per-vertex data)
|
||||||
*/
|
*/
|
||||||
static fn getBindingDescription() -> vk::VertexInputBindingDescription {
|
static fn getBindingDescription() -> vk::VertexInputBindingDescription {
|
||||||
return { .binding = 0, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex };
|
return vk::VertexInputBindingDescription {
|
||||||
|
.binding = 0,
|
||||||
|
.stride = sizeof(Vertex),
|
||||||
|
.inputRate = vk::VertexInputRate::eVertex,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Provides attribute descriptions for vertex data interpretation.
|
* @brief Provides attribute descriptions for vertex data interpretation.
|
||||||
*
|
*
|
||||||
* @return std::array<vk::VertexInputAttributeDescription, 3> Array of descriptions for position, color, and texture coordinates.
|
* @return std::array<vk::VertexInputAttributeDescription, 4> Array of descriptions for position, color, texture coordinates, and normal.
|
||||||
*
|
*
|
||||||
* The attribute descriptions specify:
|
* The attribute descriptions specify:
|
||||||
* - Location indices (0 for position, 1 for color, 2 for texture coordinates)
|
* - Location indices (0 for position, 1 for color, 2 for texture coordinates, 3 for normal)
|
||||||
* - Binding point (0)
|
* - Binding point (0)
|
||||||
* - Data format (R32G32B32 for vec3, R32G32 for vec2)
|
* - Data format (R32G32B32 for vec3, R32G32 for vec2)
|
||||||
* - Offset of each attribute in the vertex structure
|
* - Offset of each attribute in the vertex structure
|
||||||
*/
|
*/
|
||||||
static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 3> {
|
static fn getAttributeDescriptions() -> std::array<vk::VertexInputAttributeDescription, 4> {
|
||||||
return {
|
return {
|
||||||
vk::VertexInputAttributeDescription { 0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, pos) },
|
vk::VertexInputAttributeDescription {
|
||||||
vk::VertexInputAttributeDescription { 1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color) },
|
.location = 0,
|
||||||
vk::VertexInputAttributeDescription { 2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, tex_coord) }
|
.binding = 0,
|
||||||
|
.format = vk::Format::eR32G32B32Sfloat,
|
||||||
|
.offset = offsetof(Vertex, pos),
|
||||||
|
},
|
||||||
|
vk::VertexInputAttributeDescription {
|
||||||
|
.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),
|
||||||
|
},
|
||||||
|
vk::VertexInputAttributeDescription {
|
||||||
|
.location = 3,
|
||||||
|
.binding = 0,
|
||||||
|
.format = vk::Format::eR32G32B32Sfloat,
|
||||||
|
.offset = offsetof(Vertex, normal),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,10 +92,10 @@ struct Vertex {
|
||||||
* @brief Compares two vertices for equality.
|
* @brief Compares two vertices for equality.
|
||||||
*
|
*
|
||||||
* @param other The vertex to compare with.
|
* @param other The vertex to compare with.
|
||||||
* @return bool True if vertices are identical in position, color, and texture coordinates.
|
* @return bool True if vertices are identical in position, color, texture coordinates, and normal.
|
||||||
*/
|
*/
|
||||||
fn operator==(const Vertex& other) const -> bool {
|
fn operator==(const Vertex& other) const->bool {
|
||||||
return pos == other.pos && color == other.color && tex_coord == other.tex_coord;
|
return pos == other.pos && color == other.color && texCoord == other.texCoord && normal == other.normal;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -78,7 +104,7 @@ namespace std {
|
||||||
* @brief Hash function specialization for Vertex type.
|
* @brief Hash function specialization for Vertex type.
|
||||||
*
|
*
|
||||||
* This specialization allows Vertex objects to be used as keys in unordered containers.
|
* This specialization allows Vertex objects to be used as keys in unordered containers.
|
||||||
* The hash combines position, color, and texture coordinate data using bit operations
|
* The hash combines position, color, texture coordinate, and normal data using bit operations
|
||||||
* to create a unique hash value.
|
* to create a unique hash value.
|
||||||
*/
|
*/
|
||||||
template <>
|
template <>
|
||||||
|
@ -89,9 +115,10 @@ namespace std {
|
||||||
* @param vertex The vertex to hash.
|
* @param vertex The vertex to hash.
|
||||||
* @return size_t Hash value combining all vertex attributes.
|
* @return size_t Hash value combining all vertex attributes.
|
||||||
*/
|
*/
|
||||||
fn operator()(Vertex const& vertex) const -> size_t {
|
fn operator()(Vertex const& vertex) const->size_t {
|
||||||
return ((hash<vec3>()(vertex.pos) ^ (hash<vec3>()(vertex.color) << 1)) >> 1) ^
|
return ((hash<glm::vec3>()(vertex.pos) ^ (hash<glm::vec3>()(vertex.color) << 1)) >> 1) ^
|
||||||
(hash<vec2>()(vertex.tex_coord) << 1);
|
(hash<glm::vec2>()(vertex.texCoord) << 1) ^
|
||||||
|
(hash<glm::vec3>()(vertex.normal) << 2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue