From 8a4a1ea9dc0e6458c3341e0a81df7d9f0ecbd965 Mon Sep 17 00:00:00 2001 From: AmyFoxie Date: Thu, 14 Dec 2023 20:29:30 +0100 Subject: [PATCH] load embedded texture --- src/engine/vk/VkImage.cpp | 90 +++++++++++++++++++----------- src/engine/vk/VkImage.h | 8 +++ src/engine/vk/test/main.cpp | 41 +++++++++++--- src/engine/vk/test/vikingroom.glb | Bin 1258496 -> 1258496 bytes 4 files changed, 98 insertions(+), 41 deletions(-) diff --git a/src/engine/vk/VkImage.cpp b/src/engine/vk/VkImage.cpp index bdc7dbb..0cf938d 100644 --- a/src/engine/vk/VkImage.cpp +++ b/src/engine/vk/VkImage.cpp @@ -12,12 +12,12 @@ namespace Vixen::Vk { const uint8_t mipLevels ) : device(device), allocation(VK_NULL_HANDLE), - image(VK_NULL_HANDLE), width(width), height(height), - format(format), - usageFlags(usageFlags), layout(VK_IMAGE_LAYOUT_UNDEFINED), + usageFlags(usageFlags), + image(VK_NULL_HANDLE), + format(format), mipLevels(mipLevels) { const VkImageCreateInfo imageCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, @@ -34,7 +34,7 @@ namespace Vixen::Vk { .arrayLayers = 1, .samples = samples, .tiling = tiling, - .usage = usageFlags, + .usage = usageFlags | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -101,50 +101,38 @@ namespace Vixen::Vk { if (!bitmap) error("Failed to load image from file \"{}\"", path); - const auto& converted = FreeImage_ConvertTo32Bits(bitmap); - if (!converted) - error("Failed to convert image to 32 bits"); - - const auto& width = FreeImage_GetWidth(converted); - const auto& height = FreeImage_GetHeight(converted); - const auto& bitsPerPixel = FreeImage_GetBPP(converted); - const auto& pixels = FreeImage_GetBits(converted); - - const VkDeviceSize size = width * height * (bitsPerPixel / 8); - - auto staging = VkBuffer(device, Buffer::Usage::UNIFORM | Buffer::Usage::TRANSFER_SRC, size); - staging.write(reinterpret_cast(pixels), size, 0); + return from(device, bitmap); + } - FreeImage_Unload(converted); - FreeImage_Unload(bitmap); + VkImage VkImage::from(const std::shared_ptr& device, const std::string& format, const std::byte* data, + const uint32_t size) { + // TODO: Add some way to detect the format + const auto& memory = FreeImage_OpenMemory((BYTE*)data, size); + if (!memory) + error("Failed to open image from memory"); - FreeImage_DeInitialise(); + const auto& bitmap = FreeImage_LoadFromMemory(FreeImage_GetFIFFromFormat(format.c_str()), memory, 0); + if (!bitmap) + throw std::runtime_error("Failed to load image from memory"); - VkFormat f; - switch (bitsPerPixel) { - case 24: - f = VK_FORMAT_R8G8B8_SRGB; - break; - case 32: - f = VK_FORMAT_R8G8B8A8_SRGB; - break; - default: - throw std::runtime_error("Failed to determine format"); - } + return from(device, bitmap); + } + VkImage VkImage::from(const std::shared_ptr& device, const VkBuffer& buffer, const uint32_t width, + const uint32_t height, const VkFormat format) { auto image = VkImage( device, width, height, VK_SAMPLE_COUNT_1_BIT, - f, + format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, static_cast(std::floor(std::log2(std::max(width, height)))) + 1 ); image.transition(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - image.copyFrom(staging); + image.copyFrom(buffer); image.transition(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); return image; @@ -258,6 +246,42 @@ namespace Vixen::Vk { return mipLevels; } + VkImage VkImage::from(const std::shared_ptr& device, FIBITMAP* bitmap) { + const auto& converted = FreeImage_ConvertTo32Bits(bitmap); + if (!converted) + error("Failed to convert image to 32 bits"); + + const auto& width = FreeImage_GetWidth(converted); + const auto& height = FreeImage_GetHeight(converted); + const auto& bitsPerPixel = FreeImage_GetBPP(converted); + const auto& pixels = FreeImage_GetBits(converted); + + const VkDeviceSize size = width * height * (bitsPerPixel / 8); + + auto staging = VkBuffer(device, Buffer::Usage::UNIFORM | Buffer::Usage::TRANSFER_SRC, size); + staging.write(reinterpret_cast(pixels), size, 0); + + FreeImage_Unload(converted); + FreeImage_Unload(bitmap); + + FreeImage_DeInitialise(); + + VkFormat f; + // TODO: This will need a better implementation to detect the exact format later + switch (bitsPerPixel) { + case 24: + f = VK_FORMAT_B8G8R8_SRGB; + break; + case 32: + f = VK_FORMAT_B8G8R8A8_SRGB; + break; + default: + throw std::runtime_error("Failed to determine format"); + } + + return from(device, staging, width, height, f); + } + VkFormat VkImage::getFormat() const { return format; } diff --git a/src/engine/vk/VkImage.h b/src/engine/vk/VkImage.h index 47df8e5..5fd6e5f 100644 --- a/src/engine/vk/VkImage.h +++ b/src/engine/vk/VkImage.h @@ -48,6 +48,11 @@ namespace Vixen::Vk { static VkImage from(const std::shared_ptr& device, const std::string& path); + static VkImage from(const std::shared_ptr& device, const std::string& format, const std::byte* data, uint32_t size); + + static VkImage from(const std::shared_ptr& device, const VkBuffer& buffer, uint32_t width, + uint32_t height, VkFormat format); + void transition(VkImageLayout newLayout); void copyFrom(const VkBuffer& buffer); @@ -63,5 +68,8 @@ namespace Vixen::Vk { [[nodiscard]] const std::shared_ptr& getDevice() const; [[nodiscard]] uint8_t getMipLevels() const; + + private: + static VkImage from(const std::shared_ptr& device, FIBITMAP* bitmap); }; } diff --git a/src/engine/vk/test/main.cpp b/src/engine/vk/test/main.cpp index 2bf589a..07d7d0f 100644 --- a/src/engine/vk/test/main.cpp +++ b/src/engine/vk/test/main.cpp @@ -82,15 +82,19 @@ int main() { Assimp::Importer importer; const auto& scene = importer.ReadFile("../../src/engine/vk/test/vikingroom.glb", aiProcessPreset_TargetRealtime_Fast); - if (nullptr == scene) + if (!scene) throw std::runtime_error("Failed to load model from file"); const auto& mesh = scene->mMeshes[0]; + const auto& hasColors = mesh->HasVertexColors(0); + const auto& hasUvs = mesh->HasTextureCoords(0); + std::vector vertices(mesh->mNumVertices); for (uint32_t i = 0; i < mesh->mNumVertices; i++) { const auto& vertex = mesh->mVertices[i]; - const auto& color = mesh->HasVertexColors(i) ? *mesh->mColors[i] : aiColor4D{1.0f, 1.0f, 1.0f, 1.0f}; - const auto& uv = mesh->HasTextureCoords(i) ? *mesh->mTextureCoords[i] : aiVector3D{0.0f, 0.0f, 0.0f}; + // TODO: Instead of storing default values for each vertex where a color or UV is missing, we should compact this down to save memory + const auto& color = hasColors ? mesh->mColors[0][i] : aiColor4D{1.0f, 1.0f, 1.0f, 1.0f}; + const auto& uv = hasUvs ? mesh->mTextureCoords[0][i] : aiVector3D{1.0f, 1.0f, 1.0f}; vertices[i] = Vertex{ .position = {vertex.x, vertex.y, vertex.z}, @@ -101,7 +105,7 @@ int main() { std::vector indices(mesh->mNumFaces * 3); for (uint32_t i = 0; i < mesh->mNumFaces; i++) { - const auto &face = mesh->mFaces[i]; + const auto& face = mesh->mFaces[i]; if (face.mNumIndices != 3) { spdlog::warn("Skipping face with {} indices", face.mNumIndices); continue; @@ -112,6 +116,27 @@ int main() { indices[i * 3 + 2] = face.mIndices[2]; } + aiString path; + scene->mMaterials[mesh->mMaterialIndex]->GetTexture(aiTextureType_DIFFUSE, 0, &path); + + const auto& texture = scene->GetEmbeddedTexture(path.C_Str()); + assert(texture != nullptr && "Texture is nullptr"); + + std::shared_ptr image; + if (texture->mHeight != 0) { + image = nullptr; // TODO + } + else { + image = std::make_shared( + Vixen::Vk::VkImage::from( + vixen.device, + texture->achFormatHint, + reinterpret_cast(texture->pcData), + texture->mWidth + ) + ); + } + const auto buffer = Vixen::Vk::VkBuffer::stage( vixen.device, Vixen::Buffer::Usage::VERTEX | @@ -162,8 +187,8 @@ int main() { auto mvp = Vixen::Vk::VkDescriptorSet(vixen.device, descriptorPool, *program.getDescriptorSetLayout()); mvp.updateUniformBuffer(0, uniformBuffer, 0, uniformBuffer.getSize()); - auto image = std::make_shared( - Vixen::Vk::VkImage::from(vixen.device, "../../src/engine/vk/test/texture.jpg")); + auto testImage = std::make_shared( + Vixen::Vk::VkImage::from(vixen.device, std::string("../../src/engine/vk/test/texture.jpg"))); auto view = Vixen::Vk::VkImageView(image, VK_IMAGE_ASPECT_COLOR_BIT); auto sampler = Vixen::Vk::VkSampler(vixen.device); @@ -185,8 +210,8 @@ int main() { const double& now = glfwGetTime(); double deltaTime = now - lastFrame; lastFrame = now; - ubo.model = rotate(ubo.model, static_cast(deltaTime) * glm::radians(90.0f), - glm::vec3(0.0f, 0.0f, 1.0f)); + ubo.model = rotate(glm::mat4(1.0f), /*static_cast(deltaTime) **/ glm::radians(90.0f), + glm::vec3(1.0f, 0.0f, 0.0f)); ubo.view = camera.view(); ubo.projection = camera.perspective( static_cast(vixen.swapchain.getExtent().width) / diff --git a/src/engine/vk/test/vikingroom.glb b/src/engine/vk/test/vikingroom.glb index 8fd9de6c80ba8692a97c5874836e7fb27596b23d..b784b1da0186ee05a270811c76b8038516a41b6a 100644 GIT binary patch delta 1091 zcmY+C4@{J09LMi@p5u=9J>(Gaf)fP~1Otb|74qladou8jGA{m<(&?B}h|NK$H7t}n zM1oq0+HSl>ij=>j@YOo-md~E}E+K(?51VGPL_RbOgfE0Zk`e~&0WsIk zS|u4hZTdd$SgTbsbqris{X#z%-C~s@HZ6CqKhw|sm%}UK!48#qj}<=`@H;moUJ@j$e5llm^ESND`YB(=Pi6vrpuIkg%Q<6vJlI^+r;i#t*cPPuC2yY)xKb^B)|w?n7hfccv8o68uMk9O* z{gLyL$A+mUk{|3KTg49DIpoU7|3H|@BQf>BC}+-ZsdSBpR149h^eeCvv5j?%Ys14T z!LN`XZ2%FB%6Zr|w7E^_CP7~Vlbn6EQ|=Pqs1#aulU>bVPRTU=obyexw+(Cg!gf%^ zqz~jI9Wq(*Ai2{H8vNMf#%yWQeNa@yk(w?r2iV2m9gxoZZs!(VBegvs4zk@__hNh4 zR*pGAuJ!^QU^5ao;DG*0aqvf?_#VJzPnY-)gWCV~=o|qnyi}!3fi%6UTO3>w$=?_U zK|VtRdcoac!+ot-&xa0!aI*RyNSCvgCaXAlz1|S_F)6z81Ir)*(X6 zW%%X{ue}1NppeP?x}gD1&l9s7YE{abA#re7o1`iYL)WEDY7VY7$c~XjBgzDD!opXZPz_q% zo5x3{qUYg}Klq$fWK_&qqwu9%(wK_Mwd$&dD;zDwnq8$hIe3gD?L@KAKmNg&3j^^m z&c&y|aYM-a#F~dh0AD)68}gB1L9I`ei22B}L|#N!i3IOQH+@k!ZzG-$5u;FQGc3Zp zB@SMF6dgv?kdsFdCr2f5P2`3hUGPHXw|dCuwJ1%6F6Bm%F$YS}ur2No@uzbM(&m#S z#)VEpg~Kcp#f#*sh~^+WJs)8Bb=Hi(ctEaSL^Z4ac`X@1QU9hk-bF7X*z+v;YaHzb z8g5O1l>X>bVd0B#_cq~H_Ii9Uf>NB3{H( delta 1100 zcmY+Ce^8V~7{}Rtj{EW60futOkAgsmw1Kce1nzwoCw`42kx&7nHyVM$fS20fBye|0 z^1}%xLPV*x0eKrh;>0|hpx(Y$ysdph^SU&N@8FzTCuP!rwPHRVOjX- zIkRkRf2(85z;*t!7PFk(cB3OKzK>sZ)+~pnKHnMrbszsMM^vsw?`datn#tzjVZLC|`g_PnzsZoFdD>ot^oYZg_XTZhkgUw0H;d6ksLqSt==giy zLYT(T-UgJV9R0b~PM3TdeheikG{%>!9D@OMP7hjs0_6f{I-+rW7*Xx~vPhgb zg+eu59kq_31LvGk)aNuxR&ZRl(vdM;>3qjT3+-rDrP;nUuS-x{h__hWicFeWV_sZ& zvYGVKpIgzXC7gF^mVCbdW62gu8+*_!;{2|Z$#>nh@GaBSb_K1$TyM@U5^yh@FZ+t# zzKVhnmleH^plg*9{*fxbN7!$1iRT!y{qKzSarA<#wpddnkA+;8qA!MtLl2OTF22Cv zjBdA(GdjT_29BW>H0&WtS8x~W&C=A+F`>1V?t6q*dvLGaa+l5LBS`YCHVNn4#E<7h z-+ufXPR$nG2e5$i|Db6D*jKCSx=?FKfAS|ajp80ZE)HGejj3wMQYZ@ZICRmT?wH3V zYIr=CNyk;-g`h|?L+8~H=!>kZy&!@~dN5=`+y`m&OfX!15pNz8OA;YSUs};8ZSb-S z?C<4=M|5&Z`YZCC1LE04DA6DMsa_)Qrpi@%4Qm~%Z1=YbwIYp8f{h55Z58X1p%G>x z^Tn4^APP4>5{)U4q^#b*l0+G4a|-P9_3+u<&$CjZFDxgYuAiWBxe$pxGhB}^Tu8>q z`{X$KLWr%W<~)!Pb~K8@HV_un{8Xv54RV$$yjfdO|56xs2a{(7ov#6nQoK+D@nXs$ z$b|0GC+N{bz$+k9ZKp#G(BTSAhcD5?HrSwrx4XmW4$G)