aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am16
-rw-r--r--src/Makefile.am1
-rw-r--r--src/ctrl/keyboard.hpp2
-rw-r--r--src/ctrl/mouse.hpp2
-rw-r--r--src/engine.cpp249
-rw-r--r--src/fb/chfb.cpp7
-rw-r--r--src/fb/chfb.hpp5
-rw-r--r--src/fb/pixfb.cpp9
-rw-r--r--src/fb/pixfb.hpp5
-rw-r--r--src/math/mat4.hpp1
-rw-r--r--src/math/quat.hpp26
-rw-r--r--src/math/utils.hpp24
-rw-r--r--src/math/vector.hpp26
-rw-r--r--src/o3d/camera.hpp10
-rw-r--r--src/o3d/deriv_vertex.hpp4
-rw-r--r--src/o3d/mesh.cpp39
-rw-r--r--src/o3d/mesh.hpp3
-rw-r--r--src/o3d/vertex.hpp9
-rw-r--r--src/o3d/vertex_data.hpp5
-rw-r--r--src/obj_parser.cpp149
-rw-r--r--src/renderer.cpp13
-rw-r--r--src/renderer.hpp7
-rw-r--r--src/shaders/shader.slang34
-rw-r--r--src/vulkan_utils.hpp14
24 files changed, 412 insertions, 248 deletions
diff --git a/Makefile.am b/Makefile.am
index 0e3ddf4..3297551 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,9 +3,21 @@ SUBDIRS = src
ACLOCAL_AMFLAGS = -Im4 --install
assetsdir = $(datarootdir)/assets
-assets_DATA = ../assets/suzanne.obj
+assets_DATA = ../assets/suzanne.obj ../assets/viking_room.obj
EXTRA_DIST += $(assets_DATA)
texturesdir = $(assetsdir)/textures
-textures_DATA = ../assets/textures/texture.jpg
+textures_DATA = ../assets/textures/texture.jpg ../assets/textures/viking_room.png
EXTRA_DIST += $(textures_DATA)
+
+# assets origin:
+# - assets/suzanne.obj
+# taken from Blender
+# - assets/texture.jpg
+# comes from here https://pixabay.com/photos/statue-sculpture-figure-1275469/, modified by the
+# author(s) of the vulkan tutorials (see
+# https://docs.vulkan.org/tutorial/latest/06_Texture_mapping/00_Images.html#_loading_an_image)
+# - assets/viking_room.obj & assets/textures/viking_room.png
+# comes from here https://sketchfab.com/3d-models/viking-room-a49f1b8e4f5c4ecf9e1fe7d81915ad38,
+# modified by the author(s) of the vulkan tutorials (see
+# https://docs.vulkan.org/tutorial/latest/08_Loading_models.html#_sample_mesh)
diff --git a/src/Makefile.am b/src/Makefile.am
index 805db1f..e8f98a1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,6 +11,7 @@ engine_SOURCES = \
vulkan_utils.hpp \
stb_image.c \
fb/chfb.hpp fb/chfb.cpp fb/pixfb.hpp fb/pixfb.cpp \
+ math/utils.hpp \
math/vector.hpp \
math/mat4.hpp \
math/quat.hpp \
diff --git a/src/ctrl/keyboard.hpp b/src/ctrl/keyboard.hpp
index f7bdf3d..dc8e259 100644
--- a/src/ctrl/keyboard.hpp
+++ b/src/ctrl/keyboard.hpp
@@ -4,8 +4,6 @@
#include <cstdint>
#include "math/vector.hpp"
-using engine::math::Vector2;
-
namespace engine::controllers {
enum class KeyboardKey {
diff --git a/src/ctrl/mouse.hpp b/src/ctrl/mouse.hpp
index 5c7af80..3b45b4d 100644
--- a/src/ctrl/mouse.hpp
+++ b/src/ctrl/mouse.hpp
@@ -10,7 +10,7 @@ class Mouse {
public:
constexpr Mouse(MouseMotionCallback mouse_motion_cb) : mouse_motion_cb{mouse_motion_cb} {}
- void mouse_motion_event(Vector2 rel) const & {
+ void mouse_motion_event(engine::math::Vector2 rel) const & {
mouse_motion_cb(rel);
}
diff --git a/src/engine.cpp b/src/engine.cpp
index bde3058..aed0970 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -55,6 +55,7 @@ using
engine::fb::CharacterFrameBuffer,
engine::fb::PixelFrameBuffer,
engine::o3d::Scene,
+ engine::o3d::Object3D,
engine::o3d::Mesh,
engine::o3d::Triangle,
engine::o3d::Camera,
@@ -82,9 +83,10 @@ enum class GameType {
plane,
suzanne,
plane_and_suzanne,
+ test,
};
-constexpr GameType game_type { GameType::plane_and_suzanne };
+constexpr GameType game_type { GameType::test };
static void print_usage(std::ostream& output_stream) {
output_stream << "Usage: ./engine [-htg] [--help] [--term] [--graphical]\n"
@@ -139,7 +141,7 @@ static void scene_main(const Matrix4& final_transform_mat, Scene& scene,
if (kb.is_down(KeyboardKey::fw) || kb.is_down(KeyboardKey::key_left)
|| kb.is_down(KeyboardKey::bw) || kb.is_down(KeyboardKey::key_right))
movement.normalize();
- scene.camera.transform.loc += movement.rot(Quaternion::rot_y(ry)) * movement_speed * ellapsed_time;
+ scene.camera.transform.loc += Quaternion::rot_y(ry).rot(movement) * movement_speed * ellapsed_time;
scene.camera.transform.rot = Quaternion::euler_zxy(rx, ry, 0.f);
scene.camera.fov = (kb.is_down(KeyboardKey::zoom) ? 40.f : 80.f) * PI / 180.f;
@@ -152,7 +154,7 @@ static void scene_main(const Matrix4& final_transform_mat, Scene& scene,
auto proj_mat = final_transform_mat
* Matrix4::perspective(scene.camera.fov, static_cast<float>(surface_width) / static_cast<float>(surface_height), .5f, 12.f);
- render_and_present_frame(scene.camera.transform.to_inverse_mat4(), proj_mat, time, ellapsed_time);
+ render_and_present_frame(scene.camera.transform.to_inverse_mat4(), proj_mat, scene.camera.transform.to_mat4(), time, ellapsed_time);
}
}
@@ -162,7 +164,7 @@ static void render_software(Renderer<FrameBuffer>& renderer, const Matrix4& fina
PollEventsFn poll_events, UpdateRendererSizeFn update_renderer_size, PresentFrameFn present_frame) {
scene_main(final_transform_mat, scene,
// update_surface_size
- [&]() {
+ [&] {
update_renderer_size();
return std::tuple { renderer.width(), renderer.height() };
},
@@ -170,25 +172,33 @@ static void render_software(Renderer<FrameBuffer>& renderer, const Matrix4& fina
poll_events,
// render_and_present_frame
- [&](const Matrix4& view_mat, const Matrix4& proj_mat, float /* time */, float /* ellapsed_time */) {
- auto proj_view_mat = proj_mat * view_mat;
+ [&](const Matrix4& /* view_mat */, const Matrix4& /* proj_mat */, const Matrix4& /* inv_view_mat */, float /* time */, float /* ellapsed_time */) {
+ // TODO: remove
renderer.clear();
- for (const auto& obj : scene.objs) {
- auto model_mat = obj.transform.to_mat4();
- auto final_mat = proj_view_mat * model_mat;
- const auto& mesh = obj.mesh;
- std::vector<Vector4> vertices;
- std::vector<VertexData> vertices_data;
- for (const auto& vertex : mesh.vertices) {
- vertices.push_back(final_mat * Vector4 { vertex, 1.f });
- vertices_data.push_back(VertexData((model_mat * vertex).xyz()));
- }
- for (const auto& triangle_indices : mesh.indices) {
- [&]<std::size_t... j>(std::integer_sequence<std::size_t, j...>) {
- renderer.draw_triangle({{vertices[triangle_indices[j][0]], mesh.normals[triangle_indices[j][1]], vertices_data[triangle_indices[j][0]]}...});
- }(std::make_integer_sequence<std::size_t, 3>());
- }
- }
+ // auto proj_view_mat = proj_mat * view_mat;
+ // renderer.clear();
+ // for (const auto& obj : scene.objs) {
+ // auto model_mat = obj.transform.to_mat4();
+ // auto final_mat = proj_view_mat * model_mat;
+ // const auto& mesh = obj.mesh;
+ // std::vector<Vector4> vertices;
+ // std::vector<Vector3> normals;
+ // std::vector<VertexData> vertices_data;
+ // for (const auto& vertex : mesh.vertices) {
+ // Vector4 vertex4 { vertex, 1.f };
+ // vertices.push_back(final_mat * vertex4);
+ // vertices_data.push_back(VertexData((model_mat * vertex4).xyz()));
+ // }
+ // for (const auto& normal : mesh.normals)
+ // normals.push_back((model_mat * Vector4 { normal, 0.f }).xyz());
+ // for (const auto& triangle_indices : mesh.indices) {
+ // [&]<std::size_t... j>(std::index_sequence<j...>) {
+ // renderer.draw_triangle(
+ // {{vertices[triangle_indices[j][0]], normals[triangle_indices[j][1]], vertices_data[triangle_indices[j][0]]}...}
+ // );
+ // }(std::make_index_sequence<3>());
+ // }
+ // }
present_frame();
}
);
@@ -208,10 +218,10 @@ static int main_term(Scene& scene) {
set_escdelay(0);
curs_set(0);
- auto renderer = [&]() {
+ auto renderer = [&] {
int w, h;
getmaxyx(stdscr, h, w);
- return Renderer<CharacterFrameBuffer> { CharacterFrameBuffer { static_cast<unsigned int>(w), static_cast<unsigned int>(h) } };
+ return Renderer { CharacterFrameBuffer { static_cast<unsigned int>(w), static_cast<unsigned int>(h) } };
}();
render_software(renderer, Matrix4::scale(Vector3(2.f, 1.f, 1.f)), scene,
@@ -289,14 +299,14 @@ static int main_term(Scene& scene) {
},
// update_renderer_size
- [&]() {
+ [&] {
int w, h;
getmaxyx(stdscr, h, w);
renderer.resize(static_cast<unsigned int>(w), static_cast<unsigned int>(h));
},
// present_frame
- [&]() {
+ [&] {
mvaddnstr(0, 0, renderer.fb.chars(), renderer.width() * renderer.height());
}
);
@@ -331,6 +341,7 @@ constexpr bool enable_validation_layers = true;
constexpr size_t max_frames_in_flight = 2;
constexpr bool show_fps = false;
+constexpr bool transparent_window = false;
struct PhysicalDeviceEntry {
uint32_t idx;
@@ -347,7 +358,7 @@ enum class GraphicalRendererMode {
};
static bool check_validation_layer_support() {
- auto avail_layers = [&]() {
+ auto avail_layers = [&] {
uint32_t avail_layers_count;
if (VkResult res = vkEnumerateInstanceLayerProperties(&avail_layers_count, nullptr); res != VK_SUCCESS) {
std::cerr << "failed to enumerate instance layer properties, error code: " << string_VkResult(res) << std::endl;
@@ -477,7 +488,7 @@ static uint32_t find_mem_type(VkPhysicalDevice physical_device, uint32_t type_fi
static std::tuple<VkBuffer, VkDeviceMemory>
create_buf(VkPhysicalDevice physical_device, VkDevice device, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags mem_flags) {
- auto buf = [&]() {
+ auto buf = [&] {
VkBufferCreateInfo buf_ci {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
@@ -499,7 +510,7 @@ create_buf(VkPhysicalDevice physical_device, VkDevice device, VkDeviceSize size,
VkMemoryRequirements buf_mem_requirements;
vkGetBufferMemoryRequirements(device, buf, &buf_mem_requirements);
- auto buf_device_mem = [&]() {
+ auto buf_device_mem = [&] {
VkMemoryAllocateInfo buf_mem_ai {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = nullptr,
@@ -522,7 +533,7 @@ create_buf(VkPhysicalDevice physical_device, VkDevice device, VkDeviceSize size,
}
static VkCommandBuffer begin_single_time_cmds(VkDevice device, VkCommandPool cmd_pool) {
- auto cmd_buf = [&]() {
+ auto cmd_buf = [&] {
VkCommandBufferAllocateInfo cmd_buf_ai {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.pNext = nullptr,
@@ -612,7 +623,7 @@ static void copy_buf_to_img(VkCommandBuffer cmd_buf, VkBuffer src, VkImage dst,
static std::tuple<VkImage, VkDeviceMemory>
create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, uint32_t height, VkFormat format,
VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags mem_flags) {
- auto texture_img = [&]() {
+ auto texture_img = [&] {
VkImageCreateInfo img_ci {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.pNext = nullptr,
@@ -639,7 +650,7 @@ create_img(VkPhysicalDevice physical_device, VkDevice device, uint32_t width, ui
return texture_img;
}();
- auto texture_img_device_mem = [&]() {
+ auto texture_img_device_mem = [&] {
VkMemoryRequirements mem_requirements;
vkGetImageMemoryRequirements(device, texture_img, &mem_requirements);
VkMemoryAllocateInfo mem_ai {
@@ -668,7 +679,7 @@ static std::tuple<VkImage, VkDeviceMemory, VkImageView>
create_depth_resources(VkPhysicalDevice physical_device, VkDevice device, VkExtent2D swapchain_extent, VkFormat depth_format) {
auto [depth_img, depth_img_device_mem] = create_img(physical_device, device, swapchain_extent.width, swapchain_extent.height, depth_format,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
- auto depth_img_view = [&]() {
+ auto depth_img_view = [&] {
VkImageViewCreateInfo img_view_ci {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = {},
@@ -743,6 +754,10 @@ static int main_graphical(Scene& scene) {
// init window
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
+ if (transparent_window) {
+ glfwWindowHint(GLFW_DECORATED, GLFW_FALSE);
+ glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE);
+ }
bool fb_resized = false;
@@ -777,7 +792,7 @@ static int main_graphical(Scene& scene) {
exit(EXIT_FAILURE);
}
- auto instance = [&]() {
+ auto instance = [&] {
VkApplicationInfo app_info {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = nullptr,
@@ -788,7 +803,7 @@ static int main_graphical(Scene& scene) {
.apiVersion = VK_API_VERSION_1_4,
};
- auto instance_exts = [&]() {
+ auto instance_exts = [&] {
std::vector<const char*> instance_exts;
{
uint32_t glfw_extension_count;
@@ -800,7 +815,7 @@ static int main_graphical(Scene& scene) {
return instance_exts;
}();
- auto avail_instance_exts = [&]() {
+ auto avail_instance_exts = [&] {
uint32_t avail_instance_exts_count;
if (VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &avail_instance_exts_count, nullptr); res != VK_SUCCESS) {
std::cerr << "failed to enumerate instance extension properties, error code: " << string_VkResult(res) << std::endl;
@@ -863,7 +878,7 @@ static int main_graphical(Scene& scene) {
}
// create window surface
- auto surface = [&]() {
+ auto surface = [&] {
VkSurfaceKHR surface;
if (VkResult res = glfwCreateWindowSurface(instance, window, nullptr, &surface); res != VK_SUCCESS) {
std::cerr << "failed to create window surface, error code: " << string_VkResult(res) << std::endl;
@@ -873,10 +888,10 @@ static int main_graphical(Scene& scene) {
}();
// select physical device and queues
- auto [physical_device, graphics_queue_family_index, present_queue_family_index, physical_device_props, physical_device_features, depth_format] = [&]() {
+ auto [physical_device, graphics_queue_family_index, present_queue_family_index, physical_device_props, physical_device_features, depth_format] = [&] {
std::multimap<unsigned, PhysicalDeviceEntry> physical_devices;
- auto avail_physical_devices = [&]() {
+ auto avail_physical_devices = [&] {
uint32_t avail_physical_devices_count;
if (VkResult res = vkEnumeratePhysicalDevices(instance, &avail_physical_devices_count, nullptr); res != VK_SUCCESS) {
std::cerr << "failed to enumerate physical devices, error code: " << string_VkResult(res) << std::endl;
@@ -900,7 +915,7 @@ static int main_graphical(Scene& scene) {
for (uint32_t i = 0; i < avail_physical_devices.size(); i++) {
const auto& avail_physical_device = avail_physical_devices[i];
- auto physical_device_props = [&]() {
+ auto physical_device_props = [&] {
VkPhysicalDeviceProperties2 physical_device_props{};
physical_device_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
vkGetPhysicalDeviceProperties2(avail_physical_device, &physical_device_props);
@@ -910,14 +925,14 @@ static int main_graphical(Scene& scene) {
std::cout << " " << (i + 1) << ". " << physical_device_props.properties.deviceName << ":" << std::endl;
std::cout << " apiVersion: " << engine::vk::api { physical_device_props.properties.apiVersion } << std::endl;
- auto physical_device_features = [&]() {
+ auto physical_device_features = [&] {
VkPhysicalDeviceFeatures2 physical_device_features{};
physical_device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
vkGetPhysicalDeviceFeatures2(avail_physical_device, &physical_device_features);
return physical_device_features;
}();
- auto physical_device_ext_props = [&]() {
+ auto physical_device_ext_props = [&] {
uint32_t physical_device_ext_props_count;
if (VkResult res = vkEnumerateDeviceExtensionProperties(avail_physical_device, nullptr,
&physical_device_ext_props_count, nullptr); res != VK_SUCCESS) {
@@ -941,7 +956,7 @@ static int main_graphical(Scene& scene) {
std::cout << " " << ext << std::endl;
}
- auto queue_family_properties = [&]() {
+ auto queue_family_properties = [&] {
uint32_t queue_family_properties_count;
vkGetPhysicalDeviceQueueFamilyProperties2(avail_physical_device, &queue_family_properties_count, nullptr);
std::vector<VkQueueFamilyProperties2> queue_family_properties(queue_family_properties_count);
@@ -975,7 +990,7 @@ static int main_graphical(Scene& scene) {
std::cout << "none";
std::cout << std::endl;
- auto score = [&]() {
+ auto score = [&] {
if (VK_API_VERSION_VARIANT(physical_device_props.properties.apiVersion) != 0
|| physical_device_props.properties.apiVersion < VK_API_VERSION_1_4)
return std::optional<unsigned> {};
@@ -1051,7 +1066,7 @@ static int main_graphical(Scene& scene) {
best->second.present_queue_family_index, best->second.props, best->second.features, best->second.depth_format };
}();
- auto device = [&]() {
+ auto device = [&] {
// TODO: really weird way of making a single structure if
// graphics_queue_family_index == present_queue_family_index
// here, in this cas, we create both *CreateInfo, and then tell VkDeviceCreateInfo that
@@ -1135,7 +1150,7 @@ static int main_graphical(Scene& scene) {
return device;
}();
- auto [graphics_queue, present_queue] = [&]() {
+ auto [graphics_queue, present_queue] = [&] {
const auto map_family_to_queue = [&](uint32_t queue_family_index) {
VkQueue queue;
vkGetDeviceQueue(device, queue_family_index, 0, &queue);
@@ -1166,13 +1181,13 @@ static int main_graphical(Scene& scene) {
VkDeviceMemory depth_img_device_mem;
VkImageView depth_img_view;
- const auto destroy_swapchain = [&]() {
+ const auto destroy_swapchain = [&] {
for (auto it_img_view = swapchain_img_views.rbegin(); it_img_view != swapchain_img_views.rend(); ++it_img_view)
vkDestroyImageView(device, *it_img_view, nullptr);
vkDestroySwapchainKHR(device, swapchain, nullptr);
};
- const auto recreate_swapchain = [&]() {
+ const auto recreate_swapchain = [&] {
bool is_first_swapchain = (swapchain == nullptr);
if (!is_first_swapchain) {
if (VkResult res = vkDeviceWaitIdle(device); res != VK_SUCCESS) {
@@ -1185,7 +1200,7 @@ static int main_graphical(Scene& scene) {
// TODO: should probably use version 2 of theses functions, but glfwCreateWindowSurface
// return version 1, so for now we will use version 1
- auto surface_capabilities = [&]() {
+ auto surface_capabilities = [&] {
VkSurfaceCapabilitiesKHR surface_capabilities;
if (VkResult res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_capabilities); res != VK_SUCCESS) {
std::cerr << "failed to get physical device surface capabilities, error code: " << string_VkResult(res) << std::endl;
@@ -1202,8 +1217,16 @@ static int main_graphical(Scene& scene) {
exit(EXIT_FAILURE);
}
}
+ {
+ VkCompositeAlphaFlagsKHR required_composite_alpha =
+ (transparent_window ? VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR);
+ if ((surface_capabilities.supportedCompositeAlpha & required_composite_alpha) != required_composite_alpha) {
+ std::cerr << "required composite alpha flags not present" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ }
- swapchain_extent = [&]() {
+ swapchain_extent = [&] {
if (surface_capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
return surface_capabilities.currentExtent;
int width, height;
@@ -1220,8 +1243,8 @@ static int main_graphical(Scene& scene) {
};
}();
- surface_format = [&]() {
- auto surface_formats = [&]() {
+ surface_format = [&] {
+ auto surface_formats = [&] {
uint32_t surface_formats_count;
if (VkResult res = vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &surface_formats_count, nullptr); res != VK_SUCCESS) {
std::cerr << "failed to get physical device surface formats, error code: " << string_VkResult(res) << std::endl;
@@ -1246,9 +1269,9 @@ static int main_graphical(Scene& scene) {
exit(EXIT_FAILURE);
}();
- [&]() {
- auto present_mode = [&]() {
- auto present_modes = [&]() {
+ [&] {
+ auto present_mode = [&] {
+ auto present_modes = [&] {
uint32_t present_modes_count;
if (VkResult res = vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_modes_count, nullptr); res != VK_SUCCESS) {
std::cerr << "failed to get physical device present modes, error code: " << string_VkResult(res) << std::endl;
@@ -1293,7 +1316,7 @@ static int main_graphical(Scene& scene) {
.queueFamilyIndexCount = {},
.pQueueFamilyIndices = {},
.preTransform = surface_capabilities.currentTransform,
- .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
+ .compositeAlpha = (transparent_window ? VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR),
.presentMode = present_mode,
.clipped = VK_TRUE,
.oldSwapchain = {},
@@ -1316,7 +1339,7 @@ static int main_graphical(Scene& scene) {
}
}();
- [&]() {
+ [&] {
uint32_t swapchain_imgs_count;
if (VkResult res = vkGetSwapchainImagesKHR(device, swapchain, &swapchain_imgs_count, nullptr); res != VK_SUCCESS) {
std::cerr << "failed to get swapchain images, error code: " << string_VkResult(res) << std::endl;
@@ -1329,7 +1352,7 @@ static int main_graphical(Scene& scene) {
}
}();
- [&]() {
+ [&] {
swapchain_img_views.resize(swapchain_imgs.size());
for (uint32_t i = 0; i < swapchain_imgs.size(); i++) {
VkImageViewCreateInfo img_view_ci {
@@ -1361,10 +1384,10 @@ static int main_graphical(Scene& scene) {
recreate_swapchain();
- auto renderer_mode = GraphicalRendererMode::software;
+ auto renderer_mode = GraphicalRendererMode::hardware;
// create descriptor set layout
- auto descriptor_set_layout = [&]() {
+ auto descriptor_set_layout = [&] {
std::array descriptor_set_layout_bindings {
VkDescriptorSetLayoutBinding {
.binding = 0,
@@ -1397,10 +1420,10 @@ static int main_graphical(Scene& scene) {
}();
// create pipeline
- auto [pl_layout, graphics_pl] = [&]() {
+ auto [pl_layout, graphics_pl] = [&] {
// reading shader file
- auto shader_module = [&]() {
- auto shader_code = [&]() {
+ auto shader_module = [&] {
+ auto shader_code = [&] {
const char* shader_file_name = SHADERSDIR "/shader.spv";
std::ifstream shader_file(shader_file_name, std::ios::ate | std::ios::binary);
if (!shader_file.is_open()) {
@@ -1429,7 +1452,7 @@ static int main_graphical(Scene& scene) {
return shader_module;
}();
- auto [pl_layout, graphics_pl] = [&]() {
+ auto [pl_layout, graphics_pl] = [&] {
std::array pl_shader_stage_cis {
VkPipelineShaderStageCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
@@ -1563,7 +1586,7 @@ static int main_graphical(Scene& scene) {
.blendConstants = { 0.f, 0.f, 0.f, 0.f },
};
- auto pl_layout = [&]() {
+ auto pl_layout = [&] {
VkPipelineLayoutCreateInfo pl_layout_ci {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
@@ -1628,7 +1651,7 @@ static int main_graphical(Scene& scene) {
}();
// create command pool
- auto cmd_pool = [&]() {
+ auto cmd_pool = [&] {
VkCommandPoolCreateInfo cmd_pool_ci {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
@@ -1649,9 +1672,9 @@ static int main_graphical(Scene& scene) {
std::tie(depth_img, depth_img_device_mem, depth_img_view) = create_depth_resources(physical_device, device, swapchain_extent, depth_format);
// create texture image
- auto [texture_img, texture_img_device_mem] = [&]() {
+ auto [texture_img, texture_img_device_mem] = [&] {
int w, h, channels;
- stbi_uc* pixels = stbi_load(DATADIR "/assets/textures/texture.jpg", &w, &h, &channels, STBI_rgb_alpha);
+ stbi_uc* pixels = stbi_load(DATADIR "/assets/textures/viking_room.png", &w, &h, &channels, STBI_rgb_alpha);
// TODO: we're also using this size as the host pixels buffer size, I don't know if mixing
// the two will cause problems later. Right now they are the same, so it doesn't
@@ -1708,7 +1731,7 @@ static int main_graphical(Scene& scene) {
// TODO: this is the same code, with minor differences, than the one that create swapchain image
// views, so maybe we should create a function. The thing is, this code only call a single
// function with a single create-info, so I'm not sure if it would make sense
- auto texture_img_view = [&]() {
+ auto texture_img_view = [&] {
VkImageViewCreateInfo img_view_ci {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = {},
@@ -1734,7 +1757,7 @@ static int main_graphical(Scene& scene) {
}();
// create texture sampler
- auto sampler = [&]() {
+ auto sampler = [&] {
VkSamplerCreateInfo sampler_ci {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = nullptr,
@@ -1765,7 +1788,7 @@ static int main_graphical(Scene& scene) {
}();
// create vertex buffers
- auto [vertex_bufs, vertex_buf_device_mems] = [&]() {
+ auto [vertex_bufs, vertex_buf_device_mems] = [&] {
std::vector<VkBuffer> vertex_bufs;
std::vector<VkDeviceMemory> vertex_buf_device_mems;
for (const auto& vertices : meshes_vertices) {
@@ -1817,7 +1840,7 @@ static int main_graphical(Scene& scene) {
// TODO: this code is pretty much a duplicate with minor differences of the vertex_bufs one. We
// should probably factor it out in a function. Also, every TODOs were remove for this one, but
// they still hold
- auto [index_bufs, index_buf_device_mems] = [&]() {
+ auto [index_bufs, index_buf_device_mems] = [&] {
std::vector<VkBuffer> index_bufs;
std::vector<VkDeviceMemory> index_buf_device_mems;
for (const auto& indices : meshes_indices) {
@@ -1861,7 +1884,7 @@ static int main_graphical(Scene& scene) {
}();
// create uniform buffers
- auto [uniform_bufs, uniform_buf_device_mems, uniform_buf_mems] = [&]() {
+ auto [uniform_bufs, uniform_buf_device_mems, uniform_buf_mems] = [&] {
constexpr VkDeviceSize uniform_buf_size = sizeof(engine::vk::UniformBufferObject);
std::vector<VkBuffer> uniform_bufs(max_frames_in_flight * scene.objs.size(), nullptr);
std::vector<VkDeviceMemory> uniform_buf_device_mems(max_frames_in_flight * scene.objs.size(), nullptr);
@@ -1879,7 +1902,7 @@ static int main_graphical(Scene& scene) {
}();
// create descriptor pool
- auto descriptor_pool = [&]() {
+ auto descriptor_pool = [&] {
std::array descriptor_pool_sizes {
VkDescriptorPoolSize {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
@@ -1912,7 +1935,7 @@ static int main_graphical(Scene& scene) {
// TODO: right now, we just send every descriptors every frame and for every object, without
// taking into account update frequencies. For example we should have only one set for the
// camera, shared between every objects, etc
- auto descriptor_sets = [&]() {
+ auto descriptor_sets = [&] {
std::vector<VkDescriptorSetLayout> layouts(max_frames_in_flight * scene.objs.size(), descriptor_set_layout);
VkDescriptorSetAllocateInfo descriptor_set_ai {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
@@ -1974,7 +1997,7 @@ static int main_graphical(Scene& scene) {
}();
// create command buffers
- auto cmd_bufs = [&]() {
+ auto cmd_bufs = [&] {
VkCommandBufferAllocateInfo cmd_buf_ai {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.pNext = nullptr,
@@ -1991,10 +2014,10 @@ static int main_graphical(Scene& scene) {
}();
// create sync objects
- auto [sems_present_complete, sems_render_finished] = [&]() {
+ auto [sems_present_complete, sems_render_finished] = [&] {
// TODO: remove duplicate code
- auto sems_present_complete = [&]() {
+ auto sems_present_complete = [&] {
std::array<VkSemaphore, max_frames_in_flight> sems_present_complete;
size_t num = 0;
for (auto& sem : sems_present_complete) {
@@ -2011,7 +2034,7 @@ static int main_graphical(Scene& scene) {
return sems_present_complete;
}();
- auto sems_render_finished = [&]() {
+ auto sems_render_finished = [&] {
std::vector<VkSemaphore> sems_render_finished(swapchain_imgs.size());
size_t num = 0;
for (auto& sem : sems_render_finished) {
@@ -2050,7 +2073,7 @@ static int main_graphical(Scene& scene) {
}
std::cout << " ]" << std::endl;
- auto fences_in_flight = [&]() {
+ auto fences_in_flight = [&] {
std::array<VkFence, max_frames_in_flight> fences_in_flight;
size_t num = 0;
for (auto& fence_in_flight : fences_in_flight) {
@@ -2087,7 +2110,7 @@ static int main_graphical(Scene& scene) {
std::array<VkDeviceMemory, max_frames_in_flight> software_renderer_buf_device_mems;
std::array<void*, max_frames_in_flight> software_renderer_buf_mems;
- auto destroy_software_renderer_bufs = [&]() {
+ auto destroy_software_renderer_bufs = [&] {
for (size_t i = max_frames_in_flight; i > 0; i--) {
vkUnmapMemory(device, software_renderer_buf_device_mems[i - 1]);
vkFreeMemory(device, software_renderer_buf_device_mems[i - 1], nullptr);
@@ -2095,7 +2118,7 @@ static int main_graphical(Scene& scene) {
}
};
- auto recreate_software_renderer_bufs = [&]() {
+ auto recreate_software_renderer_bufs = [&] {
if (!first_software_renderer_buf_creation) {
destroy_software_renderer_bufs();
if (software_renderer.width() != swapchain_extent.width || software_renderer.height() != swapchain_extent.height)
@@ -2125,7 +2148,7 @@ static int main_graphical(Scene& scene) {
scene_main(Matrix4::idty(), scene,
// update_surface_size
- [&]() {
+ [&] {
return std::tuple { swapchain_extent.width, swapchain_extent.height };
},
@@ -2219,7 +2242,7 @@ static int main_graphical(Scene& scene) {
},
// render_and_present_frame
- [&](const Matrix4& view_mat, const Matrix4& proj_mat, float /* time */, float ellapsed_time) {
+ [&](const Matrix4& view_mat, const Matrix4& proj_mat, const Matrix4& inv_view_mat, float /* time */, float ellapsed_time) {
if (show_fps)
std::cout << "\rfps: " << (1.f / ellapsed_time) << " ";
@@ -2246,9 +2269,6 @@ static int main_graphical(Scene& scene) {
}
}
- // TODO: this is where we set uniform buffers for the hardware renderer, and where we
- // render for the software renderer. We should do approximately the same thing in the
- // same place for both renderers
switch (renderer_mode) {
case GraphicalRendererMode::hardware:
{
@@ -2256,20 +2276,24 @@ static int main_graphical(Scene& scene) {
for (size_t i = 0; i < scene.objs.size(); i++) {
// TODO: should use push constants
engine::vk::UniformBufferObject ubo {
- .model = scene.objs[i].transform.to_mat4(),
- .view = view_mat,
- .proj = proj_mat,
- .camera_rot = Matrix4::from_quaternion(scene.camera.transform.rot),
- .camera_loc = scene.camera.transform.loc,
+ .model = scene.objs[i].transform.to_mat4(),
+ .view = view_mat,
+ .proj = proj_mat,
+ .inv_view = inv_view_mat,
};
memcpy(uniform_buf_mems[frame_idx * scene.objs.size() + i], &ubo, sizeof(engine::vk::UniformBufferObject));
}
}
break;
case GraphicalRendererMode::software:
+ break;
+ }
+
+ switch (renderer_mode) {
+ case GraphicalRendererMode::hardware:
+ break;
+ case GraphicalRendererMode::software:
{
- // TODO: this is a copy-paste of the code in render_software, which is obviously
- // bad
auto proj_view_mat = proj_mat * view_mat;
software_renderer.clear();
for (const auto& obj : scene.objs) {
@@ -2277,17 +2301,27 @@ static int main_graphical(Scene& scene) {
auto final_mat = proj_view_mat * model_mat;
const auto& mesh = obj.mesh;
std::vector<Vector4> vertices;
+ std::vector<Vector3> normals;
std::vector<VertexData> vertices_data;
for (const auto& vertex : mesh.vertices) {
- vertices.push_back(final_mat * Vector4 { vertex, 1.f });
- vertices_data.push_back(VertexData((model_mat * vertex).xyz()));
+ Vector4 vertex4 { vertex, 1.f };
+ vertices.push_back(final_mat * vertex4);
+ vertices_data.push_back(VertexData((model_mat * vertex4).xyz()));
}
+ for (const auto& normal : mesh.normals)
+ normals.push_back((model_mat * Vector4 { normal, 0.f }).xyz());
for (const auto& triangle_indices : mesh.indices) {
- [&]<std::size_t... j>(std::integer_sequence<std::size_t, j...>) {
- software_renderer.draw_triangle(
- {{vertices[triangle_indices[j][0]], mesh.normals[triangle_indices[j][1]], vertices_data[triangle_indices[j][0]]}...}
- );
- }(std::make_integer_sequence<std::size_t, 3>());
+ software_renderer.draw_triangle([&]<size_t... j>(std::index_sequence<j...>) constexpr -> engine::o3d::Triangle {
+ return {
+ {
+ vertices[triangle_indices[j][0]],
+ normals [triangle_indices[j][1]],
+ mesh.uvs[triangle_indices[j][2]],
+ vertices_data[triangle_indices[j][0]]
+ }
+ ...
+ };
+ }(std::make_index_sequence<3>()));
}
}
memcpy(software_renderer_buf_mems[frame_idx], software_renderer.fb.pixels(), swapchain_extent.width * swapchain_extent.height * 4);
@@ -2380,7 +2414,7 @@ static int main_graphical(Scene& scene) {
}
{
- VkClearValue clear_color_val { .color { .float32 = { 0.f, 0.f, 0.f, 1.f } } };
+ VkClearValue clear_color_val { .color { .float32 = { 0.f, 0.f, 0.f, (transparent_window ? 0.f : 1.f) } } };
VkClearValue clear_depth_val { .depthStencil { .depth = 1.f, .stencil = 0 } };
VkRenderingAttachmentInfo rendering_color_info {
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
@@ -2760,6 +2794,17 @@ int main(int argc, char *argv[]) {
}
},
};
+ case GameType::test:
+ return {
+ {
+ engine::parse_object(DATADIR "/assets/viking_room.obj"),
+ {
+ Vector3(0.f, .5f, 0.f),
+ Quaternion::look_towards({ -1.f, 0.f, 0.f }, { 0.f, 0.f, 1.f }).conjugate(),
+ Vector3(1.f, 1.f, 1.f),
+ }
+ },
+ };
}
std::unreachable();
}()
diff --git a/src/fb/chfb.cpp b/src/fb/chfb.cpp
index 9f293ed..7341015 100644
--- a/src/fb/chfb.cpp
+++ b/src/fb/chfb.cpp
@@ -8,6 +8,7 @@
using namespace engine::fb;
using
+ engine::math::Vector2,
engine::math::Vector3,
engine::o3d::VertexData,
engine::o3d::Camera;
@@ -31,10 +32,8 @@ void CharacterFrameBuffer::clear() {
char brightness_chars[] = " `.-':_,^=;><+!rc*/z?sLTv)J7(|Fi{C}fI31tlu[neoZ5Yxjya]2ESwqkP6h9d4VpOGbUAKXHm8RD#$Bg0MNWQ%&@";
extern Camera* camera;
-void CharacterFrameBuffer::draw_point(int x, int y, const Vector3& loc, const VertexData& vd, const Vector3& normal) {
- (void) loc;
- (void) vd;
- auto v = normal.rot(camera->transform.rot.conjugate());
+void CharacterFrameBuffer::draw_point(int x, int y, const Vector3& /* loc */, const Vector3& normal, const Vector2& /* uv */, const VertexData& /* vd */) {
+ auto v = camera->transform.rot.conjugate().rot(normal).normalize();
float light = .1f + (v.z < 0.f ? 0.f : v.z) * .9f;
std::uint32_t c = (int) (light * static_cast<float>(sizeof(brightness_chars) - 1));
chars_vector[x + y * w] = brightness_chars[c];
diff --git a/src/fb/chfb.hpp b/src/fb/chfb.hpp
index 7f6857b..1748abf 100644
--- a/src/fb/chfb.hpp
+++ b/src/fb/chfb.hpp
@@ -7,14 +7,13 @@
namespace engine::fb {
-using engine::math::Vector3, engine::o3d::VertexData;
-
class CharacterFrameBuffer {
public:
CharacterFrameBuffer(unsigned int w, unsigned int h);
void resize(unsigned int w, unsigned int h);
void clear();
- void draw_point(int x, int y, const Vector3& loc, const VertexData& vd, const Vector3& normal);
+ void draw_point(int x, int y, const engine::math::Vector3& loc,
+ const engine::math::Vector3& normal, const engine::math::Vector2& uv, const engine::o3d::VertexData& vd);
constexpr unsigned int width() const & {
return w;
diff --git a/src/fb/pixfb.cpp b/src/fb/pixfb.cpp
index b38ec30..c629adb 100644
--- a/src/fb/pixfb.cpp
+++ b/src/fb/pixfb.cpp
@@ -8,6 +8,7 @@
using namespace engine::fb;
using
+ engine::math::Vector2,
engine::math::Vector3,
engine::o3d::VertexData,
engine::o3d::Camera;
@@ -29,17 +30,15 @@ void PixelFrameBuffer::clear() {
extern Camera* camera;
-void PixelFrameBuffer::draw_point(int x, int y, const Vector3& loc, const VertexData& vd, const Vector3& normal) {
- (void) loc;
- (void) vd;
+void PixelFrameBuffer::draw_point(int x, int y, const Vector3& /* loc */, const Vector3& normal, const Vector2& /* uv */, const VertexData& vd) {
auto u = vd.world_loc - camera->transform.loc;
float u_len_sq = u.length_squared();
u = u.normalize();
- float attenuation = Vector3(0.f, 0.f, -1.f).rot(camera->transform.rot).dot(u);
+ float attenuation = camera->transform.rot.rot({ 0.f, 0.f, -1.f }).dot(u);
if (attenuation < .7f) attenuation = 0.f;
else if (attenuation > .8f) attenuation = 1.f;
else attenuation = (attenuation - .7f) / .1f;
- float light = -normal.dot(u) / u_len_sq;
+ float light = -normal.normalize().dot(u) / u_len_sq;
if (light < 0.f) light = 0.f;
float final_light = .003f + light * attenuation * .8f * .997f;
if (final_light > 1.f) final_light = 1.f;
diff --git a/src/fb/pixfb.hpp b/src/fb/pixfb.hpp
index 92fa604..954f6e1 100644
--- a/src/fb/pixfb.hpp
+++ b/src/fb/pixfb.hpp
@@ -8,14 +8,13 @@
namespace engine::fb {
-using engine::math::Vector3, engine::o3d::VertexData;
-
class PixelFrameBuffer {
public:
PixelFrameBuffer(unsigned int w, unsigned int h);
void resize(unsigned int w, unsigned int h);
void clear();
- void draw_point(int x, int y, const Vector3& loc, const VertexData& vd, const Vector3& normal);
+ void draw_point(int x, int y, const engine::math::Vector3& loc,
+ const engine::math::Vector3& normal, const engine::math::Vector2& uv, const engine::o3d::VertexData& vd);
constexpr unsigned int width() const & {
return w;
diff --git a/src/math/mat4.hpp b/src/math/mat4.hpp
index 6a215d3..b25dbd4 100644
--- a/src/math/mat4.hpp
+++ b/src/math/mat4.hpp
@@ -4,6 +4,7 @@
#include <array>
#include <cmath>
#include "math/vector.hpp"
+#include "math/quat.hpp"
namespace engine::math {
diff --git a/src/math/quat.hpp b/src/math/quat.hpp
index 51392e7..9687c98 100644
--- a/src/math/quat.hpp
+++ b/src/math/quat.hpp
@@ -2,6 +2,7 @@
#define MATH_QUAT_HPP
#include <cmath>
+#include "math/vector.hpp"
namespace engine::math {
@@ -30,6 +31,23 @@ struct Quaternion {
return {std::cos(a / 2.f), 0.f, std::sin(a / 2.f), 0.f};
}
+ static constexpr Quaternion look_towards(const Vector3& dir, const Vector3& up) {
+ // TODO: extract common code between Matrix4::look_at and this. We should have something
+ // similar to a function returning a 3x3 matrix which does:
+ // e_x -> up.cross(-dir).normalize()
+ // e_y -> (-dir).cross(e_x).normalize()
+ // e_z -> (-dir).normalize()
+ Vector3 new_x = up.cross(-dir).normalize();
+ Vector3 new_y = (-dir).cross(new_x).normalize();
+ Vector3 new_z = (-dir).normalize();
+ return {
+ std::sqrt(std::max(0.f, new_x.x + new_y.y + new_z.z + 1.f)) / 2.f,
+ std::copysign(std::sqrt(std::max(0.f, new_x.x - new_y.y - new_z.z + 1.f)) / 2.f, new_y.z - new_z.y),
+ std::copysign(std::sqrt(std::max(0.f, -new_x.x + new_y.y - new_z.z + 1.f)) / 2.f, new_z.x - new_x.z),
+ std::copysign(std::sqrt(std::max(0.f, -new_x.x - new_y.y + new_z.z + 1.f)) / 2.f, new_x.y - new_y.x),
+ };
+ }
+
float w, x, y, z;
constexpr Quaternion() {}
@@ -71,6 +89,14 @@ struct Quaternion {
constexpr Quaternion conjugate() const & {
return {w, -x, -y, -z};
}
+
+ constexpr Vector3 rot(const Vector3& v) const & {
+ return {
+ (2.f * (w * w + x * x) - 1.f) * v.x + (2.f * (x * y - w * z) ) * v.y + (2.f * (x * z + w * y) ) * v.z,
+ (2.f * (x * y + w * z) ) * v.x + (2.f * (w * w + y * y) - 1.f) * v.y + (2.f * (y * z - w * x) ) * v.z,
+ (2.f * (x * z - w * y) ) * v.x + (2.f * (y * z + w * x) ) * v.y + (2.f * (w * w + z * z) - 1.f) * v.z,
+ };
+ }
};
}
diff --git a/src/math/utils.hpp b/src/math/utils.hpp
new file mode 100644
index 0000000..5ec5959
--- /dev/null
+++ b/src/math/utils.hpp
@@ -0,0 +1,24 @@
+#ifndef MATH_UTILS_HPP
+#define MATH_UTILS_HPP
+
+#include <array>
+#include <utility>
+#include "math/vector.hpp"
+
+namespace engine::math::utils {
+
+template<size_t size> struct Vector;
+template<> struct Vector<2> { using type = engine::math::Vector2; };
+template<> struct Vector<3> { using type = engine::math::Vector3; };
+template<> struct Vector<4> { using type = engine::math::Vector4; };
+
+template<size_t vector_size>
+constexpr Vector<vector_size>::type array_to_vec(const std::array<float, vector_size>& coords) {
+ return [&]<size_t... i>(std::index_sequence<i...>) constexpr -> Vector<vector_size>::type {
+ return { coords[i] ... };
+ }(std::make_index_sequence<vector_size>());
+}
+
+}
+
+#endif // MATH_UTILS_HPP
diff --git a/src/math/vector.hpp b/src/math/vector.hpp
index e836e0d..471f30d 100644
--- a/src/math/vector.hpp
+++ b/src/math/vector.hpp
@@ -2,11 +2,20 @@
#define MATH_VECTOR_HPP
#include <cmath>
-#include "math/quat.hpp"
namespace engine::math {
+struct Vector2;
+constexpr Vector2 operator*(float n, const Vector2& other);
+constexpr Vector2 operator/(const Vector2& other, float n);
+
struct Vector2 {
+ static constexpr size_t size = 2;
+
+ static constexpr Vector2 bilerp(const Vector2& v1, const Vector2& v2, const Vector2& v3, float b0, float b1) {
+ return b0 * v1 + b1 * v2 + (1.f - b0 - b1) * v3;
+ }
+
float x, y;
constexpr bool operator==(const Vector2& other) const & {
@@ -77,6 +86,8 @@ constexpr Vector3 operator*(float n, const Vector3& other);
constexpr Vector3 operator/(const Vector3& other, float n);
struct Vector3 {
+ static constexpr size_t size = 3;
+
static constexpr Vector3 bilerp(const Vector3& v1, const Vector3& v2, const Vector3& v3, float b0, float b1) {
return b0 * v1 + b1 * v2 + (1.f - b0 - b1) * v3;
}
@@ -126,14 +137,6 @@ struct Vector3 {
};
}
- constexpr Vector3 rot(const Quaternion& q) const & {
- return {
- (2.f * (q.w * q.w + q.x * q.x) - 1.f) * x + (2.f * (q.x * q.y - q.w * q.z) ) * y + (2.f * (q.x * q.z + q.w * q.y) ) * z,
- (2.f * (q.x * q.y + q.w * q.z) ) * x + (2.f * (q.w * q.w + q.y * q.y) - 1.f) * y + (2.f * (q.y * q.z - q.w * q.x) ) * z,
- (2.f * (q.x * q.z - q.w * q.y) ) * x + (2.f * (q.y * q.z + q.w * q.x) ) * y + (2.f * (q.w * q.w + q.z * q.z) - 1.f) * z,
- };
- }
-
constexpr float dot(const Vector3& other) const & {
return x * other.x + y * other.y + z * other.z;
}
@@ -168,15 +171,14 @@ constexpr Vector3 operator/(const Vector3& other, float n) {
}
struct Vector4 {
+ static constexpr size_t size = 4;
+
float x, y, z, w;
constexpr Vector4() {}
constexpr Vector4(float x, float y, float z, float w) : x{x}, y{y}, z{z}, w{w} {}
- constexpr Vector4(float x, float y, float z) : x{x}, y{y}, z{z}, w{1.f} {}
constexpr Vector4(const Vector2& v, float z, float w) : x{v.x}, y{v.y}, z{z}, w{w} {}
- constexpr Vector4(const Vector2& v, float z) : x{v.x}, y{v.y}, z{z}, w{1.f} {}
constexpr Vector4(const Vector3& v, float w) : x{v.x}, y{v.y}, z{v.z}, w{w} {}
- constexpr Vector4(const Vector3& v) : x{v.x}, y{v.y}, z{v.z}, w{1.f} {}
constexpr bool operator==(const Vector4& other) const & {
return x == other.x && y == other.y && z == other.z && w == other.w;
diff --git a/src/o3d/camera.hpp b/src/o3d/camera.hpp
index eb10450..ff99c08 100644
--- a/src/o3d/camera.hpp
+++ b/src/o3d/camera.hpp
@@ -4,18 +4,14 @@
#include "math/mat4.hpp"
#include "math/tform.hpp"
-using
- engine::math::Matrix4,
- engine::math::Transform;
-
namespace engine::o3d {
struct Camera {
float fov;
- Transform transform;
+ engine::math::Transform transform;
- constexpr Matrix4 to_mat4(float aspect_ratio, float min_z, float max_z) const & {
- return Matrix4::perspective(fov, aspect_ratio, min_z, max_z) * transform.to_inverse_mat4();
+ constexpr engine::math::Matrix4 to_mat4(float aspect_ratio, float min_z, float max_z) const & {
+ return engine::math::Matrix4::perspective(fov, aspect_ratio, min_z, max_z) * transform.to_inverse_mat4();
}
};
diff --git a/src/o3d/deriv_vertex.hpp b/src/o3d/deriv_vertex.hpp
index 6fc32e0..5e8dc22 100644
--- a/src/o3d/deriv_vertex.hpp
+++ b/src/o3d/deriv_vertex.hpp
@@ -5,10 +5,8 @@
namespace engine::o3d {
-using engine::math::Vector4;
-
struct DerivedVertex {
- Vector4 vertex;
+ engine::math::Vector4 vertex;
float b0, b1;
constexpr DerivedVertex div_by_w() const & {
diff --git a/src/o3d/mesh.cpp b/src/o3d/mesh.cpp
index ce6da30..fc5ff78 100644
--- a/src/o3d/mesh.cpp
+++ b/src/o3d/mesh.cpp
@@ -4,11 +4,23 @@
#include <cstdint>
#include <cstddef>
#include <tuple>
+#include <unordered_map>
#include "math/vector.hpp"
#include "vulkan_utils.hpp"
using namespace engine::o3d;
+template<size_t indices_size>
+struct VertexIndicesHasher {
+ // taken from boost's container_hash
+ std::size_t operator()(const std::array<size_t, indices_size>& vertex_indices) const {
+ size_t h = 0;
+ for (const auto& idx : vertex_indices)
+ h ^= std::hash<size_t>{}(idx) + 0x9e3779b9 + (h << 6) + (h >> 2);
+ return h;
+ }
+};
+
Mesh Mesh::plane(float width, float height) {
const float w2 = width / 2,
h2 = height / 2;
@@ -24,23 +36,36 @@ Mesh Mesh::plane(float width, float height) {
{ 0.f, +1.f, 0.f },
},
{
- {{ {{ 0, 0 }}, {{ 1, 0 }}, {{ 2, 0 }} }},
- {{ {{ 2, 0 }}, {{ 3, 0 }}, {{ 0, 0 }} }},
- {{ {{ 0, 1 }}, {{ 3, 1 }}, {{ 2, 1 }} }},
- {{ {{ 2, 1 }}, {{ 1, 1 }}, {{ 0, 1 }} }},
+ { 0.f, 0.f },
+ { 1.f, 0.f },
+ { 1.f, 1.f },
+ { 0.f, 1.f },
+ },
+ {
+ {{ {{ 0, 0, 0 }}, {{ 1, 0, 1 }}, {{ 2, 0, 2 }} }},
+ {{ {{ 2, 0, 2 }}, {{ 3, 0, 3 }}, {{ 0, 0, 0 }} }},
+ {{ {{ 0, 1, 0 }}, {{ 3, 1, 3 }}, {{ 2, 1, 2 }} }},
+ {{ {{ 2, 1, 2 }}, {{ 1, 1, 1 }}, {{ 0, 1, 0 }} }},
}
};
}
std::tuple<std::vector<engine::vk::Vertex>, std::vector<uint16_t>> Mesh::linearize_indices() const & {
+ std::unordered_map<std::array<size_t, 3>, size_t, VertexIndicesHasher<3>> unique_vertices;
std::vector<engine::vk::Vertex> linearized_vertices;
std::vector<uint16_t> linearized_indices;
- size_t n = 0;
for (const auto& triangle_indices : this->indices) {
for (const auto& vertex_indices : triangle_indices) {
- linearized_vertices.emplace_back(this->vertices[vertex_indices[0]], this->normals[vertex_indices[1]]);
- linearized_indices.emplace_back(n++);
+ auto it = unique_vertices.find(vertex_indices);
+ if (it == unique_vertices.end()) {
+ linearized_vertices.emplace_back(this->vertices[vertex_indices[0]], this->normals[vertex_indices[1]], this->uvs[vertex_indices[2]]);
+ size_t idx = linearized_vertices.size() - 1;
+ unique_vertices.emplace(vertex_indices, idx);
+ linearized_indices.emplace_back(idx);
+ } else {
+ linearized_indices.emplace_back((*it).second);
+ }
}
}
diff --git a/src/o3d/mesh.hpp b/src/o3d/mesh.hpp
index 525d9bd..d1d3f1c 100644
--- a/src/o3d/mesh.hpp
+++ b/src/o3d/mesh.hpp
@@ -17,7 +17,8 @@ struct Mesh {
std::vector<engine::math::Vector3> vertices;
std::vector<engine::math::Vector3> normals;
- std::vector<std::array<std::array<size_t, 2>, 3>> indices;
+ std::vector<engine::math::Vector2> uvs;
+ std::vector<std::array<std::array<size_t, 3>, 3>> indices;
// TODO: find a better way to do this. This workaround is due to the fact that vulkan only
// accepts a single index, not an index for each attributes
diff --git a/src/o3d/vertex.hpp b/src/o3d/vertex.hpp
index 1605f0d..8d02297 100644
--- a/src/o3d/vertex.hpp
+++ b/src/o3d/vertex.hpp
@@ -6,12 +6,11 @@
namespace engine::o3d {
-using engine::math::Vector3, engine::math::Vector4;
-
struct Vertex {
- Vector4 vertex;
- Vector3 normal;
- VertexData data;
+ engine::math::Vector4 vertex;
+ engine::math::Vector3 normal;
+ engine::math::Vector2 uv;
+ engine::o3d::VertexData data;
};
}
diff --git a/src/o3d/vertex_data.hpp b/src/o3d/vertex_data.hpp
index f70100c..fa62271 100644
--- a/src/o3d/vertex_data.hpp
+++ b/src/o3d/vertex_data.hpp
@@ -3,9 +3,6 @@
#include "math/vector.hpp"
-using
- engine::math::Vector3;
-
namespace engine::o3d {
struct VertexData {
@@ -21,7 +18,7 @@ struct VertexData {
};
}
- Vector3 world_loc;
+ engine::math::Vector3 world_loc;
};
}
diff --git a/src/obj_parser.cpp b/src/obj_parser.cpp
index d83d75f..28df871 100644
--- a/src/obj_parser.cpp
+++ b/src/obj_parser.cpp
@@ -6,37 +6,44 @@
#include <vector>
#include <cstddef>
#include <array>
+#include <string_view>
+#include <optional>
#include "math/vector.hpp"
+#include "math/utils.hpp"
#include "o3d/mesh.hpp"
#include "o3d/vertex_data.hpp"
namespace engine {
-namespace {
+using math::Vector2, math::Vector3;
-std::vector<std::string> split(const std::string& s, char sep) {
- std::vector<std::string> res;
- std::string::size_type last_ind = 0;
- for (std::string::size_type ind = 0; ind < s.length(); ind++) {
+template<typename CallbackFn>
+static void split(const std::string_view& s, char sep, CallbackFn fn) {
+ size_t n = 0;
+ std::string_view::size_type last_ind = 0;
+ for (std::string_view::size_type ind = 0; ind < s.length(); ind++) {
if (s[ind] == sep) {
- res.push_back(s.substr(last_ind, ind - last_ind));
+ if (!fn(n, s.substr(last_ind, ind - last_ind))) return;
+ n++;
last_ind = ind + 1;
}
}
- res.push_back(s.substr(last_ind));
- return res;
+ fn(n, s.substr(last_ind));
}
-float parse_float(const std::string& s) {
+template<typename NumType>
+static std::optional<NumType> parse_num(const std::string_view& s);
+
+template<>
+std::optional<float> parse_num<float>(const std::string_view& s) {
std::string::size_type ind = 0;
bool positive = true;
while (ind < s.length() && (s[ind] == '+' || s[ind] == '-'))
if (s[ind++] == '-')
positive = !positive;
- // TODO: improve error checking
if (ind == s.length())
- return 0.f;
+ return {};
int n = 0;
while (ind < s.length() && s[ind] >= '0' && s[ind] <= '9')
@@ -45,9 +52,8 @@ float parse_float(const std::string& s) {
if (ind == s.length())
return (positive ? 1.f : -1.f) * static_cast<float>(n);
- // TODO: improve error checking
if (s[ind] != '.')
- return 0.f;
+ return {};
ind++;
@@ -58,79 +64,116 @@ float parse_float(const std::string& s) {
decimal_fac *= .1f;
}
- // TODO: improve error checking
if (ind != s.length())
- return 0.f;
+ return {};
return (positive ? 1.f : -1.f) * (static_cast<float>(n) + decimal_part);
}
-float parse_int(const std::string& s) {
+template<>
+std::optional<int> parse_num<int>(const std::string_view& s) {
std::string::size_type ind = 0;
bool positive = true;
while (ind < s.length() && (s[ind] == '+' || s[ind] == '-'))
if (s[ind++] == '-')
positive = !positive;
- // TODO: improve error checking
if (ind == s.length())
- return 0.f;
+ return {};
int n = 0;
while (ind < s.length() && s[ind] >= '0' && s[ind] <= '9')
n = n * 10 + static_cast<int>(s[ind++]) - static_cast<int>('0');
- // TODO: improve error checking
if (ind != s.length())
- return 0.f;
+ return {};
return (positive ? 1.f : -1.f) * n;
}
+template<typename VectorType>
+static constexpr std::optional<VectorType> split_to_vec(const std::string_view& s, char sep) {
+ std::array<float, VectorType::size> coords;
+ bool err = false;
+ split(s, sep, [&](auto n, const auto& coord_s) {
+ auto coord = parse_num<float>(coord_s);
+ if (!coord) {
+ err = true;
+ return false;
+ }
+ coords[n] = *coord;
+ return true;
+ });
+ if (err) return {};
+ return engine::math::utils::array_to_vec(coords);
+}
+
+// TODO: improve this. This is a workaround to have a unwrap-like function, but we should check more
+// precisely the error
+template<typename Ret, size_t line_num>
+[[noreturn]]
+static std::optional<Ret> err_exit() {
+ std::cerr << "error: failed to parse .obj file (line number " << line_num << ")" << std::endl;
+ exit(EXIT_FAILURE);
}
o3d::Mesh parse_object(const std::string& obj_path) {
o3d::Mesh mesh;
std::ifstream obj_file(obj_path);
if (!obj_file.is_open()) {
- std::cerr << "file `" << obj_path << "'not found" << std::endl; // TODO: improve
- std::exit(1);
+ std::cerr << "file `" << obj_path << "' not found" << std::endl; // TODO: improve
+ exit(EXIT_FAILURE);
}
std::string line;
while (std::getline(obj_file, line)) {
- if (line.length() == 0 || line[0] == '#')
+ std::string_view line_view { line };
+ if (line_view.length() == 0 || line_view[0] == '#')
continue;
- if (line.rfind("o ", 0) == 0) {
- // std::cout << "Object: " << line.substr(2) << std::endl;
- } else if (line.rfind("v ", 0) == 0) {
- auto s_coords = split(line.substr(2), ' ');
- mesh.vertices.emplace_back(parse_float(s_coords[0]), parse_float(s_coords[1]), parse_float(s_coords[2]));
- } else if (line.rfind("vn ", 0) == 0) {
- auto s_coords = split(line.substr(3), ' ');
- mesh.normals.emplace_back(parse_float(s_coords[0]), parse_float(s_coords[1]), parse_float(s_coords[2]));
- } else if (line.rfind("s ", 0) == 0) {
- // auto smooth = false;
- // auto s_smooth = line.substr(2);
- // if (s_smooth == "0" || s_smooth == "off") {
- // smooth = false;
- // } else if (s_smooth == "1" || s_smooth == "on") {
- // smooth = true;
- // }
- // std::cout << "Smooth: " << std::boolalpha << smooth << std::endl;
- } else if (line.rfind("f ", 0) == 0) {
- std::array<std::array<std::size_t, 2>, 3> indices;
- auto line_split = split(line.substr(2), ' ');
- for (int i = 0; i < 3; i++) {
- auto indices_s_group = split(line_split[i], '/');
- indices[i][0] = parse_int(indices_s_group[0]) - 1;
- indices[i][1] = parse_int(indices_s_group[2]) - 1;
- }
- // std::cout << "Face:"
- // << " 1: vertex: " << indices[0][0] << " normal: " << indices[0][1]
- // << " 2: vertex: " << indices[1][0] << " normal: " << indices[1][1]
- // << " 3: vertex: " << indices[2][0] << " normal: " << indices[2][1]
- // << std::endl;
+ auto data_type_idx = line_view.find(" ");
+ if (data_type_idx == std::string_view::npos) {
+ std::cerr << "error: failed to parse .obj file: no argument given to data type '" << line_view << "`" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ auto data_type = line_view.substr(0, data_type_idx);
+ auto data_arg = line_view.substr(data_type_idx + 1);
+ if (data_type == "o") {
+ // TODO: implement
+ } else if (data_type == "v") {
+ mesh.vertices.push_back(*split_to_vec<Vector3>(data_arg, ' ').or_else(err_exit<Vector3, __LINE__>));
+ } else if (data_type == "vn") {
+ mesh.normals.push_back(*split_to_vec<Vector3>(data_arg, ' ').or_else(err_exit<Vector3, __LINE__>));
+ } else if (data_type == "vt") {
+ auto uv = *split_to_vec<Vector2>(data_arg, ' ').or_else(err_exit<Vector2, __LINE__>);
+ uv.y = 1.f - uv.y;
+ mesh.uvs.push_back(uv);
+ } else if (data_type == "s") {
+ // TODO: implement
+ } else if (data_type == "f") {
+ std::array<std::array<std::size_t, 3>, 3> indices;
+ bool err = false;
+ split(data_arg, ' ', [&](auto m, const auto& vertex_indices_s) {
+ split(vertex_indices_s, '/', [&](auto n, const auto& idx_s) {
+ // indices of texture coordinates and normals are reversed in .obj relative to the
+ // engine
+ auto idx_opt = parse_num<int>(idx_s);
+ if (!idx_opt) {
+ err = true;
+ return false;
+ }
+ indices[m][(n > 0 ? 3 - n : n)] = *idx_opt - 1;
+ return true;
+ });
+ return !err;
+ });
+ if (err) err_exit<int, __LINE__>();
mesh.indices.push_back(indices);
+ } else if (data_type == "mtllib") {
+ // TODO: implement
+ } else if (data_type == "usemtl") {
+ // TODO: implement
+ } else {
+ std::cerr << "error while parsing .obj file: unknown data type: " << line_view << std::endl;
+ exit(EXIT_FAILURE);
}
}
return mesh;
diff --git a/src/renderer.cpp b/src/renderer.cpp
index 73b31f7..cf97af8 100644
--- a/src/renderer.cpp
+++ b/src/renderer.cpp
@@ -10,6 +10,7 @@
using namespace engine;
using
engine::math::Vector2,
+ engine::math::Vector3,
engine::math::Vector4,
engine::o3d::Triangle,
engine::o3d::TriangleDerived,
@@ -19,8 +20,7 @@ using
engine::fb::PixelFrameBuffer;
template<typename FrameBuffer>
-template<typename FBArg>
-Renderer<FrameBuffer>::Renderer(FBArg&& fb) : fb{std::forward<FBArg>(fb)} {
+Renderer<FrameBuffer>::Renderer(FrameBuffer&& fb) : fb { std::forward<FrameBuffer>(fb) } {
depth_buf.resize(this->fb.width() * this->fb.height());
}
@@ -145,8 +145,9 @@ void Renderer<FrameBuffer>::_draw_cropped_triangle(const Triangle& root, const T
loc_z
};
fb.draw_point(x, y, loc,
- VertexData::bilerp(root.vertex1.data, root.vertex2.data, root.vertex3.data, b0, b1),
- Vector3::bilerp(root.vertex1.normal, root.vertex2.normal, root.vertex3.normal, b0, b1).normalize());
+ Vector3 ::bilerp(root.vertex1.normal, root.vertex2.normal, root.vertex3.normal, b0, b1),
+ Vector2 ::bilerp(root.vertex1.uv, root.vertex2.uv, root.vertex3.uv, b0, b1),
+ VertexData::bilerp(root.vertex1.data, root.vertex2.data, root.vertex3.data, b0, b1));
}
}
};
@@ -155,8 +156,4 @@ void Renderer<FrameBuffer>::_draw_cropped_triangle(const Triangle& root, const T
}
template class Renderer<CharacterFrameBuffer>;
-template Renderer<CharacterFrameBuffer>::Renderer(CharacterFrameBuffer&&);
-template Renderer<CharacterFrameBuffer>::Renderer(const CharacterFrameBuffer&);
template class Renderer<PixelFrameBuffer>;
-template Renderer<PixelFrameBuffer>::Renderer(PixelFrameBuffer&&);
-template Renderer<PixelFrameBuffer>::Renderer(const PixelFrameBuffer&);
diff --git a/src/renderer.hpp b/src/renderer.hpp
index ec67489..ea79fce 100644
--- a/src/renderer.hpp
+++ b/src/renderer.hpp
@@ -8,19 +8,16 @@
namespace engine {
-using math::Vector3, o3d::Triangle;
-
template<typename FrameBuffer>
class Renderer {
public:
FrameBuffer fb;
- template<typename FBArg>
- Renderer(FBArg&& fb);
+ Renderer(FrameBuffer&& fb);
void resize(unsigned int w, unsigned int h);
void clear();
- void draw_triangle(const Triangle& triangle);
+ void draw_triangle(const o3d::Triangle& triangle);
constexpr unsigned int width() const & {
return fb.width();
diff --git a/src/shaders/shader.slang b/src/shaders/shader.slang
index e92f5aa..245df4b 100644
--- a/src/shaders/shader.slang
+++ b/src/shaders/shader.slang
@@ -1,12 +1,11 @@
struct VertexInput {
float3 pos;
float3 normal;
+ float2 uv;
};
struct UniformBuffer {
- float4x4 model, view, proj;
- float4x4 camera_rot;
- float3 camera_loc;
+ float4x4 model, view, proj, inv_view;
};
ConstantBuffer<UniformBuffer> ubo;
@@ -15,6 +14,7 @@ struct VertexOutput {
float4 pos : SV_Position;
float3 world_loc;
float3 normal;
+ float2 uv;
};
[shader("vertex")]
@@ -23,7 +23,8 @@ VertexOutput vert_main(VertexInput vi) {
float4 world_loc = mul(ubo.model, float4(vi.pos, 1.));
vo.pos = mul(ubo.proj, mul(ubo.view, world_loc));
vo.world_loc = world_loc.xyz;
- vo.normal = vi.normal;
+ vo.normal = mul(ubo.model, float4(vi.normal, 0.)).xyz;
+ vo.uv = vi.uv;
return vo;
}
@@ -31,16 +32,17 @@ Sampler2D texture;
[shader("fragment")]
float4 frag_main(VertexOutput vo) : SV_Target {
- float3 u = vo.world_loc - ubo.camera_loc;
- float u_len_sq = dot(u, u);
- u = normalize(u);
- float attenuation = dot(mul(ubo.camera_rot, float4(0., 0., -1., 0.)).xyz, u);
- if (attenuation < .7) attenuation = 0.f;
- else if (attenuation > .8) attenuation = 1.;
- else attenuation = (attenuation - .7) / .1;
- float light = -dot(vo.normal, u) / u_len_sq;
- if (light < 0.) light = 0.;
- float final_light = .003 + light * attenuation * .8 * .997;
- if (final_light > 1.) final_light = 1.;
- return float4(final_light, final_light, final_light, 1.);
+ // float3 u = vo.world_loc - (ubo.inv_view * float4(0., 0., 0., 1.)).xyz;
+ // float u_len_sq = dot(u, u);
+ // u = normalize(u);
+ // float attenuation = dot(mul(ubo.inv_view, float4(0., 0., -1., 0.)).xyz, u);
+ // if (attenuation < .7) attenuation = 0.f;
+ // else if (attenuation > .8) attenuation = 1.;
+ // else attenuation = (attenuation - .7) / .1;
+ // float light = -dot(vo.normal, u) / u_len_sq;
+ // if (light < 0.) light = 0.;
+ // float final_light = .003 + light * attenuation * .8 * .997;
+ // if (final_light > 1.) final_light = 1.;
+ // return float4(final_light, final_light, final_light, 1.);
+ return float4(texture.Sample(vo.uv).rgb, 1.f);
}
diff --git a/src/vulkan_utils.hpp b/src/vulkan_utils.hpp
index c822caa..54c82c5 100644
--- a/src/vulkan_utils.hpp
+++ b/src/vulkan_utils.hpp
@@ -39,7 +39,7 @@ struct Vertex {
};
}
- static constexpr std::array<VkVertexInputAttributeDescription, 2> get_attr_descs() {
+ static constexpr std::array<VkVertexInputAttributeDescription, 3> get_attr_descs() {
return {
VkVertexInputAttributeDescription {
.location = 0,
@@ -53,19 +53,23 @@ struct Vertex {
.format = VK_FORMAT_R32G32B32_SFLOAT,
.offset = offsetof(Vertex, normal),
},
+ VkVertexInputAttributeDescription {
+ .location = 2,
+ .binding = 0,
+ .format = VK_FORMAT_R32G32_SFLOAT,
+ .offset = offsetof(Vertex, uv),
+ },
};
}
engine::math::Vector3 pos;
engine::math::Vector3 normal;
+ engine::math::Vector2 uv;
};
// TODO: move to a better place. Also, see TODOs for struct Vertex
struct UniformBufferObject {
- alignas(16) engine::math::Matrix4 model, view, proj;
- // TODO: should me Matrix3, but it isn't implemented yet
- alignas(16) engine::math::Matrix4 camera_rot;
- alignas(16) engine::math::Vector3 camera_loc;
+ alignas(16) engine::math::Matrix4 model, view, proj, inv_view;
};
}