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
|
||||
|
||||
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 = 1) in vec2 fragTexCoord;
|
||||
layout(location = 2) in vec3 fragNormal;
|
||||
layout(location = 3) in vec3 fragWorldPos;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
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 = 1) in vec3 inColor;
|
||||
layout(location = 2) in vec2 inTexCoord;
|
||||
layout(location = 3) in vec3 inNormal;
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
layout(location = 1) out vec2 fragTexCoord;
|
||||
layout(location = 2) out vec3 fragNormal;
|
||||
layout(location = 3) out vec3 fragWorldPos;
|
||||
|
||||
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;
|
||||
fragTexCoord = inTexCoord;
|
||||
}
|
||||
|
|
545
src/main.cpp
545
src/main.cpp
|
@ -23,6 +23,7 @@
|
|||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||
|
||||
// Include custom utility headers
|
||||
#include "util/crosshair.hpp" // Crosshair definitions
|
||||
#include "util/shaders.hpp" // Compiled shader code
|
||||
#include "util/types.hpp" // Custom type definitions
|
||||
#include "util/unique_image.hpp" // Custom image handling utilities
|
||||
|
@ -50,6 +51,8 @@ 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
|
||||
|
@ -127,6 +130,13 @@ class VulkanApp {
|
|||
vk::UniquePipeline mGraphicsPipeline; ///< Graphics pipeline
|
||||
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::UniqueImage mColorImage; ///< Color image
|
||||
|
@ -154,11 +164,27 @@ class VulkanApp {
|
|||
std::vector<vk::UniqueDeviceMemory> mUniformBuffersMemory; ///< Memory for 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 mImGuiDescriptorPool; ///< Separate descriptor pool for ImGui
|
||||
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>
|
||||
mImageAvailableSemaphores; ///< Signals that an image is available for rendering
|
||||
|
@ -222,6 +248,17 @@ class VulkanApp {
|
|||
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 {
|
||||
glm::dvec3 position;
|
||||
glm::dvec3 front;
|
||||
|
@ -231,19 +268,23 @@ class VulkanApp {
|
|||
f64 pitch;
|
||||
|
||||
Camera()
|
||||
: position(2.0, 2.0, 2.0),
|
||||
front(glm::normalize(glm::dvec3(-2.0, -2.0, -2.0))),
|
||||
: position(2.0, 2.0, 0.5),
|
||||
front(glm::normalize(glm::dvec3(0.0, 1.0, 0.0))),
|
||||
up(0.0, 0.0, 1.0),
|
||||
right(glm::normalize(glm::cross(front, up))),
|
||||
yaw(-135.0) // -135 degrees to match the initial front vector
|
||||
,
|
||||
pitch(-35.26) // -35.26 degrees to match the initial front vector
|
||||
{
|
||||
right(glm::normalize(glm::cross(front, glm::dvec3(0.0, 0.0, 1.0)))),
|
||||
yaw(180.0),
|
||||
pitch(0.0) {
|
||||
updateCameraVectors();
|
||||
}
|
||||
|
||||
[[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 {
|
||||
return glm::lookAt(position, position + front, up);
|
||||
}
|
||||
|
@ -289,6 +330,12 @@ class VulkanApp {
|
|||
yaw += xoffset * 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
|
||||
if (pitch > 89.0)
|
||||
pitch = 89.0;
|
||||
|
@ -348,9 +395,20 @@ class VulkanApp {
|
|||
// Set window creation hints
|
||||
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
|
||||
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
|
||||
mWindow->setUserPointer(this);
|
||||
|
||||
|
@ -359,22 +417,22 @@ class VulkanApp {
|
|||
|
||||
// Set up mouse callback
|
||||
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)
|
||||
return; // Skip camera movement when cursor is not captured
|
||||
|
||||
if (mFirstMouse) {
|
||||
mLastX = xpos;
|
||||
mLastY = ypos;
|
||||
mLastX = mouseX;
|
||||
mLastY = mouseY;
|
||||
mFirstMouse = false;
|
||||
return;
|
||||
}
|
||||
|
||||
f64 xoffset = xpos - mLastX;
|
||||
f64 yoffset = mLastY - ypos; // Reversed since y-coordinates range from bottom to top
|
||||
f64 xoffset = mouseX - mLastX;
|
||||
f64 yoffset = mLastY - mouseY; // Reversed since y-coordinates range from bottom to top
|
||||
|
||||
mLastX = xpos;
|
||||
mLastY = ypos;
|
||||
mLastX = mouseX;
|
||||
mLastY = mouseY;
|
||||
|
||||
mCamera.rotate(-xoffset, yoffset); // Invert xoffset for correct horizontal movement
|
||||
};
|
||||
|
@ -391,6 +449,7 @@ class VulkanApp {
|
|||
mCursorCaptured = false;
|
||||
window.set<vkfw::InputMode::eCursor>(vkfw::CursorMode::eNormal);
|
||||
}
|
||||
|
||||
if (key == vkfw::Key::eR && action == vkfw::KeyAction::ePress) {
|
||||
try {
|
||||
mDevice->waitIdle();
|
||||
|
@ -454,6 +513,7 @@ class VulkanApp {
|
|||
createRenderPass(); // Set up the render pass
|
||||
createDescriptorSetLayout(); // Create the descriptor set layout
|
||||
createGraphicsPipeline(); // Create the graphics pipeline
|
||||
createCrosshairPipeline(); // Create the crosshair pipeline
|
||||
createCommandPool(); // Create a command pool for allocating command buffers
|
||||
createColorResources(); // Create resources for multisampling
|
||||
createDepthResources(); // Create resources for depth testing
|
||||
|
@ -465,9 +525,12 @@ class VulkanApp {
|
|||
createVertexBuffer(); // Create a buffer for vertex data
|
||||
createIndexBuffer(); // Create a buffer for index data
|
||||
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
|
||||
createDescriptorSets(); // Allocate and update descriptor sets
|
||||
createCommandBuffers(); // Create command buffers for rendering commands
|
||||
createCrosshairBuffers(); // Create crosshair buffers
|
||||
createSyncObjects(); // Create synchronization objects (semaphores and fences)
|
||||
initImGui(); // Initialize Dear ImGui for GUI rendering
|
||||
}
|
||||
|
@ -564,6 +627,107 @@ class VulkanApp {
|
|||
++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();
|
||||
|
||||
drawFrame();
|
||||
}
|
||||
|
||||
|
@ -603,13 +767,17 @@ class VulkanApp {
|
|||
createImageViews();
|
||||
createRenderPass();
|
||||
createGraphicsPipeline();
|
||||
createCrosshairPipeline();
|
||||
createColorResources();
|
||||
createDepthResources();
|
||||
createFramebuffers();
|
||||
createUniformBuffers();
|
||||
createLightUniformBuffers();
|
||||
createCameraUniformBuffers();
|
||||
createDescriptorPool();
|
||||
createDescriptorSets();
|
||||
createCommandBuffers();
|
||||
createCrosshairBuffers();
|
||||
|
||||
mImagesInFlight.resize(mSwapChainImages.size(), nullptr);
|
||||
}
|
||||
|
@ -981,15 +1149,36 @@ class VulkanApp {
|
|||
.pImmutableSamplers = nullptr,
|
||||
};
|
||||
|
||||
vk::DescriptorSetLayoutBinding samplerLayoutBinding {
|
||||
vk::DescriptorSetLayoutBinding lightLayoutBinding {
|
||||
.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,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
||||
.pImmutableSamplers = nullptr,
|
||||
};
|
||||
|
||||
std::array<vk::DescriptorSetLayoutBinding, 2> bindings = { uboLayoutBinding, samplerLayoutBinding };
|
||||
std::array<vk::DescriptorSetLayoutBinding, 4> bindings = {
|
||||
uboLayoutBinding,
|
||||
lightLayoutBinding,
|
||||
cameraLayoutBinding,
|
||||
samplerLayoutBinding,
|
||||
};
|
||||
|
||||
vk::DescriptorSetLayoutCreateInfo layoutInfo {
|
||||
.bindingCount = static_cast<u32>(bindings.size()),
|
||||
|
@ -1033,7 +1222,7 @@ class VulkanApp {
|
|||
};
|
||||
|
||||
vk::VertexInputBindingDescription bindingDescription = Vertex::getBindingDescription();
|
||||
std::array<vk::VertexInputAttributeDescription, 3> attributeDescriptions =
|
||||
std::array<vk::VertexInputAttributeDescription, 4> attributeDescriptions =
|
||||
Vertex::getAttributeDescriptions();
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo vertexInputInfo {
|
||||
|
@ -1132,6 +1321,152 @@ class VulkanApp {
|
|||
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.
|
||||
*
|
||||
|
@ -1710,10 +2045,15 @@ class VulkanApp {
|
|||
attrib.vertices[static_cast<u32>((3 * index.vertex_index) + 2)],
|
||||
},
|
||||
.color = { 1.0F, 1.0F, 1.0F },
|
||||
.tex_coord = {
|
||||
.texCoord = {
|
||||
attrib.texcoords[static_cast<u32>((2 * index.texcoord_index) + 0)],
|
||||
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)) {
|
||||
|
@ -1803,14 +2143,52 @@ class VulkanApp {
|
|||
mUniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
mUniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT);
|
||||
|
||||
for (usize i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
std::tie(mUniformBuffers[i], mUniformBuffersMemory[i]) = createBuffer(
|
||||
for (usize idx = 0; idx < MAX_FRAMES_IN_FLIGHT; idx++) {
|
||||
std::tie(mUniformBuffers[idx], mUniformBuffersMemory[idx]) = createBuffer(
|
||||
bufferSize,
|
||||
vk::BufferUsageFlagBits::eUniformBuffer,
|
||||
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.
|
||||
*/
|
||||
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 {
|
||||
.type = vk::DescriptorType::eUniformBuffer,
|
||||
.descriptorCount = MAX_FRAMES_IN_FLIGHT,
|
||||
|
@ -1858,33 +2244,61 @@ class VulkanApp {
|
|||
|
||||
mDescriptorSets = mDevice->allocateDescriptorSets(allocInfo);
|
||||
|
||||
for (usize i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
vk::DescriptorBufferInfo bufferInfo {
|
||||
.buffer = mUniformBuffers[i].get(),
|
||||
for (usize idx = 0; idx < MAX_FRAMES_IN_FLIGHT; idx++) {
|
||||
vk::DescriptorBufferInfo uboBufferInfo {
|
||||
.buffer = mUniformBuffers[idx].get(),
|
||||
.offset = 0,
|
||||
.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 {
|
||||
.sampler = mTextureSampler.get(),
|
||||
.imageView = mTextureImageView.get(),
|
||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
};
|
||||
|
||||
std::array<vk::WriteDescriptorSet, 2> descriptorWrites = {
|
||||
std::array<vk::WriteDescriptorSet, 4> descriptorWrites = {
|
||||
vk::WriteDescriptorSet {
|
||||
.dstSet = mDescriptorSets[i],
|
||||
.dstSet = mDescriptorSets[idx],
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||
.pBufferInfo = &bufferInfo,
|
||||
.pBufferInfo = &uboBufferInfo,
|
||||
},
|
||||
vk::WriteDescriptorSet {
|
||||
.dstSet = mDescriptorSets[i],
|
||||
.dstSet = mDescriptorSets[idx],
|
||||
.dstBinding = 1,
|
||||
.dstArrayElement = 0,
|
||||
.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,
|
||||
.pImageInfo = &imageInfo,
|
||||
},
|
||||
|
@ -2170,6 +2584,23 @@ class VulkanApp {
|
|||
// Copy the uniform buffer object to the mapped memory
|
||||
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
|
||||
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)),
|
||||
|
@ -2195,53 +2626,19 @@ class VulkanApp {
|
|||
commandBuffer.drawIndexed(static_cast<u32>(mIndices.size()), 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Only render ImGui when cursor is not captured
|
||||
if (!mCursorCaptured) {
|
||||
ImGui_ImplVulkan_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
// Draw the crosshair
|
||||
commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, mCrosshairPipeline.get());
|
||||
|
||||
// Create ImGui window with useful controls
|
||||
ImGui::Begin("Settings", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
|
||||
std::array<vk::Buffer, 1> vertexBuffers = { mCrosshairVertexBuffer.get() };
|
||||
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)
|
||||
ImGui::SetWindowSize(ImVec2(400, 300), ImGuiCond_FirstUseEver);
|
||||
// Draw the crosshair
|
||||
commandBuffer.drawIndexed(static_cast<u32>(crosshairIndices.size()), 1, 0, 0, 0);
|
||||
|
||||
// Camera settings
|
||||
if (ImGui::CollapsingHeader("Camera")) {
|
||||
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();
|
||||
// Render ImGui if we have a draw data (ImGui::Render was called)
|
||||
if (ImGui::GetDrawData()) {
|
||||
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 <cstdint>
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
#include <string>
|
||||
|
||||
#define fn auto
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* @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
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
@ -18,16 +18,17 @@
|
|||
#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,
|
||||
* 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.
|
||||
*/
|
||||
struct Vertex {
|
||||
vec3 pos; ///< Position of the vertex in 3D space (x, y, z)
|
||||
vec3 color; ///< RGB color values, each component in range [0, 1]
|
||||
vec2 tex_coord; ///< Texture coordinates (u, v) for texture mapping
|
||||
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::vec3 normal; ///< Normal vector of the vertex
|
||||
|
||||
/**
|
||||
* @brief Provides the vertex binding description for Vulkan.
|
||||
|
@ -40,25 +41,50 @@ struct Vertex {
|
|||
* - Input rate (per-vertex data)
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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:
|
||||
* - 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)
|
||||
* - Data format (R32G32B32 for vec3, R32G32 for vec2)
|
||||
* - 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 {
|
||||
vk::VertexInputAttributeDescription { 0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, pos) },
|
||||
vk::VertexInputAttributeDescription { 1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, color) },
|
||||
vk::VertexInputAttributeDescription { 2, 0, vk::Format::eR32G32Sfloat, offsetof(Vertex, tex_coord) }
|
||||
vk::VertexInputAttributeDescription {
|
||||
.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),
|
||||
},
|
||||
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.
|
||||
*
|
||||
* @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 {
|
||||
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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
template <>
|
||||
|
@ -90,8 +116,9 @@ namespace std {
|
|||
* @return size_t Hash value combining all vertex attributes.
|
||||
*/
|
||||
fn operator()(Vertex const& vertex) const->size_t {
|
||||
return ((hash<vec3>()(vertex.pos) ^ (hash<vec3>()(vertex.color) << 1)) >> 1) ^
|
||||
(hash<vec2>()(vertex.tex_coord) << 1);
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue