@@ -38,7 +38,6 @@ constexpr uint32_t WIDTH = 800;
3838constexpr uint32_t HEIGHT = 600 ;
3939constexpr uint64_t FenceTimeout = 100000000 ;
4040const std::string MODEL_PATH = " models/plant_on_table.obj" ;
41- const std::string TEXTURE_PATH = " textures/nettle_plant_diff_4k.png" ;
4241constexpr int MAX_FRAMES_IN_FLIGHT = 2 ;
4342
4443const std::vector validationLayers = {
@@ -85,6 +84,10 @@ struct UniformBufferObject {
8584 alignas (16 ) glm::mat4 proj;
8685};
8786
87+ struct PushConstant {
88+ uint32_t materialIndex;
89+ };
90+
8891class HelloTriangleApplication {
8992public:
9093 void run () {
@@ -122,9 +125,9 @@ class HelloTriangleApplication {
122125 vk::raii::DeviceMemory depthImageMemory = nullptr ;
123126 vk::raii::ImageView depthImageView = nullptr ;
124127
125- vk::raii::Image textureImage = nullptr ;
126- vk::raii::DeviceMemory textureImageMemory = nullptr ;
127- vk::raii::ImageView textureImageView = nullptr ;
128+ std::vector< vk::raii::Image> textureImages ;
129+ std::vector< vk::raii::DeviceMemory> textureImageMemories ;
130+ std::vector< vk::raii::ImageView> textureImageViews ;
128131 vk::raii::Sampler textureSampler = nullptr ;
129132
130133 std::vector<Vertex> vertices;
@@ -138,6 +141,17 @@ class HelloTriangleApplication {
138141 std::vector<vk::raii::DeviceMemory> uniformBuffersMemory;
139142 std::vector<void *> uniformBuffersMapped;
140143
144+ struct SubMesh {
145+ uint32_t indexOffset;
146+ uint32_t indexCount;
147+ int materialID;
148+ uint32_t firstVertex;
149+ uint32_t maxVertex;
150+ bool alphaCut;
151+ };
152+ std::vector<SubMesh> submeshes;
153+ std::vector<tinyobj::material_t > materials;
154+
141155 vk::raii::DescriptorPool descriptorPool = nullptr ;
142156 std::vector<vk::raii::DescriptorSet> descriptorSets;
143157
@@ -187,8 +201,6 @@ class HelloTriangleApplication {
187201 createGraphicsPipeline ();
188202 createCommandPool ();
189203 createDepthResources ();
190- createTextureImage ();
191- createTextureImageView ();
192204 createTextureSampler ();
193205 loadModel ();
194206 createVertexBuffer ();
@@ -519,7 +531,13 @@ class HelloTriangleApplication {
519531 };
520532 vk::PipelineDynamicStateCreateInfo dynamicState{ .dynamicStateCount = static_cast <uint32_t >(dynamicStates.size ()), .pDynamicStates = dynamicStates.data () };
521533
522- vk::PipelineLayoutCreateInfo pipelineLayoutInfo{ .setLayoutCount = 1 , .pSetLayouts = &*descriptorSetLayout, .pushConstantRangeCount = 0 };
534+ vk::PushConstantRange pushConstantRange {
535+ .stageFlags = vk::ShaderStageFlagBits::eFragment,
536+ .offset = 0 ,
537+ .size = sizeof (PushConstant)
538+ };
539+
540+ vk::PipelineLayoutCreateInfo pipelineLayoutInfo{ .setLayoutCount = 1 , .pSetLayouts = &*descriptorSetLayout, .pushConstantRangeCount = 1 , .pPushConstantRanges = &pushConstantRange };
523541
524542 pipelineLayout = vk::raii::PipelineLayout (device, pipelineLayoutInfo);
525543
@@ -589,9 +607,9 @@ class HelloTriangleApplication {
589607 return format == vk::Format::eD32SfloatS8Uint || format == vk::Format::eD24UnormS8Uint;
590608 }
591609
592- void createTextureImage () {
610+ std::pair<vk::raii::Image, vk::raii::DeviceMemory> createTextureImage (const std::string& path ) {
593611 int texWidth, texHeight, texChannels;
594- stbi_uc* pixels = stbi_load (TEXTURE_PATH .c_str (), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
612+ stbi_uc* pixels = stbi_load (path .c_str (), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
595613 vk::DeviceSize imageSize = texWidth * texHeight * 4 ;
596614
597615 if (!pixels) {
@@ -608,15 +626,20 @@ class HelloTriangleApplication {
608626
609627 stbi_image_free (pixels);
610628
629+ vk::raii::Image textureImage = nullptr ;
630+ vk::raii::DeviceMemory textureImageMemory = nullptr ;
631+
611632 createImage (texWidth, texHeight, vk::Format::eR8G8B8A8Srgb, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal, textureImage, textureImageMemory);
612633
613634 transitionImageLayout (textureImage, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal);
614635 copyBufferToImage (stagingBuffer, textureImage, static_cast <uint32_t >(texWidth), static_cast <uint32_t >(texHeight));
615636 transitionImageLayout (textureImage, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
637+
638+ return std::make_pair (std::move (textureImage), std::move (textureImageMemory));
616639 }
617640
618- void createTextureImageView () {
619- textureImageView = createImageView (textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageAspectFlagBits::eColor);
641+ vk::raii::ImageView createTextureImageView (vk::raii::Image& textureImage ) {
642+ return createImageView (textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageAspectFlagBits::eColor);
620643 }
621644
622645 void createTextureSampler () {
@@ -720,16 +743,27 @@ class HelloTriangleApplication {
720743 void loadModel () {
721744 tinyobj::attrib_t attrib;
722745 std::vector<tinyobj::shape_t > shapes;
723- std::vector<tinyobj::material_t > materials ;
746+ std::vector<tinyobj::material_t > localMaterials ;
724747 std::string warn, err;
725748
726- if (!LoadObj (&attrib, &shapes, &materials , &warn, &err, MODEL_PATH.c_str ())) {
749+ if (!LoadObj (&attrib, &shapes, &localMaterials , &warn, &err, MODEL_PATH. c_str (), MODEL_PATH. substr ( 0 , MODEL_PATH. find_last_of ( " / \\ " )) .c_str ())) {
727750 throw std::runtime_error (warn + err);
728751 }
729752
753+ size_t materialOffset = materials.size ();
754+ size_t oldTextureCount = textureImageViews.size ();
755+
756+ materials.insert (materials.end (), localMaterials.begin (), localMaterials.end ());
757+
730758 std::unordered_map<Vertex, uint32_t > uniqueVertices{};
759+ uint32_t indexOffset = 0 ;
731760
732761 for (const auto & shape : shapes) {
762+ std::cout << " Loading mesh: " << shape.name << " : " << shape.mesh .indices .size ()/3 << " triangles\n " ;
763+
764+ uint32_t startOffset = indexOffset;
765+ uint32_t localMaxV = 0 ;
766+
733767 for (const auto & index : shape.mesh .indices ) {
734768 Vertex vertex{};
735769
@@ -752,6 +786,51 @@ class HelloTriangleApplication {
752786 }
753787
754788 indices.push_back (uniqueVertices[vertex]);
789+
790+ indexOffset++;
791+
792+ uint32_t vi;
793+ auto it = uniqueVertices.find (vertex);
794+ if (it != uniqueVertices.end ()) {
795+ vi = it->second ;
796+ } else {
797+ vi = static_cast <uint32_t >(vertices.size ());
798+ uniqueVertices[vertex] = vi;
799+ vertices.push_back (vertex);
800+ }
801+
802+ localMaxV = std::max (localMaxV, vi);
803+ }
804+
805+ int localMaterialID = shape.mesh .material_ids .empty () ? -1 : shape.mesh .material_ids [0 ];
806+ int globalMaterialID = (localMaterialID < 0 ) ? -1 : static_cast <int >(materialOffset + localMaterialID);
807+
808+ uint32_t indexCount = indexOffset - startOffset;
809+
810+ // Note that this is only valid for this particular MODEL_PATH
811+ bool alphaCut = (shape.name .find (" nettle_plant" ) != std::string::npos);
812+
813+ submeshes.push_back ({
814+ .indexOffset = startOffset,
815+ .indexCount = indexCount,
816+ .materialID = globalMaterialID,
817+ .firstVertex = 0u ,
818+ .maxVertex = localMaxV + 1 ,
819+ .alphaCut = alphaCut
820+ });
821+ }
822+
823+ for (size_t i = 0 ; i < localMaterials.size (); ++i) {
824+ const auto & material = localMaterials[i];
825+
826+ if (!material.diffuse_texname .empty ()) {
827+ std::string texturePath = MODEL_PATH.substr (0 , MODEL_PATH.find_last_of (" /\\ " )) + " /" + material.diffuse_texname ;
828+ auto [img, mem] = createTextureImage (texturePath);
829+ textureImages.push_back (std::move (img));
830+ textureImageMemories.push_back (std::move (mem));
831+ textureImageViews.emplace_back (createTextureImageView (textureImages.back ()));
832+ } else {
833+ std::cout << " No texture for material: " << material.name << std::endl;
755834 }
756835 }
757836 }
@@ -836,7 +915,7 @@ class HelloTriangleApplication {
836915 };
837916 vk::DescriptorImageInfo imageInfo{
838917 .sampler = textureSampler,
839- .imageView = textureImageView ,
918+ .imageView = textureImageViews[ 0 ] ,
840919 .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal
841920 };
842921 std::array descriptorWrites{
@@ -1001,7 +1080,13 @@ class HelloTriangleApplication {
10011080 commandBuffers[currentFrame].bindVertexBuffers (0 , *vertexBuffer, {0 });
10021081 commandBuffers[currentFrame].bindIndexBuffer ( *indexBuffer, 0 , vk::IndexType::eUint32 );
10031082 commandBuffers[currentFrame].bindDescriptorSets (vk::PipelineBindPoint::eGraphics, pipelineLayout, 0 , *descriptorSets[currentFrame], nullptr );
1004- commandBuffers[currentFrame].drawIndexed (indices.size (), 1 , 0 , 0 , 0 );
1083+
1084+ for (auto & sub : submeshes) {
1085+ uint32_t idx = sub.materialID < 0 ? 0u : static_cast <uint32_t >(sub.materialID );
1086+ commandBuffers[currentFrame].pushConstants <PushConstant>(pipelineLayout, vk::ShaderStageFlagBits::eFragment, 0 , PushConstant{ .materialIndex = idx });
1087+ commandBuffers[currentFrame].drawIndexed (sub.indexCount , 1 , sub.indexOffset , 0 , 0 );
1088+ }
1089+
10051090 commandBuffers[currentFrame].endRendering ();
10061091 // After rendering, transition the swapchain image to PRESENT_SRC
10071092 transition_image_layout (
0 commit comments