clang format
This commit is contained in:
parent
a1afaf3de5
commit
9e96aeaa5f
2 changed files with 521 additions and 421 deletions
|
|
@ -30,7 +30,8 @@
|
||||||
Get a node's local matrix from the current translation, rotation and scale values
|
Get a node's local matrix from the current translation, rotation and scale values
|
||||||
These are calculated from the current animation an need to be calculated dynamically
|
These are calculated from the current animation an need to be calculated dynamically
|
||||||
*/
|
*/
|
||||||
glm::mat4 VulkanglTFModel::Node::getLocalMatrix() {
|
glm::mat4 VulkanglTFModel::Node::getLocalMatrix()
|
||||||
|
{
|
||||||
return glm::translate(glm::mat4(1.0f), translation) * glm::mat4(rotation) * glm::scale(glm::mat4(1.0f), scale) * matrix;
|
return glm::translate(glm::mat4(1.0f), translation) * glm::mat4(rotation) * glm::scale(glm::mat4(1.0f), scale) * matrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,7 +44,8 @@ VulkanglTFModel::~VulkanglTFModel()
|
||||||
vkFreeMemory(vulkanDevice->logicalDevice, vertices.memory, nullptr);
|
vkFreeMemory(vulkanDevice->logicalDevice, vertices.memory, nullptr);
|
||||||
vkDestroyBuffer(vulkanDevice->logicalDevice, indices.buffer, nullptr);
|
vkDestroyBuffer(vulkanDevice->logicalDevice, indices.buffer, nullptr);
|
||||||
vkFreeMemory(vulkanDevice->logicalDevice, indices.memory, nullptr);
|
vkFreeMemory(vulkanDevice->logicalDevice, indices.memory, nullptr);
|
||||||
for (Image image : images) {
|
for (Image image : images)
|
||||||
|
{
|
||||||
vkDestroyImageView(vulkanDevice->logicalDevice, image.texture.view, nullptr);
|
vkDestroyImageView(vulkanDevice->logicalDevice, image.texture.view, nullptr);
|
||||||
vkDestroyImage(vulkanDevice->logicalDevice, image.texture.image, nullptr);
|
vkDestroyImage(vulkanDevice->logicalDevice, image.texture.image, nullptr);
|
||||||
vkDestroySampler(vulkanDevice->logicalDevice, image.texture.sampler, nullptr);
|
vkDestroySampler(vulkanDevice->logicalDevice, image.texture.sampler, nullptr);
|
||||||
|
|
@ -62,32 +64,37 @@ void VulkanglTFModel::loadImages(tinygltf::Model& input)
|
||||||
// Images can be stored inside the glTF (which is the case for the sample model), so instead of directly
|
// Images can be stored inside the glTF (which is the case for the sample model), so instead of directly
|
||||||
// loading them from disk, we fetch them from the glTF loader and upload the buffers
|
// loading them from disk, we fetch them from the glTF loader and upload the buffers
|
||||||
images.resize(input.images.size());
|
images.resize(input.images.size());
|
||||||
for (size_t i = 0; i < input.images.size(); i++) {
|
for (size_t i = 0; i < input.images.size(); i++)
|
||||||
|
{
|
||||||
tinygltf::Image &glTFImage = input.images[i];
|
tinygltf::Image &glTFImage = input.images[i];
|
||||||
// Get the image data from the glTF loader
|
// Get the image data from the glTF loader
|
||||||
unsigned char *buffer = nullptr;
|
unsigned char *buffer = nullptr;
|
||||||
VkDeviceSize bufferSize = 0;
|
VkDeviceSize bufferSize = 0;
|
||||||
bool deleteBuffer = false;
|
bool deleteBuffer = false;
|
||||||
// We convert RGB-only images to RGBA, as most devices don't support RGB-formats in Vulkan
|
// We convert RGB-only images to RGBA, as most devices don't support RGB-formats in Vulkan
|
||||||
if (glTFImage.component == 3) {
|
if (glTFImage.component == 3)
|
||||||
|
{
|
||||||
bufferSize = glTFImage.width * glTFImage.height * 4;
|
bufferSize = glTFImage.width * glTFImage.height * 4;
|
||||||
buffer = new unsigned char[bufferSize];
|
buffer = new unsigned char[bufferSize];
|
||||||
unsigned char *rgba = buffer;
|
unsigned char *rgba = buffer;
|
||||||
unsigned char *rgb = &glTFImage.image[0];
|
unsigned char *rgb = &glTFImage.image[0];
|
||||||
for (size_t i = 0; i < glTFImage.width * glTFImage.height; ++i) {
|
for (size_t i = 0; i < glTFImage.width * glTFImage.height; ++i)
|
||||||
|
{
|
||||||
memcpy(rgba, rgb, sizeof(unsigned char) * 3);
|
memcpy(rgba, rgb, sizeof(unsigned char) * 3);
|
||||||
rgba += 4;
|
rgba += 4;
|
||||||
rgb += 3;
|
rgb += 3;
|
||||||
}
|
}
|
||||||
deleteBuffer = true;
|
deleteBuffer = true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
buffer = &glTFImage.image[0];
|
buffer = &glTFImage.image[0];
|
||||||
bufferSize = glTFImage.image.size();
|
bufferSize = glTFImage.image.size();
|
||||||
}
|
}
|
||||||
// Load texture from image buffer
|
// Load texture from image buffer
|
||||||
images[i].texture.fromBuffer(buffer, bufferSize, VK_FORMAT_R8G8B8A8_UNORM, glTFImage.width, glTFImage.height, vulkanDevice, copyQueue);
|
images[i].texture.fromBuffer(buffer, bufferSize, VK_FORMAT_R8G8B8A8_UNORM, glTFImage.width, glTFImage.height, vulkanDevice, copyQueue);
|
||||||
if (deleteBuffer) {
|
if (deleteBuffer)
|
||||||
|
{
|
||||||
delete[] buffer;
|
delete[] buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +103,8 @@ void VulkanglTFModel::loadImages(tinygltf::Model& input)
|
||||||
void VulkanglTFModel::loadTextures(tinygltf::Model &input)
|
void VulkanglTFModel::loadTextures(tinygltf::Model &input)
|
||||||
{
|
{
|
||||||
textures.resize(input.textures.size());
|
textures.resize(input.textures.size());
|
||||||
for (size_t i = 0; i < input.textures.size(); i++) {
|
for (size_t i = 0; i < input.textures.size(); i++)
|
||||||
|
{
|
||||||
textures[i].imageIndex = input.textures[i].source;
|
textures[i].imageIndex = input.textures[i].source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -104,15 +112,18 @@ void VulkanglTFModel::loadTextures(tinygltf::Model& input)
|
||||||
void VulkanglTFModel::loadMaterials(tinygltf::Model &input)
|
void VulkanglTFModel::loadMaterials(tinygltf::Model &input)
|
||||||
{
|
{
|
||||||
materials.resize(input.materials.size());
|
materials.resize(input.materials.size());
|
||||||
for (size_t i = 0; i < input.materials.size(); i++) {
|
for (size_t i = 0; i < input.materials.size(); i++)
|
||||||
|
{
|
||||||
// We only read the most basic properties required for our sample
|
// We only read the most basic properties required for our sample
|
||||||
tinygltf::Material glTFMaterial = input.materials[i];
|
tinygltf::Material glTFMaterial = input.materials[i];
|
||||||
// Get the base color factor
|
// Get the base color factor
|
||||||
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end()) {
|
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end())
|
||||||
|
{
|
||||||
materials[i].baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
|
materials[i].baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
|
||||||
}
|
}
|
||||||
// Get base color texture index
|
// Get base color texture index
|
||||||
if (glTFMaterial.values.find("baseColorTexture") != glTFMaterial.values.end()) {
|
if (glTFMaterial.values.find("baseColorTexture") != glTFMaterial.values.end())
|
||||||
|
{
|
||||||
materials[i].baseColorTextureIndex = glTFMaterial.values["baseColorTexture"].TextureIndex();
|
materials[i].baseColorTextureIndex = glTFMaterial.values["baseColorTexture"].TextureIndex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -120,25 +131,32 @@ void VulkanglTFModel::loadMaterials(tinygltf::Model& input)
|
||||||
|
|
||||||
// Helper functions for locating glTF nodes
|
// Helper functions for locating glTF nodes
|
||||||
|
|
||||||
VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index) {
|
VulkanglTFModel::Node *VulkanglTFModel::findNode(Node *parent, uint32_t index)
|
||||||
|
{
|
||||||
Node *nodeFound = nullptr;
|
Node *nodeFound = nullptr;
|
||||||
if (parent->index == index) {
|
if (parent->index == index)
|
||||||
|
{
|
||||||
return parent;
|
return parent;
|
||||||
}
|
}
|
||||||
for (auto& child : parent->children) {
|
for (auto &child : parent->children)
|
||||||
|
{
|
||||||
nodeFound = findNode(child, index);
|
nodeFound = findNode(child, index);
|
||||||
if (nodeFound) {
|
if (nodeFound)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nodeFound;
|
return nodeFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanglTFModel::Node* VulkanglTFModel::nodeFromIndex(uint32_t index) {
|
VulkanglTFModel::Node *VulkanglTFModel::nodeFromIndex(uint32_t index)
|
||||||
|
{
|
||||||
Node *nodeFound = nullptr;
|
Node *nodeFound = nullptr;
|
||||||
for (auto& node : nodes) {
|
for (auto &node : nodes)
|
||||||
|
{
|
||||||
nodeFound = findNode(node, index);
|
nodeFound = findNode(node, index);
|
||||||
if (nodeFound) {
|
if (nodeFound)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +168,8 @@ void VulkanglTFModel::loadSkins(tinygltf::Model& input)
|
||||||
{
|
{
|
||||||
skins.resize(input.skins.size());
|
skins.resize(input.skins.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < input.skins.size(); i++) {
|
for (size_t i = 0; i < input.skins.size(); i++)
|
||||||
|
{
|
||||||
tinygltf::Skin glTFSkin = input.skins[i];
|
tinygltf::Skin glTFSkin = input.skins[i];
|
||||||
|
|
||||||
skins[i].name = glTFSkin.name;
|
skins[i].name = glTFSkin.name;
|
||||||
|
|
@ -158,15 +177,18 @@ void VulkanglTFModel::loadSkins(tinygltf::Model& input)
|
||||||
skins[i].skeletonRoot = nodeFromIndex(glTFSkin.skeleton);
|
skins[i].skeletonRoot = nodeFromIndex(glTFSkin.skeleton);
|
||||||
|
|
||||||
// Find joint nodes
|
// Find joint nodes
|
||||||
for (int jointIndex : glTFSkin.joints) {
|
for (int jointIndex : glTFSkin.joints)
|
||||||
|
{
|
||||||
Node *node = nodeFromIndex(jointIndex);
|
Node *node = nodeFromIndex(jointIndex);
|
||||||
if (node) {
|
if (node)
|
||||||
|
{
|
||||||
skins[i].joints.push_back(node);
|
skins[i].joints.push_back(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the inverse bind matrices from the buffer associated to this skin
|
// Get the inverse bind matrices from the buffer associated to this skin
|
||||||
if (glTFSkin.inverseBindMatrices > -1) {
|
if (glTFSkin.inverseBindMatrices > -1)
|
||||||
|
{
|
||||||
const tinygltf::Accessor & accessor = input.accessors[glTFSkin.inverseBindMatrices];
|
const tinygltf::Accessor & accessor = input.accessors[glTFSkin.inverseBindMatrices];
|
||||||
const tinygltf::BufferView &bufferView = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView &bufferView = input.bufferViews[accessor.bufferView];
|
||||||
const tinygltf::Buffer & buffer = input.buffers[bufferView.buffer];
|
const tinygltf::Buffer & buffer = input.buffers[bufferView.buffer];
|
||||||
|
|
@ -191,13 +213,15 @@ void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
|
||||||
{
|
{
|
||||||
animations.resize(input.animations.size());
|
animations.resize(input.animations.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < input.animations.size(); i++) {
|
for (size_t i = 0; i < input.animations.size(); i++)
|
||||||
|
{
|
||||||
tinygltf::Animation glTFAnimation = input.animations[i];
|
tinygltf::Animation glTFAnimation = input.animations[i];
|
||||||
animations[i].name = glTFAnimation.name;
|
animations[i].name = glTFAnimation.name;
|
||||||
|
|
||||||
// Samplers
|
// Samplers
|
||||||
animations[i].samplers.resize(glTFAnimation.samplers.size());
|
animations[i].samplers.resize(glTFAnimation.samplers.size());
|
||||||
for (size_t j = 0; j < glTFAnimation.samplers.size(); j++) {
|
for (size_t j = 0; j < glTFAnimation.samplers.size(); j++)
|
||||||
|
{
|
||||||
tinygltf::AnimationSampler glTFSampler = glTFAnimation.samplers[j];
|
tinygltf::AnimationSampler glTFSampler = glTFAnimation.samplers[j];
|
||||||
AnimationSampler & dstSampler = animations[i].samplers[j];
|
AnimationSampler & dstSampler = animations[i].samplers[j];
|
||||||
dstSampler.interpolation = glTFSampler.interpolation;
|
dstSampler.interpolation = glTFSampler.interpolation;
|
||||||
|
|
@ -209,14 +233,18 @@ void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
|
||||||
const tinygltf::Buffer & buffer = input.buffers[bufferView.buffer];
|
const tinygltf::Buffer & buffer = input.buffers[bufferView.buffer];
|
||||||
const void * dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
const void * dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
||||||
const float * buf = static_cast<const float *>(dataPtr);
|
const float * buf = static_cast<const float *>(dataPtr);
|
||||||
for (size_t index = 0; index < accessor.count; index++) {
|
for (size_t index = 0; index < accessor.count; index++)
|
||||||
|
{
|
||||||
dstSampler.inputs.push_back(buf[index]);
|
dstSampler.inputs.push_back(buf[index]);
|
||||||
}
|
}
|
||||||
for (auto input : animations[i].samplers[j].inputs) {
|
for (auto input : animations[i].samplers[j].inputs)
|
||||||
if (input < animations[i].start) {
|
{
|
||||||
|
if (input < animations[i].start)
|
||||||
|
{
|
||||||
animations[i].start = input;
|
animations[i].start = input;
|
||||||
};
|
};
|
||||||
if (input > animations[i].end) {
|
if (input > animations[i].end)
|
||||||
|
{
|
||||||
animations[i].end = input;
|
animations[i].end = input;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,17 +256,20 @@ void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
|
||||||
const tinygltf::BufferView &bufferView = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView &bufferView = input.bufferViews[accessor.bufferView];
|
||||||
const tinygltf::Buffer & buffer = input.buffers[bufferView.buffer];
|
const tinygltf::Buffer & buffer = input.buffers[bufferView.buffer];
|
||||||
const void * dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
const void * dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
||||||
switch (accessor.type) {
|
switch (accessor.type)
|
||||||
|
{
|
||||||
case TINYGLTF_TYPE_VEC3: {
|
case TINYGLTF_TYPE_VEC3: {
|
||||||
const glm::vec3 *buf = static_cast<const glm::vec3 *>(dataPtr);
|
const glm::vec3 *buf = static_cast<const glm::vec3 *>(dataPtr);
|
||||||
for (size_t index = 0; index < accessor.count; index++) {
|
for (size_t index = 0; index < accessor.count; index++)
|
||||||
|
{
|
||||||
dstSampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f));
|
dstSampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TINYGLTF_TYPE_VEC4: {
|
case TINYGLTF_TYPE_VEC4: {
|
||||||
const glm::vec4 *buf = static_cast<const glm::vec4 *>(dataPtr);
|
const glm::vec4 *buf = static_cast<const glm::vec4 *>(dataPtr);
|
||||||
for (size_t index = 0; index < accessor.count; index++) {
|
for (size_t index = 0; index < accessor.count; index++)
|
||||||
|
{
|
||||||
dstSampler.outputsVec4.push_back(buf[index]);
|
dstSampler.outputsVec4.push_back(buf[index]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -253,7 +284,8 @@ void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
animations[i].channels.resize(glTFAnimation.channels.size());
|
animations[i].channels.resize(glTFAnimation.channels.size());
|
||||||
for (size_t j = 0; j < glTFAnimation.channels.size(); j++) {
|
for (size_t j = 0; j < glTFAnimation.channels.size(); j++)
|
||||||
|
{
|
||||||
tinygltf::AnimationChannel glTFChannel = glTFAnimation.channels[j];
|
tinygltf::AnimationChannel glTFChannel = glTFAnimation.channels[j];
|
||||||
AnimationChannel & dstChannel = animations[i].channels[j];
|
AnimationChannel & dstChannel = animations[i].channels[j];
|
||||||
dstChannel.path = glTFChannel.target_path;
|
dstChannel.path = glTFChannel.target_path;
|
||||||
|
|
@ -273,33 +305,41 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
|
|
||||||
// Get the local node matrix
|
// Get the local node matrix
|
||||||
// It's either made up from translation, rotation, scale or a 4x4 matrix
|
// It's either made up from translation, rotation, scale or a 4x4 matrix
|
||||||
if (inputNode.translation.size() == 3) {
|
if (inputNode.translation.size() == 3)
|
||||||
|
{
|
||||||
node->translation = glm::make_vec3(inputNode.translation.data());
|
node->translation = glm::make_vec3(inputNode.translation.data());
|
||||||
}
|
}
|
||||||
if (inputNode.rotation.size() == 4) {
|
if (inputNode.rotation.size() == 4)
|
||||||
|
{
|
||||||
glm::quat q = glm::make_quat(inputNode.rotation.data());
|
glm::quat q = glm::make_quat(inputNode.rotation.data());
|
||||||
node->rotation = glm::mat4(q);
|
node->rotation = glm::mat4(q);
|
||||||
}
|
}
|
||||||
if (inputNode.scale.size() == 3) {
|
if (inputNode.scale.size() == 3)
|
||||||
|
{
|
||||||
node->scale = glm::make_vec3(inputNode.scale.data());
|
node->scale = glm::make_vec3(inputNode.scale.data());
|
||||||
}
|
}
|
||||||
if (inputNode.matrix.size() == 16) {
|
if (inputNode.matrix.size() == 16)
|
||||||
|
{
|
||||||
node->matrix = glm::make_mat4x4(inputNode.matrix.data());
|
node->matrix = glm::make_mat4x4(inputNode.matrix.data());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load node's children
|
// Load node's children
|
||||||
if (inputNode.children.size() > 0) {
|
if (inputNode.children.size() > 0)
|
||||||
for (size_t i = 0; i < inputNode.children.size(); i++) {
|
{
|
||||||
|
for (size_t i = 0; i < inputNode.children.size(); i++)
|
||||||
|
{
|
||||||
loadNode(input.nodes[inputNode.children[i]], input, node, inputNode.children[i], indexBuffer, vertexBuffer);
|
loadNode(input.nodes[inputNode.children[i]], input, node, inputNode.children[i], indexBuffer, vertexBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the node contains mesh data, we load vertices and indices from the the buffers
|
// If the node contains mesh data, we load vertices and indices from the the buffers
|
||||||
// In glTF this is done via accessors and buffer views
|
// In glTF this is done via accessors and buffer views
|
||||||
if (inputNode.mesh > -1) {
|
if (inputNode.mesh > -1)
|
||||||
|
{
|
||||||
const tinygltf::Mesh mesh = input.meshes[inputNode.mesh];
|
const tinygltf::Mesh mesh = input.meshes[inputNode.mesh];
|
||||||
// Iterate through all primitives of this node's mesh
|
// Iterate through all primitives of this node's mesh
|
||||||
for (size_t i = 0; i < mesh.primitives.size(); i++) {
|
for (size_t i = 0; i < mesh.primitives.size(); i++)
|
||||||
|
{
|
||||||
const tinygltf::Primitive &glTFPrimitive = mesh.primitives[i];
|
const tinygltf::Primitive &glTFPrimitive = mesh.primitives[i];
|
||||||
uint32_t firstIndex = static_cast<uint32_t>(indexBuffer.size());
|
uint32_t firstIndex = static_cast<uint32_t>(indexBuffer.size());
|
||||||
uint32_t vertexStart = static_cast<uint32_t>(vertexBuffer.size());
|
uint32_t vertexStart = static_cast<uint32_t>(vertexBuffer.size());
|
||||||
|
|
@ -315,21 +355,24 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
size_t vertexCount = 0;
|
size_t vertexCount = 0;
|
||||||
|
|
||||||
// Get buffer data for vertex normals
|
// Get buffer data for vertex normals
|
||||||
if (glTFPrimitive.attributes.find("POSITION") != glTFPrimitive.attributes.end()) {
|
if (glTFPrimitive.attributes.find("POSITION") != glTFPrimitive.attributes.end())
|
||||||
|
{
|
||||||
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("POSITION")->second];
|
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("POSITION")->second];
|
||||||
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
||||||
positionBuffer = reinterpret_cast<const float *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
positionBuffer = reinterpret_cast<const float *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||||||
vertexCount = accessor.count;
|
vertexCount = accessor.count;
|
||||||
}
|
}
|
||||||
// Get buffer data for vertex normals
|
// Get buffer data for vertex normals
|
||||||
if (glTFPrimitive.attributes.find("NORMAL") != glTFPrimitive.attributes.end()) {
|
if (glTFPrimitive.attributes.find("NORMAL") != glTFPrimitive.attributes.end())
|
||||||
|
{
|
||||||
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("NORMAL")->second];
|
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("NORMAL")->second];
|
||||||
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
||||||
normalsBuffer = reinterpret_cast<const float *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
normalsBuffer = reinterpret_cast<const float *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||||||
}
|
}
|
||||||
// Get buffer data for vertex texture coordinates
|
// Get buffer data for vertex texture coordinates
|
||||||
// glTF supports multiple sets, we only load the first one
|
// glTF supports multiple sets, we only load the first one
|
||||||
if (glTFPrimitive.attributes.find("TEXCOORD_0") != glTFPrimitive.attributes.end()) {
|
if (glTFPrimitive.attributes.find("TEXCOORD_0") != glTFPrimitive.attributes.end())
|
||||||
|
{
|
||||||
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("TEXCOORD_0")->second];
|
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("TEXCOORD_0")->second];
|
||||||
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
||||||
texCoordsBuffer = reinterpret_cast<const float *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
texCoordsBuffer = reinterpret_cast<const float *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||||||
|
|
@ -337,13 +380,15 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
|
|
||||||
// POI: Get buffer data required for vertex skinning
|
// POI: Get buffer data required for vertex skinning
|
||||||
// Get vertex joint indices
|
// Get vertex joint indices
|
||||||
if (glTFPrimitive.attributes.find("JOINTS_0") != glTFPrimitive.attributes.end()) {
|
if (glTFPrimitive.attributes.find("JOINTS_0") != glTFPrimitive.attributes.end())
|
||||||
|
{
|
||||||
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("JOINTS_0")->second];
|
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("JOINTS_0")->second];
|
||||||
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
||||||
jointIndicesBuffer = reinterpret_cast<const uint16_t *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
jointIndicesBuffer = reinterpret_cast<const uint16_t *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||||||
}
|
}
|
||||||
// Get vertex joint weights
|
// Get vertex joint weights
|
||||||
if (glTFPrimitive.attributes.find("WEIGHTS_0") != glTFPrimitive.attributes.end()) {
|
if (glTFPrimitive.attributes.find("WEIGHTS_0") != glTFPrimitive.attributes.end())
|
||||||
|
{
|
||||||
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("WEIGHTS_0")->second];
|
const tinygltf::Accessor & accessor = input.accessors[glTFPrimitive.attributes.find("WEIGHTS_0")->second];
|
||||||
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
const tinygltf::BufferView &view = input.bufferViews[accessor.bufferView];
|
||||||
jointWeightsBuffer = reinterpret_cast<const float *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
jointWeightsBuffer = reinterpret_cast<const float *>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||||||
|
|
@ -352,7 +397,8 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
hasSkin = (jointIndicesBuffer && jointWeightsBuffer);
|
hasSkin = (jointIndicesBuffer && jointWeightsBuffer);
|
||||||
|
|
||||||
// Append data to model's vertex buffer
|
// Append data to model's vertex buffer
|
||||||
for (size_t v = 0; v < vertexCount; v++) {
|
for (size_t v = 0; v < vertexCount; v++)
|
||||||
|
{
|
||||||
Vertex vert{};
|
Vertex vert{};
|
||||||
vert.pos = glm::vec4(glm::make_vec3(&positionBuffer[v * 3]), 1.0f);
|
vert.pos = glm::vec4(glm::make_vec3(&positionBuffer[v * 3]), 1.0f);
|
||||||
vert.normal = glm::normalize(glm::vec3(normalsBuffer ? glm::make_vec3(&normalsBuffer[v * 3]) : glm::vec3(0.0f)));
|
vert.normal = glm::normalize(glm::vec3(normalsBuffer ? glm::make_vec3(&normalsBuffer[v * 3]) : glm::vec3(0.0f)));
|
||||||
|
|
@ -372,11 +418,13 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
indexCount += static_cast<uint32_t>(accessor.count);
|
indexCount += static_cast<uint32_t>(accessor.count);
|
||||||
|
|
||||||
// glTF supports different component types of indices
|
// glTF supports different component types of indices
|
||||||
switch (accessor.componentType) {
|
switch (accessor.componentType)
|
||||||
|
{
|
||||||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: {
|
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: {
|
||||||
uint32_t *buf = new uint32_t[accessor.count];
|
uint32_t *buf = new uint32_t[accessor.count];
|
||||||
memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint32_t));
|
memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint32_t));
|
||||||
for (size_t index = 0; index < accessor.count; index++) {
|
for (size_t index = 0; index < accessor.count; index++)
|
||||||
|
{
|
||||||
indexBuffer.push_back(buf[index] + vertexStart);
|
indexBuffer.push_back(buf[index] + vertexStart);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -384,7 +432,8 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: {
|
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: {
|
||||||
uint16_t *buf = new uint16_t[accessor.count];
|
uint16_t *buf = new uint16_t[accessor.count];
|
||||||
memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint16_t));
|
memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint16_t));
|
||||||
for (size_t index = 0; index < accessor.count; index++) {
|
for (size_t index = 0; index < accessor.count; index++)
|
||||||
|
{
|
||||||
indexBuffer.push_back(buf[index] + vertexStart);
|
indexBuffer.push_back(buf[index] + vertexStart);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -392,7 +441,8 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
|
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
|
||||||
uint8_t *buf = new uint8_t[accessor.count];
|
uint8_t *buf = new uint8_t[accessor.count];
|
||||||
memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint8_t));
|
memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(uint8_t));
|
||||||
for (size_t index = 0; index < accessor.count; index++) {
|
for (size_t index = 0; index < accessor.count; index++)
|
||||||
|
{
|
||||||
indexBuffer.push_back(buf[index] + vertexStart);
|
indexBuffer.push_back(buf[index] + vertexStart);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -410,10 +460,12 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent) {
|
if (parent)
|
||||||
|
{
|
||||||
parent->children.push_back(node);
|
parent->children.push_back(node);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
nodes.push_back(node);
|
nodes.push_back(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -423,10 +475,12 @@ void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// POI: Traverse the node hierarchy to the top-most parent to get the local matrix of the given node
|
// POI: Traverse the node hierarchy to the top-most parent to get the local matrix of the given node
|
||||||
glm::mat4 VulkanglTFModel::getNodeMatrix(VulkanglTFModel::Node* node) {
|
glm::mat4 VulkanglTFModel::getNodeMatrix(VulkanglTFModel::Node *node)
|
||||||
|
{
|
||||||
glm::mat4 nodeMatrix = node->getLocalMatrix();
|
glm::mat4 nodeMatrix = node->getLocalMatrix();
|
||||||
VulkanglTFModel::Node *currentParent = node->parent;
|
VulkanglTFModel::Node *currentParent = node->parent;
|
||||||
while (currentParent) {
|
while (currentParent)
|
||||||
|
{
|
||||||
nodeMatrix = currentParent->getLocalMatrix() * nodeMatrix;
|
nodeMatrix = currentParent->getLocalMatrix() * nodeMatrix;
|
||||||
currentParent = currentParent->parent;
|
currentParent = currentParent->parent;
|
||||||
}
|
}
|
||||||
|
|
@ -434,8 +488,10 @@ glm::mat4 VulkanglTFModel::getNodeMatrix(VulkanglTFModel::Node* node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// POI: Update the joint matrices from the current animation frame and pass them to the GPU
|
// POI: Update the joint matrices from the current animation frame and pass them to the GPU
|
||||||
void VulkanglTFModel::updateJoints(VulkanglTFModel::Node* node) {
|
void VulkanglTFModel::updateJoints(VulkanglTFModel::Node *node)
|
||||||
if (node->skin > -1) {
|
{
|
||||||
|
if (node->skin > -1)
|
||||||
|
{
|
||||||
glm::mat4 m = getNodeMatrix(node);
|
glm::mat4 m = getNodeMatrix(node);
|
||||||
// Update joint matrices
|
// Update joint matrices
|
||||||
glm::mat4 inverseTransform = glm::inverse(m);
|
glm::mat4 inverseTransform = glm::inverse(m);
|
||||||
|
|
@ -443,7 +499,8 @@ void VulkanglTFModel::updateJoints(VulkanglTFModel::Node* node) {
|
||||||
size_t numJoints = (uint32_t) skin.joints.size();
|
size_t numJoints = (uint32_t) skin.joints.size();
|
||||||
std::vector<glm::mat4> jointMatrices(numJoints);
|
std::vector<glm::mat4> jointMatrices(numJoints);
|
||||||
// @todo: bail out if model has more joints than shader can handle
|
// @todo: bail out if model has more joints than shader can handle
|
||||||
for (size_t i = 0; i < numJoints; i++) {
|
for (size_t i = 0; i < numJoints; i++)
|
||||||
|
{
|
||||||
jointMatrices[i] = getNodeMatrix(skin.joints[i]) * skin.inverseBindMatrices[i];
|
jointMatrices[i] = getNodeMatrix(skin.joints[i]) * skin.inverseBindMatrices[i];
|
||||||
jointMatrices[i] = inverseTransform * jointMatrices[i];
|
jointMatrices[i] = inverseTransform * jointMatrices[i];
|
||||||
}
|
}
|
||||||
|
|
@ -451,7 +508,8 @@ void VulkanglTFModel::updateJoints(VulkanglTFModel::Node* node) {
|
||||||
skin.ssbo.copyTo(jointMatrices.data(), jointMatrices.size() * sizeof(glm::mat4));
|
skin.ssbo.copyTo(jointMatrices.data(), jointMatrices.size() * sizeof(glm::mat4));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& child : node->children) {
|
for (auto &child : node->children)
|
||||||
|
{
|
||||||
updateJoints(child);
|
updateJoints(child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -459,33 +517,42 @@ void VulkanglTFModel::updateJoints(VulkanglTFModel::Node* node) {
|
||||||
// POI: Update the current animation
|
// POI: Update the current animation
|
||||||
void VulkanglTFModel::updateAnimation(float deltaTime)
|
void VulkanglTFModel::updateAnimation(float deltaTime)
|
||||||
{
|
{
|
||||||
if (activeAnimation > static_cast<uint32_t>(animations.size()) - 1) {
|
if (activeAnimation > static_cast<uint32_t>(animations.size()) - 1)
|
||||||
|
{
|
||||||
std::cout << "No animation with index " << activeAnimation << std::endl;
|
std::cout << "No animation with index " << activeAnimation << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Animation &animation = animations[activeAnimation];
|
Animation &animation = animations[activeAnimation];
|
||||||
animation.currentTime += deltaTime;
|
animation.currentTime += deltaTime;
|
||||||
if (animation.currentTime > animation.end) {
|
if (animation.currentTime > animation.end)
|
||||||
|
{
|
||||||
animation.currentTime -= animation.end;
|
animation.currentTime -= animation.end;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool updated = false;
|
bool updated = false;
|
||||||
for (auto& channel : animation.channels) {
|
for (auto &channel : animation.channels)
|
||||||
|
{
|
||||||
AnimationSampler &sampler = animation.samplers[channel.samplerIndex];
|
AnimationSampler &sampler = animation.samplers[channel.samplerIndex];
|
||||||
if (sampler.inputs.size() > sampler.outputsVec4.size()) {
|
if (sampler.inputs.size() > sampler.outputsVec4.size())
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < sampler.inputs.size() - 1; i++) {
|
for (size_t i = 0; i < sampler.inputs.size() - 1; i++)
|
||||||
if ((animation.currentTime >= sampler.inputs[i]) && (animation.currentTime <= sampler.inputs[i + 1])) {
|
{
|
||||||
|
if ((animation.currentTime >= sampler.inputs[i]) && (animation.currentTime <= sampler.inputs[i + 1]))
|
||||||
|
{
|
||||||
float u = std::max(0.0f, animation.currentTime - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]);
|
float u = std::max(0.0f, animation.currentTime - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]);
|
||||||
if (u <= 1.0f) {
|
if (u <= 1.0f)
|
||||||
if (channel.path == "translation") {
|
{
|
||||||
|
if (channel.path == "translation")
|
||||||
|
{
|
||||||
glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
|
glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
|
||||||
channel.node->translation = glm::vec3(trans);
|
channel.node->translation = glm::vec3(trans);
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
if (channel.path == "rotation") {
|
if (channel.path == "rotation")
|
||||||
|
{
|
||||||
glm::quat q1;
|
glm::quat q1;
|
||||||
q1.x = sampler.outputsVec4[i].x;
|
q1.x = sampler.outputsVec4[i].x;
|
||||||
q1.y = sampler.outputsVec4[i].y;
|
q1.y = sampler.outputsVec4[i].y;
|
||||||
|
|
@ -499,7 +566,8 @@ void VulkanglTFModel::updateAnimation(float deltaTime)
|
||||||
channel.node->rotation = glm::normalize(glm::slerp(q1, q2, u));
|
channel.node->rotation = glm::normalize(glm::slerp(q1, q2, u));
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
if (channel.path == "scale") {
|
if (channel.path == "scale")
|
||||||
|
{
|
||||||
glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
|
glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
|
||||||
channel.node->scale = glm::vec3(trans);
|
channel.node->scale = glm::vec3(trans);
|
||||||
updated = true;
|
updated = true;
|
||||||
|
|
@ -508,8 +576,10 @@ void VulkanglTFModel::updateAnimation(float deltaTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (updated) {
|
if (updated)
|
||||||
for (auto& node : nodes) {
|
{
|
||||||
|
for (auto &node : nodes)
|
||||||
|
{
|
||||||
updateJoints(node);
|
updateJoints(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -522,12 +592,14 @@ void VulkanglTFModel::updateAnimation(float deltaTime)
|
||||||
// Draw a single node including child nodes (if present)
|
// Draw a single node including child nodes (if present)
|
||||||
void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node node)
|
void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node node)
|
||||||
{
|
{
|
||||||
if (node.mesh.primitives.size() > 0) {
|
if (node.mesh.primitives.size() > 0)
|
||||||
|
{
|
||||||
// Pass the node's matrix via push constanst
|
// Pass the node's matrix via push constanst
|
||||||
// Traverse the node hierarchy to the top-most parent to get the final matrix of the current node
|
// Traverse the node hierarchy to the top-most parent to get the final matrix of the current node
|
||||||
glm::mat4 nodeMatrix = node.matrix;
|
glm::mat4 nodeMatrix = node.matrix;
|
||||||
VulkanglTFModel::Node *currentParent = node.parent;
|
VulkanglTFModel::Node *currentParent = node.parent;
|
||||||
while (currentParent) {
|
while (currentParent)
|
||||||
|
{
|
||||||
nodeMatrix = currentParent->matrix * nodeMatrix;
|
nodeMatrix = currentParent->matrix * nodeMatrix;
|
||||||
currentParent = currentParent->parent;
|
currentParent = currentParent->parent;
|
||||||
}
|
}
|
||||||
|
|
@ -535,8 +607,10 @@ void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout p
|
||||||
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &nodeMatrix);
|
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &nodeMatrix);
|
||||||
// Bind SSBO with skin data for this node to set 1
|
// Bind SSBO with skin data for this node to set 1
|
||||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &skins[node.skin].descriptorSet, 0, nullptr);
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &skins[node.skin].descriptorSet, 0, nullptr);
|
||||||
for (VulkanglTFModel::Primitive& primitive : node.mesh.primitives) {
|
for (VulkanglTFModel::Primitive &primitive : node.mesh.primitives)
|
||||||
if (primitive.indexCount > 0) {
|
{
|
||||||
|
if (primitive.indexCount > 0)
|
||||||
|
{
|
||||||
// Get the texture index for this primitive
|
// Get the texture index for this primitive
|
||||||
VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
|
VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
|
||||||
// Bind the descriptor for the current primitive's texture to set 2
|
// Bind the descriptor for the current primitive's texture to set 2
|
||||||
|
|
@ -545,7 +619,8 @@ void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto& child : node.children) {
|
for (auto &child : node.children)
|
||||||
|
{
|
||||||
drawNode(commandBuffer, pipelineLayout, *child);
|
drawNode(commandBuffer, pipelineLayout, *child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -558,19 +633,20 @@ void VulkanglTFModel::draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipel
|
||||||
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
|
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
|
||||||
vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
||||||
// Render all nodes at top-level
|
// Render all nodes at top-level
|
||||||
for (auto& node : nodes) {
|
for (auto &node : nodes)
|
||||||
|
{
|
||||||
drawNode(commandBuffer, pipelineLayout, *node);
|
drawNode(commandBuffer, pipelineLayout, *node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Vulkan Example class
|
Vulkan Example class
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
VulkanExample::VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
|
VulkanExample::VulkanExample() :
|
||||||
|
VulkanExampleBase(ENABLE_VALIDATION)
|
||||||
{
|
{
|
||||||
title = "glTF vertex skinning";
|
title = "glTF vertex skinning";
|
||||||
camera.type = Camera::CameraType::lookat;
|
camera.type = Camera::CameraType::lookat;
|
||||||
|
|
@ -584,7 +660,8 @@ VulkanExample::VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION)
|
||||||
VulkanExample::~VulkanExample()
|
VulkanExample::~VulkanExample()
|
||||||
{
|
{
|
||||||
vkDestroyPipeline(device, pipelines.solid, nullptr);
|
vkDestroyPipeline(device, pipelines.solid, nullptr);
|
||||||
if (pipelines.wireframe != VK_NULL_HANDLE) {
|
if (pipelines.wireframe != VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
vkDestroyPipeline(device, pipelines.wireframe, nullptr);
|
vkDestroyPipeline(device, pipelines.wireframe, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -598,7 +675,8 @@ VulkanExample::~VulkanExample()
|
||||||
void VulkanExample::getEnabledFeatures()
|
void VulkanExample::getEnabledFeatures()
|
||||||
{
|
{
|
||||||
// Fill mode non solid is required for wireframe display
|
// Fill mode non solid is required for wireframe display
|
||||||
if (deviceFeatures.fillModeNonSolid) {
|
if (deviceFeatures.fillModeNonSolid)
|
||||||
|
{
|
||||||
enabledFeatures.fillModeNonSolid = VK_TRUE;
|
enabledFeatures.fillModeNonSolid = VK_TRUE;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -608,7 +686,8 @@ void VulkanExample::buildCommandBuffers()
|
||||||
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
|
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
|
||||||
|
|
||||||
VkClearValue clearValues[2];
|
VkClearValue clearValues[2];
|
||||||
clearValues[0].color = { { 0.25f, 0.25f, 0.25f, 1.0f } };;
|
clearValues[0].color = {{0.25f, 0.25f, 0.25f, 1.0f}};
|
||||||
|
;
|
||||||
clearValues[1].depthStencil = {1.0f, 0};
|
clearValues[1].depthStencil = {1.0f, 0};
|
||||||
|
|
||||||
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
|
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
|
||||||
|
|
@ -662,23 +741,27 @@ void VulkanExample::loadglTFFile(std::string filename)
|
||||||
std::vector<uint32_t> indexBuffer;
|
std::vector<uint32_t> indexBuffer;
|
||||||
std::vector<VulkanglTFModel::Vertex> vertexBuffer;
|
std::vector<VulkanglTFModel::Vertex> vertexBuffer;
|
||||||
|
|
||||||
if (fileLoaded) {
|
if (fileLoaded)
|
||||||
|
{
|
||||||
glTFModel.loadImages(glTFInput);
|
glTFModel.loadImages(glTFInput);
|
||||||
glTFModel.loadMaterials(glTFInput);
|
glTFModel.loadMaterials(glTFInput);
|
||||||
glTFModel.loadTextures(glTFInput);
|
glTFModel.loadTextures(glTFInput);
|
||||||
const tinygltf::Scene &scene = glTFInput.scenes[0];
|
const tinygltf::Scene &scene = glTFInput.scenes[0];
|
||||||
for (size_t i = 0; i < scene.nodes.size(); i++) {
|
for (size_t i = 0; i < scene.nodes.size(); i++)
|
||||||
|
{
|
||||||
const tinygltf::Node node = glTFInput.nodes[scene.nodes[i]];
|
const tinygltf::Node node = glTFInput.nodes[scene.nodes[i]];
|
||||||
glTFModel.loadNode(node, glTFInput, nullptr, scene.nodes[i], indexBuffer, vertexBuffer);
|
glTFModel.loadNode(node, glTFInput, nullptr, scene.nodes[i], indexBuffer, vertexBuffer);
|
||||||
}
|
}
|
||||||
glTFModel.loadSkins(glTFInput);
|
glTFModel.loadSkins(glTFInput);
|
||||||
glTFModel.loadAnimations(glTFInput);
|
glTFModel.loadAnimations(glTFInput);
|
||||||
// Calculate initial pose
|
// Calculate initial pose
|
||||||
for (auto node : glTFModel.nodes) {
|
for (auto node : glTFModel.nodes)
|
||||||
|
{
|
||||||
glTFModel.updateJoints(node);
|
glTFModel.updateJoints(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
vks::tools::exitFatal("Could not open the glTF file.\n\nThe file is part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1);
|
vks::tools::exitFatal("Could not open the glTF file.\n\nThe file is part of the additional asset pack.\n\nRun \"download_assets.py\" in the repository root to download the latest version.", -1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -688,7 +771,8 @@ void VulkanExample::loadglTFFile(std::string filename)
|
||||||
size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t);
|
size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t);
|
||||||
glTFModel.indices.count = static_cast<uint32_t>(indexBuffer.size());
|
glTFModel.indices.count = static_cast<uint32_t>(indexBuffer.size());
|
||||||
|
|
||||||
struct StagingBuffer {
|
struct StagingBuffer
|
||||||
|
{
|
||||||
VkBuffer buffer;
|
VkBuffer buffer;
|
||||||
VkDeviceMemory memory;
|
VkDeviceMemory memory;
|
||||||
} vertexStaging, indexStaging;
|
} vertexStaging, indexStaging;
|
||||||
|
|
@ -781,8 +865,7 @@ void VulkanExample::setupDescriptors()
|
||||||
std::array<VkDescriptorSetLayout, 3> setLayouts = {
|
std::array<VkDescriptorSetLayout, 3> setLayouts = {
|
||||||
descriptorSetLayouts.matrices,
|
descriptorSetLayouts.matrices,
|
||||||
descriptorSetLayouts.jointMatrices,
|
descriptorSetLayouts.jointMatrices,
|
||||||
descriptorSetLayouts.textures
|
descriptorSetLayouts.textures};
|
||||||
};
|
|
||||||
VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), static_cast<uint32_t>(setLayouts.size()));
|
VkPipelineLayoutCreateInfo pipelineLayoutCI = vks::initializers::pipelineLayoutCreateInfo(setLayouts.data(), static_cast<uint32_t>(setLayouts.size()));
|
||||||
|
|
||||||
// We will use push constants to push the local matrices of a primitive to the vertex shader
|
// We will use push constants to push the local matrices of a primitive to the vertex shader
|
||||||
|
|
@ -799,7 +882,8 @@ void VulkanExample::setupDescriptors()
|
||||||
vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr);
|
vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr);
|
||||||
|
|
||||||
// Descriptor set for glTF model skin joint matrices
|
// Descriptor set for glTF model skin joint matrices
|
||||||
for (auto& skin : glTFModel.skins) {
|
for (auto &skin : glTFModel.skins)
|
||||||
|
{
|
||||||
const VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.jointMatrices, 1);
|
const VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.jointMatrices, 1);
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &skin.descriptorSet));
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &skin.descriptorSet));
|
||||||
VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(skin.descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, &skin.ssbo.descriptor);
|
VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(skin.descriptorSet, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0, &skin.ssbo.descriptor);
|
||||||
|
|
@ -807,7 +891,8 @@ void VulkanExample::setupDescriptors()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Descriptor sets for glTF model materials
|
// Descriptor sets for glTF model materials
|
||||||
for (auto& image : glTFModel.images) {
|
for (auto &image : glTFModel.images)
|
||||||
|
{
|
||||||
const VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.textures, 1);
|
const VkDescriptorSetAllocateInfo allocInfo = vks::initializers::descriptorSetAllocateInfo(descriptorPool, &descriptorSetLayouts.textures, 1);
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &image.descriptorSet));
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &image.descriptorSet));
|
||||||
VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(image.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &image.texture.descriptor);
|
VkWriteDescriptorSet writeDescriptorSet = vks::initializers::writeDescriptorSet(image.descriptorSet, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0, &image.texture.descriptor);
|
||||||
|
|
@ -848,8 +933,7 @@ void VulkanExample::preparePipelines()
|
||||||
|
|
||||||
const std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages = {
|
const std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages = {
|
||||||
loadShader(getShadersPath() + "gltfskinning/skinnedmodel.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
|
loadShader(getShadersPath() + "gltfskinning/skinnedmodel.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
|
||||||
loadShader(getShadersPath() + "gltfskinning/skinnedmodel.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)
|
loadShader(getShadersPath() + "gltfskinning/skinnedmodel.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)};
|
||||||
};
|
|
||||||
|
|
||||||
VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
|
VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayout, renderPass, 0);
|
||||||
pipelineCI.pVertexInputState = &vertexInputStateCI;
|
pipelineCI.pVertexInputState = &vertexInputStateCI;
|
||||||
|
|
@ -867,7 +951,8 @@ void VulkanExample::preparePipelines()
|
||||||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.solid));
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.solid));
|
||||||
|
|
||||||
// Wire frame rendering pipeline
|
// Wire frame rendering pipeline
|
||||||
if (deviceFeatures.fillModeNonSolid) {
|
if (deviceFeatures.fillModeNonSolid)
|
||||||
|
{
|
||||||
rasterizationStateCI.polygonMode = VK_POLYGON_MODE_LINE;
|
rasterizationStateCI.polygonMode = VK_POLYGON_MODE_LINE;
|
||||||
rasterizationStateCI.lineWidth = 1.0f;
|
rasterizationStateCI.lineWidth = 1.0f;
|
||||||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.wireframe));
|
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.wireframe));
|
||||||
|
|
@ -907,19 +992,23 @@ void VulkanExample::prepare()
|
||||||
void VulkanExample::render()
|
void VulkanExample::render()
|
||||||
{
|
{
|
||||||
renderFrame();
|
renderFrame();
|
||||||
if (camera.updated) {
|
if (camera.updated)
|
||||||
|
{
|
||||||
updateUniformBuffers();
|
updateUniformBuffers();
|
||||||
}
|
}
|
||||||
// POI: Advance animation
|
// POI: Advance animation
|
||||||
if (!paused) {
|
if (!paused)
|
||||||
|
{
|
||||||
glTFModel.updateAnimation(frameTimer);
|
glTFModel.updateAnimation(frameTimer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanExample::OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
void VulkanExample::OnUpdateUIOverlay(vks::UIOverlay *overlay)
|
||||||
{
|
{
|
||||||
if (overlay->header("Settings")) {
|
if (overlay->header("Settings"))
|
||||||
if (overlay->checkBox("Wireframe", &wireframe)) {
|
{
|
||||||
|
if (overlay->checkBox("Wireframe", &wireframe))
|
||||||
|
{
|
||||||
buildCommandBuffers();
|
buildCommandBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@
|
||||||
* If you are looking for a complete glTF implementation, check out https://github.com/SaschaWillems/Vulkan-glTF-PBR/
|
* If you are looking for a complete glTF implementation, check out https://github.com/SaschaWillems/Vulkan-glTF-PBR/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define GLM_FORCE_RADIANS
|
#define GLM_FORCE_RADIANS
|
||||||
|
|
@ -35,9 +35,9 @@
|
||||||
#endif
|
#endif
|
||||||
#include "tiny_gltf.h"
|
#include "tiny_gltf.h"
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
|
||||||
#include "vulkanexamplebase.h"
|
|
||||||
#include "VulkanTexture.hpp"
|
#include "VulkanTexture.hpp"
|
||||||
|
#include "vulkanexamplebase.h"
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
#define ENABLE_VALIDATION false
|
#define ENABLE_VALIDATION false
|
||||||
|
|
||||||
|
|
@ -53,12 +53,14 @@ public:
|
||||||
Base glTF structures, see gltfscene sample for details
|
Base glTF structures, see gltfscene sample for details
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct Vertices {
|
struct Vertices
|
||||||
|
{
|
||||||
VkBuffer buffer;
|
VkBuffer buffer;
|
||||||
VkDeviceMemory memory;
|
VkDeviceMemory memory;
|
||||||
} vertices;
|
} vertices;
|
||||||
|
|
||||||
struct Indices {
|
struct Indices
|
||||||
|
{
|
||||||
int count;
|
int count;
|
||||||
VkBuffer buffer;
|
VkBuffer buffer;
|
||||||
VkDeviceMemory memory;
|
VkDeviceMemory memory;
|
||||||
|
|
@ -66,54 +68,56 @@ public:
|
||||||
|
|
||||||
struct Node;
|
struct Node;
|
||||||
|
|
||||||
struct Material {
|
struct Material
|
||||||
|
{
|
||||||
glm::vec4 baseColorFactor = glm::vec4(1.0f);
|
glm::vec4 baseColorFactor = glm::vec4(1.0f);
|
||||||
uint32_t baseColorTextureIndex;
|
uint32_t baseColorTextureIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Image {
|
struct Image
|
||||||
|
{
|
||||||
vks::Texture2D texture;
|
vks::Texture2D texture;
|
||||||
VkDescriptorSet descriptorSet;
|
VkDescriptorSet descriptorSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Texture {
|
struct Texture
|
||||||
|
{
|
||||||
int32_t imageIndex;
|
int32_t imageIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Primitive {
|
struct Primitive
|
||||||
|
{
|
||||||
uint32_t firstIndex;
|
uint32_t firstIndex;
|
||||||
uint32_t indexCount;
|
uint32_t indexCount;
|
||||||
int32_t materialIndex;
|
int32_t materialIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Mesh {
|
struct Mesh
|
||||||
|
{
|
||||||
std::vector<Primitive> primitives;
|
std::vector<Primitive> primitives;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Node {
|
struct Node
|
||||||
|
{
|
||||||
Node * parent;
|
Node * parent;
|
||||||
uint32_t index;
|
uint32_t index;
|
||||||
std::vector<Node *> children;
|
std::vector<Node *> children;
|
||||||
Mesh mesh;
|
Mesh mesh;
|
||||||
// Matrix components are stored separately as they are affected by animations
|
|
||||||
glm::vec3 translation{};
|
glm::vec3 translation{};
|
||||||
glm::vec3 scale{1.0f};
|
glm::vec3 scale{1.0f};
|
||||||
glm::quat rotation{};
|
glm::quat rotation{};
|
||||||
// Index of the skin for this node
|
|
||||||
int32_t skin = -1;
|
int32_t skin = -1;
|
||||||
glm::mat4 matrix;
|
glm::mat4 matrix;
|
||||||
// Gets the current local matrix based on translation, rotation and scale, which can all be affected by animations
|
|
||||||
glm::mat4 getLocalMatrix();
|
glm::mat4 getLocalMatrix();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Vertex {
|
struct Vertex
|
||||||
|
{
|
||||||
glm::vec3 pos;
|
glm::vec3 pos;
|
||||||
glm::vec3 normal;
|
glm::vec3 normal;
|
||||||
glm::vec2 uv;
|
glm::vec2 uv;
|
||||||
glm::vec3 color;
|
glm::vec3 color;
|
||||||
// Contains indices of the joints that effect this vertex
|
|
||||||
glm::vec4 jointIndices;
|
glm::vec4 jointIndices;
|
||||||
// Contains the weights that define how strongly this vertex is affected by above joints
|
|
||||||
glm::vec4 jointWeights;
|
glm::vec4 jointWeights;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -121,12 +125,12 @@ public:
|
||||||
Skin structure
|
Skin structure
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct Skin {
|
struct Skin
|
||||||
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
Node * skeletonRoot = nullptr;
|
Node * skeletonRoot = nullptr;
|
||||||
std::vector<glm::mat4> inverseBindMatrices;
|
std::vector<glm::mat4> inverseBindMatrices;
|
||||||
std::vector<Node *> joints;
|
std::vector<Node *> joints;
|
||||||
// The joint matrices for this skin are stored in an shader storage buffer
|
|
||||||
vks::Buffer ssbo;
|
vks::Buffer ssbo;
|
||||||
VkDescriptorSet descriptorSet;
|
VkDescriptorSet descriptorSet;
|
||||||
};
|
};
|
||||||
|
|
@ -135,19 +139,22 @@ public:
|
||||||
Animation related structures
|
Animation related structures
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct AnimationSampler {
|
struct AnimationSampler
|
||||||
|
{
|
||||||
std::string interpolation;
|
std::string interpolation;
|
||||||
std::vector<float> inputs;
|
std::vector<float> inputs;
|
||||||
std::vector<glm::vec4> outputsVec4;
|
std::vector<glm::vec4> outputsVec4;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AnimationChannel {
|
struct AnimationChannel
|
||||||
|
{
|
||||||
std::string path;
|
std::string path;
|
||||||
Node * node;
|
Node * node;
|
||||||
uint32_t samplerIndex;
|
uint32_t samplerIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Animation {
|
struct Animation
|
||||||
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<AnimationSampler> samplers;
|
std::vector<AnimationSampler> samplers;
|
||||||
std::vector<AnimationChannel> channels;
|
std::vector<AnimationChannel> channels;
|
||||||
|
|
@ -186,9 +193,11 @@ class VulkanExample : public VulkanExampleBase
|
||||||
public:
|
public:
|
||||||
bool wireframe = false;
|
bool wireframe = false;
|
||||||
|
|
||||||
struct ShaderData {
|
struct ShaderData
|
||||||
|
{
|
||||||
vks::Buffer buffer;
|
vks::Buffer buffer;
|
||||||
struct Values {
|
struct Values
|
||||||
|
{
|
||||||
glm::mat4 projection;
|
glm::mat4 projection;
|
||||||
glm::mat4 model;
|
glm::mat4 model;
|
||||||
glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, 5.0f, 1.0f);
|
glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, 5.0f, 1.0f);
|
||||||
|
|
@ -196,12 +205,14 @@ public:
|
||||||
} shaderData;
|
} shaderData;
|
||||||
|
|
||||||
VkPipelineLayout pipelineLayout;
|
VkPipelineLayout pipelineLayout;
|
||||||
struct Pipelines {
|
struct Pipelines
|
||||||
|
{
|
||||||
VkPipeline solid;
|
VkPipeline solid;
|
||||||
VkPipeline wireframe = VK_NULL_HANDLE;
|
VkPipeline wireframe = VK_NULL_HANDLE;
|
||||||
} pipelines;
|
} pipelines;
|
||||||
|
|
||||||
struct DescriptorSetLayouts {
|
struct DescriptorSetLayouts
|
||||||
|
{
|
||||||
VkDescriptorSetLayout matrices;
|
VkDescriptorSetLayout matrices;
|
||||||
VkDescriptorSetLayout textures;
|
VkDescriptorSetLayout textures;
|
||||||
VkDescriptorSetLayout jointMatrices;
|
VkDescriptorSetLayout jointMatrices;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue