glTF loader for Android Vulkan
This post is going to introduce how we integrate with an existing glTF loader library to make it be able to show glTF models in our Vulkan rendering framework, vulkan-android.
tinygltf
In the beginning, we don't want to make our new own wheel, so choosing tinygltf as our glTF loader. tinygltf is a C++11 based library that would help us support all possible cross-platform project easily.
tinygltf setup in Android Studio
In vulkan-android project, we put third party libraries into third_party folder. Therefore, we need to include tinygltf from third_party folder in app/CMakeLists.txt as below.
set(THIRD_PARTY_DIR ../../third_party)
include_directories(${THIRD_PARTY_DIR}/tinygltf)
Model loading from tinygltf
We are going to load a gltf ASCII or binary format model from the storage. tinygltf provides two APIs , they are LoadBinaryFromFile() and LoadASCIIFromFile() respectively based on the extension name is *.glb or *.gltf. TinyGLTF loader will return a tinygltf Model that include mesh, animation, node, material, texture, and skin data. In this article, I would like to focus on mesh and texture creating from tinygltf Model.
Buffer creating from tinygltf
texture creating from tinygltf
tinygltf handles image loading from its library. It loads image data no matter from *glb or PNG images from *.gltf, it packs its byte data into model.images. model.images stores the loaded buffers, and we need to allocate Vulkan memory and copy it to the image buffer.
First of all, create a stage buffer and copy the image buffer to it.
VkBuffer stagingBuffer;
VkDeviceMemory stagingBufferMemory;
CreateBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
void* data;
vkMapMemory(mDeviceInfo.device, stagingBufferMemory, 0, imageSize, 0, &data);
memcpy(data, aBuffer, static_cast(imageSize));
vkUnmapMemory(mDeviceInfo.device, stagingBufferMemory);
Create a Vulkan image and image memory and use a single time command buffer to copy the data from the stage buffer which we just created to this Vulkan image.
if (vkCreateImage(mDeviceInfo.device, &imageInfo, nullptr, &textureImage) != VK_SUCCESS) {
LOG_E(gAppName.data(), "failed to create image!");
return false;
}
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(mDeviceInfo.device, textureImage, &memRequirements);
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
MapMemoryTypeToIndex(memRequirements.memoryTypeBits,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &allocInfo.memoryTypeIndex);
if (vkAllocateMemory(mDeviceInfo.device, &allocInfo, nullptr, &textureImageMemory) != VK_SUCCESS) {
LOG_E(gAppName.data(), "failed to allocate image memory!");
return false;
}
vkBindImageMemory(mDeviceInfo.device, textureImage, textureImageMemory, 0);
VkCommandBuffer commandBuffer = BeginSingleTimeCommands();
SetImageLayout(commandBuffer, textureImage, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
EndSingleTimeCommands(commandBuffer);
commandBuffer = BeginSingleTimeCommands();
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = {0, 0, 0};
region.imageExtent = {
(uint32_t)aTexWidth,
(uint32_t)aTexHeight,
1
};
vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, textureImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
EndSingleTimeCommands(commandBuffer);
Comments
Post a Comment