reconstructed cpp file
parent
4a5cd74dc3
commit
46b3d24dd7
|
@ -30,7 +30,7 @@ function(buildHomework HOMEWORK_NAME)
|
||||||
# Add optional readme / tutorial
|
# Add optional readme / tutorial
|
||||||
file(GLOB README_FILES "${HOMEWORK_FOLDER}/*.md")
|
file(GLOB README_FILES "${HOMEWORK_FOLDER}/*.md")
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_executable(${HOMEWORK_NAME} WIN32 ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES} "render/glTFModel.h")
|
add_executable(${HOMEWORK_NAME} WIN32 ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES} "render/glTFModel.h" "render/glTFModel.cpp")
|
||||||
target_link_libraries(${HOMEWORK_NAME} base ${Vulkan_LIBRARY} ${WINLIBS})
|
target_link_libraries(${HOMEWORK_NAME} base ${Vulkan_LIBRARY} ${WINLIBS})
|
||||||
else(WIN32)
|
else(WIN32)
|
||||||
add_executable(${HOMEWORK_NAME} ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES})
|
add_executable(${HOMEWORK_NAME} ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES})
|
||||||
|
|
|
@ -0,0 +1,494 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include "glTFModel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
glTF loading functions
|
||||||
|
|
||||||
|
The following functions take a glTF input model loaded via tinyglTF and convert all required data into our own structure
|
||||||
|
*/
|
||||||
|
|
||||||
|
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
|
||||||
|
// loading them from disk, we fetch them from the glTF loader and upload the buffers
|
||||||
|
images.resize(input.images.size());
|
||||||
|
for (size_t i = 0; i < input.images.size(); i++) {
|
||||||
|
tinygltf::Image& glTFImage = input.images[i];
|
||||||
|
// Get the image data from the glTF loader
|
||||||
|
unsigned char* buffer = nullptr;
|
||||||
|
VkDeviceSize bufferSize = 0;
|
||||||
|
bool deleteBuffer = false;
|
||||||
|
// We convert RGB-only images to RGBA, as most devices don't support RGB-formats in Vulkan
|
||||||
|
if (glTFImage.component == 3) {
|
||||||
|
bufferSize = glTFImage.width * glTFImage.height * 4;
|
||||||
|
buffer = new unsigned char[bufferSize];
|
||||||
|
unsigned char* rgba = buffer;
|
||||||
|
unsigned char* rgb = &glTFImage.image[0];
|
||||||
|
for (size_t i = 0; i < glTFImage.width * glTFImage.height; ++i) {
|
||||||
|
memcpy(rgba, rgb, sizeof(unsigned char) * 3);
|
||||||
|
rgba += 4;
|
||||||
|
rgb += 3;
|
||||||
|
}
|
||||||
|
deleteBuffer = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer = &glTFImage.image[0];
|
||||||
|
bufferSize = glTFImage.image.size();
|
||||||
|
}
|
||||||
|
// Load texture from image buffer
|
||||||
|
images[i].texture.fromBuffer(buffer, bufferSize, VK_FORMAT_R8G8B8A8_UNORM, glTFImage.width, glTFImage.height, vulkanDevice, copyQueue);
|
||||||
|
if (deleteBuffer) {
|
||||||
|
delete[] buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanglTFModel::loadTextures(tinygltf::Model& input)
|
||||||
|
{
|
||||||
|
textures.resize(input.textures.size());
|
||||||
|
for (size_t i = 0; i < input.textures.size(); i++) {
|
||||||
|
textures[i].imageIndex = input.textures[i].source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
|
||||||
|
{
|
||||||
|
animations.resize(input.animations.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < input.animations.size(); ++i)
|
||||||
|
{
|
||||||
|
auto glTFAnimation = input.animations[i];
|
||||||
|
animations[i].name = glTFAnimation.name;
|
||||||
|
|
||||||
|
//Samplers
|
||||||
|
animations[i].samplers.resize(glTFAnimation.samplers.size());
|
||||||
|
for (size_t j = 0; j < glTFAnimation.samplers.size(); ++j)
|
||||||
|
{
|
||||||
|
auto glTFSampler = glTFAnimation.samplers[j];
|
||||||
|
auto& dstSampler = animations[i].samplers[j];
|
||||||
|
dstSampler.interpolation = glTFSampler.interpolation;
|
||||||
|
|
||||||
|
// Read sampler keyframe input time values
|
||||||
|
{
|
||||||
|
const auto& accessor = input.accessors[glTFSampler.input];
|
||||||
|
const auto& bufferView = input.bufferViews[accessor.bufferView];
|
||||||
|
const auto& buffer = input.buffers[bufferView.buffer];
|
||||||
|
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
||||||
|
const float* buf = static_cast<const float*>(dataPtr);
|
||||||
|
for (size_t index = 0; index < accessor.count; ++index)
|
||||||
|
{
|
||||||
|
dstSampler.inputs.push_back(buf[index]);
|
||||||
|
}
|
||||||
|
// Adjust animation's start and end times
|
||||||
|
for (auto input : animations[i].samplers[j].inputs)
|
||||||
|
{
|
||||||
|
if (input < animations[i].start)
|
||||||
|
{
|
||||||
|
animations[i].start = input;
|
||||||
|
};
|
||||||
|
if (input > animations[i].end)
|
||||||
|
{
|
||||||
|
animations[i].end = input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read sampler keyframe output translate/rotate/scale values
|
||||||
|
{
|
||||||
|
const auto& accessor = input.accessors[glTFSampler.output];
|
||||||
|
const auto& bufferView = input.bufferViews[accessor.bufferView];
|
||||||
|
const auto& buffer = input.buffers[bufferView.buffer];
|
||||||
|
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
||||||
|
switch (accessor.type)
|
||||||
|
{
|
||||||
|
case TINYGLTF_TYPE_VEC3:
|
||||||
|
{
|
||||||
|
const glm::vec3* buf = static_cast<const glm::vec3*>(dataPtr);
|
||||||
|
for (size_t index = 0; index < accessor.count; index++)
|
||||||
|
{
|
||||||
|
dstSampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TINYGLTF_TYPE_VEC4:
|
||||||
|
{
|
||||||
|
const glm::vec4* buf = static_cast<const glm::vec4*>(dataPtr);
|
||||||
|
for (size_t index = 0; index < accessor.count; index++)
|
||||||
|
{
|
||||||
|
dstSampler.outputsVec4.push_back(buf[index]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
std::cout << "unknown type" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animations[i].channels.resize(glTFAnimation.channels.size());
|
||||||
|
for (size_t j = 0; j < glTFAnimation.channels.size(); ++j)
|
||||||
|
{
|
||||||
|
auto glTFChannel = glTFAnimation.channels[j];
|
||||||
|
auto& dstChannel = animations[i].channels[j];
|
||||||
|
dstChannel.path = glTFChannel.target_path;
|
||||||
|
dstChannel.samplerIndex = glTFChannel.sampler;
|
||||||
|
dstChannel.node = nodeFromIndex(glTFChannel.target_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanglTFModel::loadMaterials(tinygltf::Model& input)
|
||||||
|
{
|
||||||
|
materials.resize(input.materials.size());
|
||||||
|
for (size_t i = 0; i < input.materials.size(); i++) {
|
||||||
|
// We only read the most basic properties required for our sample
|
||||||
|
tinygltf::Material glTFMaterial = input.materials[i];
|
||||||
|
// Get the base color factor
|
||||||
|
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end()) {
|
||||||
|
materials[i].baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
|
||||||
|
}
|
||||||
|
// Get base color texture index
|
||||||
|
if (glTFMaterial.values.find("baseColorTexture") != glTFMaterial.values.end()) {
|
||||||
|
materials[i].baseColorTextureIndex = glTFMaterial.values["baseColorTexture"].TextureIndex();
|
||||||
|
}
|
||||||
|
if (glTFMaterial.values.find("metallicRoughnessTexture") != glTFMaterial.values.end()) {
|
||||||
|
materials[i].matalicRoughTextureIndex = glTFMaterial.values["metallicRoughnessTexture"].TextureIndex();
|
||||||
|
}
|
||||||
|
if (glTFMaterial.additionalValues.find("normalTexture") != glTFMaterial.additionalValues.end())
|
||||||
|
{
|
||||||
|
materials[i].normalMapTextureIndex = glTFMaterial.additionalValues["normalTexture"].TextureIndex();
|
||||||
|
}
|
||||||
|
if (glTFMaterial.emissiveTexture.index != -1)
|
||||||
|
{
|
||||||
|
materials[i].emissiveTextureIndex = glTFMaterial.emissiveTexture.index;
|
||||||
|
}
|
||||||
|
if (glTFMaterial.emissiveFactor.size() == 3)
|
||||||
|
{
|
||||||
|
materials[i].materialData.values.emissiveFactor = glm::make_vec3(glTFMaterial.emissiveFactor.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end())
|
||||||
|
{
|
||||||
|
materials[i].materialData.values.baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFModel::Node* parent, uint32_t nodeIndex, std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFModel::Vertex>& vertexBuffer)
|
||||||
|
{
|
||||||
|
VulkanglTFModel::Node* node = new VulkanglTFModel::Node{};
|
||||||
|
node->matrix = glm::mat4(1.0f);
|
||||||
|
node->parent = parent;
|
||||||
|
node->index = nodeIndex;
|
||||||
|
|
||||||
|
// Get the local node matrix
|
||||||
|
// It's either made up from translation, rotation, scale or a 4x4 matrix
|
||||||
|
if (inputNode.translation.size() == 3) {
|
||||||
|
node->matrix = glm::translate(node->matrix, glm::vec3(glm::make_vec3(inputNode.translation.data())));
|
||||||
|
}
|
||||||
|
if (inputNode.rotation.size() == 4) {
|
||||||
|
glm::quat q = glm::make_quat(inputNode.rotation.data());
|
||||||
|
node->matrix *= glm::mat4(q);
|
||||||
|
}
|
||||||
|
if (inputNode.scale.size() == 3) {
|
||||||
|
node->matrix = glm::scale(node->matrix, glm::vec3(glm::make_vec3(inputNode.scale.data())));
|
||||||
|
}
|
||||||
|
if (inputNode.matrix.size() == 16) {
|
||||||
|
node->matrix = glm::make_mat4x4(inputNode.matrix.data());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load node's children
|
||||||
|
if (inputNode.children.size() > 0) {
|
||||||
|
for (size_t i = 0; i < inputNode.children.size(); i++) {
|
||||||
|
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 buffers
|
||||||
|
// In glTF this is done via accessors and buffer views
|
||||||
|
if (inputNode.mesh > -1) {
|
||||||
|
const tinygltf::Mesh mesh = input.meshes[inputNode.mesh];
|
||||||
|
// Iterate through all primitives of this node's mesh
|
||||||
|
for (size_t i = 0; i < mesh.primitives.size(); i++) {
|
||||||
|
const tinygltf::Primitive& glTFPrimitive = mesh.primitives[i];
|
||||||
|
uint32_t firstIndex = static_cast<uint32_t>(indexBuffer.size());
|
||||||
|
uint32_t vertexStart = static_cast<uint32_t>(vertexBuffer.size());
|
||||||
|
uint32_t indexCount = 0;
|
||||||
|
// Vertices
|
||||||
|
{
|
||||||
|
const float* positionBuffer = nullptr;
|
||||||
|
const float* normalsBuffer = nullptr;
|
||||||
|
const float* texCoordsBuffer = nullptr;
|
||||||
|
const float* tangentsBuffer = nullptr;
|
||||||
|
size_t vertexCount = 0;
|
||||||
|
|
||||||
|
// Get buffer data for vertex positions
|
||||||
|
if (glTFPrimitive.attributes.find("POSITION") != glTFPrimitive.attributes.end()) {
|
||||||
|
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("POSITION")->second];
|
||||||
|
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
||||||
|
positionBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||||||
|
vertexCount = accessor.count;
|
||||||
|
}
|
||||||
|
// Get buffer data for vertex normals
|
||||||
|
if (glTFPrimitive.attributes.find("NORMAL") != glTFPrimitive.attributes.end()) {
|
||||||
|
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("NORMAL")->second];
|
||||||
|
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
||||||
|
normalsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||||||
|
}
|
||||||
|
// Get buffer data for vertex texture coordinates
|
||||||
|
// glTF supports multiple sets, we only load the first one
|
||||||
|
if (glTFPrimitive.attributes.find("TEXCOORD_0") != glTFPrimitive.attributes.end()) {
|
||||||
|
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TEXCOORD_0")->second];
|
||||||
|
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
||||||
|
texCoordsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glTFPrimitive.attributes.find("TANGENT") != glTFPrimitive.attributes.end())
|
||||||
|
{
|
||||||
|
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TANGENT")->second];
|
||||||
|
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
||||||
|
tangentsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append data to model's vertex buffer
|
||||||
|
for (size_t v = 0; v < vertexCount; v++) {
|
||||||
|
Vertex vert{};
|
||||||
|
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.uv = texCoordsBuffer ? glm::make_vec2(&texCoordsBuffer[v * 2]) : glm::vec3(0.0f);
|
||||||
|
vert.tangent = tangentsBuffer ? glm::normalize(glm::make_vec3(&tangentsBuffer[v * 4])) : glm::vec3(0.0f);
|
||||||
|
vert.color = glm::vec3(1.0f, 1.0f, nodeIndex);//Temp set index in color attribute
|
||||||
|
vertexBuffer.push_back(vert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Indices
|
||||||
|
{
|
||||||
|
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.indices];
|
||||||
|
const tinygltf::BufferView& bufferView = input.bufferViews[accessor.bufferView];
|
||||||
|
const tinygltf::Buffer& buffer = input.buffers[bufferView.buffer];
|
||||||
|
|
||||||
|
indexCount += static_cast<uint32_t>(accessor.count);
|
||||||
|
|
||||||
|
// glTF supports different component types of indices
|
||||||
|
switch (accessor.componentType) {
|
||||||
|
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: {
|
||||||
|
const uint32_t* buf = reinterpret_cast<const uint32_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
|
||||||
|
for (size_t index = 0; index < accessor.count; index++) {
|
||||||
|
indexBuffer.push_back(buf[index] + vertexStart);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: {
|
||||||
|
const uint16_t* buf = reinterpret_cast<const uint16_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
|
||||||
|
for (size_t index = 0; index < accessor.count; index++) {
|
||||||
|
indexBuffer.push_back(buf[index] + vertexStart);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
|
||||||
|
const uint8_t* buf = reinterpret_cast<const uint8_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
|
||||||
|
for (size_t index = 0; index < accessor.count; index++) {
|
||||||
|
indexBuffer.push_back(buf[index] + vertexStart);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
std::cerr << "Index component type " << accessor.componentType << " not supported!" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Primitive primitive{};
|
||||||
|
primitive.firstIndex = firstIndex;
|
||||||
|
primitive.indexCount = indexCount;
|
||||||
|
primitive.materialIndex = glTFPrimitive.material;
|
||||||
|
node->mesh.primitives.push_back(primitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
parent->children.push_back(node);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nodes.push_back(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)
|
||||||
|
{
|
||||||
|
Node* nodeFound = nullptr;
|
||||||
|
if (parent->index == index)
|
||||||
|
{
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
for (auto& child : parent->children)
|
||||||
|
{
|
||||||
|
nodeFound = findNode(child, index);
|
||||||
|
if (nodeFound)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodeFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanglTFModel::Node* VulkanglTFModel::nodeFromIndex(uint32_t index)
|
||||||
|
{
|
||||||
|
Node* nodeFound = nullptr;
|
||||||
|
for (auto& node : nodes)
|
||||||
|
{
|
||||||
|
nodeFound = findNode(node, index);
|
||||||
|
if (nodeFound)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nodeFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanglTFModel::updateAnimation(float deltaTime, vks::Buffer& buffer)
|
||||||
|
{
|
||||||
|
constexpr uint32_t activeAnimation = 0;
|
||||||
|
Animation& animation = animations[activeAnimation];
|
||||||
|
animation.currentTime += deltaTime;
|
||||||
|
if (animation.currentTime > animation.end)
|
||||||
|
{
|
||||||
|
animation.currentTime -= animation.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& channel : animation.channels)
|
||||||
|
{
|
||||||
|
auto& sampler = animation.samplers[channel.samplerIndex];
|
||||||
|
for (size_t i = 0; i < sampler.inputs.size() - 1; ++i)
|
||||||
|
{
|
||||||
|
if (sampler.interpolation != "LINEAR")
|
||||||
|
{
|
||||||
|
std::cout << "This sample only supports linear interpolations\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((animation.currentTime >= sampler.inputs[i]) && (animation.currentTime <= sampler.inputs[i + 1]))
|
||||||
|
{
|
||||||
|
float ratio = (animation.currentTime - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]);
|
||||||
|
if (channel.path == "translation")
|
||||||
|
{
|
||||||
|
channel.node->translation = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio);
|
||||||
|
channel.node->bAnimateNode = true;
|
||||||
|
}
|
||||||
|
if (channel.path == "rotation")
|
||||||
|
{
|
||||||
|
glm::quat q1;
|
||||||
|
q1.x = sampler.outputsVec4[i].x;
|
||||||
|
q1.y = sampler.outputsVec4[i].y;
|
||||||
|
q1.z = sampler.outputsVec4[i].z;
|
||||||
|
q1.w = sampler.outputsVec4[i].w;
|
||||||
|
|
||||||
|
glm::quat q2;
|
||||||
|
q2.x = sampler.outputsVec4[i + 1].x;
|
||||||
|
q2.y = sampler.outputsVec4[i + 1].y;
|
||||||
|
q2.z = sampler.outputsVec4[i + 1].z;
|
||||||
|
q2.w = sampler.outputsVec4[i + 1].w;
|
||||||
|
|
||||||
|
channel.node->rotation = glm::normalize(glm::slerp(q1, q2, ratio));
|
||||||
|
channel.node->bAnimateNode = true;
|
||||||
|
}
|
||||||
|
if (channel.path == "scale")
|
||||||
|
{
|
||||||
|
channel.node->scale = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio);
|
||||||
|
channel.node->bAnimateNode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<glm::mat4> nodeMatrics(nodeCount);
|
||||||
|
for (auto& node : nodes)
|
||||||
|
{
|
||||||
|
updateNodeMatrix(node, nodeMatrics);
|
||||||
|
}
|
||||||
|
buffer.copyTo(nodeMatrics.data(), nodeCount * sizeof(glm::mat4));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanglTFModel::updateNodeMatrix(Node* node, std::vector<glm::mat4>& nodeMatrics)
|
||||||
|
{
|
||||||
|
nodeMatrics[node->index] = getNodeMatrix(node);
|
||||||
|
for (auto& child : node->children)
|
||||||
|
{
|
||||||
|
updateNodeMatrix(child, nodeMatrics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 VulkanglTFModel::getNodeMatrix(Node* node)
|
||||||
|
{
|
||||||
|
glm::mat4 nodeMatrix = node->getLocalMatrix();
|
||||||
|
Node* currentParent = node->parent;
|
||||||
|
while (currentParent)
|
||||||
|
{
|
||||||
|
nodeMatrix = currentParent->getLocalMatrix() * nodeMatrix;
|
||||||
|
currentParent = currentParent->parent;
|
||||||
|
}
|
||||||
|
return nodeMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
glTF rendering functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Draw a single node including child nodes (if present)
|
||||||
|
void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants)
|
||||||
|
{
|
||||||
|
if (node->mesh.primitives.size() > 0) {
|
||||||
|
// Pass the node's matrix via push constants
|
||||||
|
// Traverse the node hierarchy to the top-most parent to get the final matrix of the current node
|
||||||
|
glm::mat4 nodeMatrix = node->matrix;
|
||||||
|
VulkanglTFModel::Node* currentParent = node->parent;
|
||||||
|
while (currentParent) {
|
||||||
|
nodeMatrix = currentParent->matrix * nodeMatrix;
|
||||||
|
currentParent = currentParent->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (VulkanglTFModel::Primitive& primitive : node->mesh.primitives) {
|
||||||
|
if (primitive.indexCount > 0) {
|
||||||
|
// Get the texture index for this primitive
|
||||||
|
if (textures.size() > 0)
|
||||||
|
{
|
||||||
|
VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
|
||||||
|
auto normalMap = textures[materials[primitive.materialIndex].normalMapTextureIndex];
|
||||||
|
auto roughMetalMap = textures[materials[primitive.materialIndex].matalicRoughTextureIndex];
|
||||||
|
|
||||||
|
if (materials[primitive.materialIndex].emissiveTextureIndex >= 0)
|
||||||
|
{
|
||||||
|
auto emissiveMap = textures[materials[primitive.materialIndex].emissiveTextureIndex];
|
||||||
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 4, 1, &images[emissiveMap.imageIndex].descriptorSet, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind the descriptor for the current primitive's texture
|
||||||
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &images[texture.imageIndex].descriptorSet, 0, nullptr);
|
||||||
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 2, 1, &images[normalMap.imageIndex].descriptorSet, 0, nullptr);
|
||||||
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 3, 1, &images[roughMetalMap.imageIndex].descriptorSet, 0, nullptr);
|
||||||
|
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 5, 1, &materials[primitive.materialIndex].materialData.descriptorSet, 0, nullptr);
|
||||||
|
}
|
||||||
|
vkCmdDrawIndexed(commandBuffer, primitive.indexCount, 1, primitive.firstIndex, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& child : node->children) {
|
||||||
|
drawNode(commandBuffer, pipelineLayout, child, bPushConstants);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the glTF scene starting at the top-level-nodes
|
||||||
|
void VulkanglTFModel::draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag = true)
|
||||||
|
{
|
||||||
|
// All vertices and indices are stored in single buffers, so we only need to bind once
|
||||||
|
VkDeviceSize offsets[1] = { 0 };
|
||||||
|
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
|
||||||
|
vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
||||||
|
// Render all nodes at top-level
|
||||||
|
for (auto& node : nodes) {
|
||||||
|
drawNode(commandBuffer, pipelineLayout, node, flag);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -11,13 +12,13 @@
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
#define TINYGLTF_IMPLEMENTATION
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#define TINYGLTF_NO_STB_IMAGE_WRITE
|
|
||||||
|
|
||||||
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
#ifdef VK_USE_PLATFORM_ANDROID_KHR
|
||||||
#define TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
#define TINYGLTF_ANDROID_LOAD_FROM_ASSETS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include "tiny_gltf.h"
|
#include "tiny_gltf.h"
|
||||||
|
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Vulkan Example - glTF scene loading and rendering
|
* Vulkan Example - glTF scene loading and rendering
|
||||||
*
|
*
|
||||||
|
@ -15,503 +17,27 @@
|
||||||
*
|
*
|
||||||
* 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/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef TINYGLTF_IMPLEMENTATION
|
||||||
|
#define TINYGLTF_IMPLEMENTATION
|
||||||
|
#endif
|
||||||
|
#ifndef STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
|
||||||
|
#define TINYGLTF_NO_STB_IMAGE_WRITE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "glTFModel.h"
|
|
||||||
/*
|
|
||||||
glTF loading functions
|
|
||||||
|
|
||||||
The following functions take a glTF input model loaded via tinyglTF and convert all required data into our own structure
|
|
||||||
*/
|
|
||||||
|
|
||||||
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
|
|
||||||
// loading them from disk, we fetch them from the glTF loader and upload the buffers
|
|
||||||
images.resize(input.images.size());
|
|
||||||
for (size_t i = 0; i < input.images.size(); i++) {
|
|
||||||
tinygltf::Image& glTFImage = input.images[i];
|
|
||||||
// Get the image data from the glTF loader
|
|
||||||
unsigned char* buffer = nullptr;
|
|
||||||
VkDeviceSize bufferSize = 0;
|
|
||||||
bool deleteBuffer = false;
|
|
||||||
// We convert RGB-only images to RGBA, as most devices don't support RGB-formats in Vulkan
|
|
||||||
if (glTFImage.component == 3) {
|
|
||||||
bufferSize = glTFImage.width * glTFImage.height * 4;
|
|
||||||
buffer = new unsigned char[bufferSize];
|
|
||||||
unsigned char* rgba = buffer;
|
|
||||||
unsigned char* rgb = &glTFImage.image[0];
|
|
||||||
for (size_t i = 0; i < glTFImage.width * glTFImage.height; ++i) {
|
|
||||||
memcpy(rgba, rgb, sizeof(unsigned char) * 3);
|
|
||||||
rgba += 4;
|
|
||||||
rgb += 3;
|
|
||||||
}
|
|
||||||
deleteBuffer = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
buffer = &glTFImage.image[0];
|
|
||||||
bufferSize = glTFImage.image.size();
|
|
||||||
}
|
|
||||||
// Load texture from image buffer
|
|
||||||
images[i].texture.fromBuffer(buffer, bufferSize, VK_FORMAT_R8G8B8A8_UNORM, glTFImage.width, glTFImage.height, vulkanDevice, copyQueue);
|
|
||||||
if (deleteBuffer) {
|
|
||||||
delete[] buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanglTFModel::loadTextures(tinygltf::Model& input)
|
|
||||||
{
|
|
||||||
textures.resize(input.textures.size());
|
|
||||||
for (size_t i = 0; i < input.textures.size(); i++) {
|
|
||||||
textures[i].imageIndex = input.textures[i].source;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
|
|
||||||
{
|
|
||||||
animations.resize(input.animations.size());
|
|
||||||
|
|
||||||
for (size_t i = 0; i < input.animations.size(); ++i)
|
|
||||||
{
|
|
||||||
auto glTFAnimation = input.animations[i];
|
|
||||||
animations[i].name = glTFAnimation.name;
|
|
||||||
|
|
||||||
//Samplers
|
|
||||||
animations[i].samplers.resize(glTFAnimation.samplers.size());
|
|
||||||
for (size_t j = 0; j < glTFAnimation.samplers.size(); ++j)
|
|
||||||
{
|
|
||||||
auto glTFSampler = glTFAnimation.samplers[j];
|
|
||||||
auto& dstSampler = animations[i].samplers[j];
|
|
||||||
dstSampler.interpolation = glTFSampler.interpolation;
|
|
||||||
|
|
||||||
// Read sampler keyframe input time values
|
|
||||||
{
|
|
||||||
const auto& accessor = input.accessors[glTFSampler.input];
|
|
||||||
const auto& bufferView = input.bufferViews[accessor.bufferView];
|
|
||||||
const auto& buffer = input.buffers[bufferView.buffer];
|
|
||||||
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
|
||||||
const float* buf = static_cast<const float*>(dataPtr);
|
|
||||||
for (size_t index = 0; index < accessor.count; ++index)
|
|
||||||
{
|
|
||||||
dstSampler.inputs.push_back(buf[index]);
|
|
||||||
}
|
|
||||||
// Adjust animation's start and end times
|
|
||||||
for (auto input : animations[i].samplers[j].inputs)
|
|
||||||
{
|
|
||||||
if (input < animations[i].start)
|
|
||||||
{
|
|
||||||
animations[i].start = input;
|
|
||||||
};
|
|
||||||
if (input > animations[i].end)
|
|
||||||
{
|
|
||||||
animations[i].end = input;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read sampler keyframe output translate/rotate/scale values
|
|
||||||
{
|
|
||||||
const auto& accessor = input.accessors[glTFSampler.output];
|
|
||||||
const auto& bufferView = input.bufferViews[accessor.bufferView];
|
|
||||||
const auto& buffer = input.buffers[bufferView.buffer];
|
|
||||||
const void* dataPtr = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
|
|
||||||
switch (accessor.type)
|
|
||||||
{
|
|
||||||
case TINYGLTF_TYPE_VEC3:
|
|
||||||
{
|
|
||||||
const glm::vec3* buf = static_cast<const glm::vec3*>(dataPtr);
|
|
||||||
for (size_t index = 0; index < accessor.count; index++)
|
|
||||||
{
|
|
||||||
dstSampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TINYGLTF_TYPE_VEC4:
|
|
||||||
{
|
|
||||||
const glm::vec4* buf = static_cast<const glm::vec4*>(dataPtr);
|
|
||||||
for (size_t index = 0; index < accessor.count; index++)
|
|
||||||
{
|
|
||||||
dstSampler.outputsVec4.push_back(buf[index]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
std::cout << "unknown type" << std::endl;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
animations[i].channels.resize(glTFAnimation.channels.size());
|
|
||||||
for (size_t j = 0; j < glTFAnimation.channels.size(); ++j)
|
|
||||||
{
|
|
||||||
auto glTFChannel = glTFAnimation.channels[j];
|
|
||||||
auto& dstChannel = animations[i].channels[j];
|
|
||||||
dstChannel.path = glTFChannel.target_path;
|
|
||||||
dstChannel.samplerIndex = glTFChannel.sampler;
|
|
||||||
dstChannel.node = nodeFromIndex(glTFChannel.target_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanglTFModel::loadMaterials(tinygltf::Model& input)
|
|
||||||
{
|
|
||||||
materials.resize(input.materials.size());
|
|
||||||
for (size_t i = 0; i < input.materials.size(); i++) {
|
|
||||||
// We only read the most basic properties required for our sample
|
|
||||||
tinygltf::Material glTFMaterial = input.materials[i];
|
|
||||||
// Get the base color factor
|
|
||||||
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end()) {
|
|
||||||
materials[i].baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
|
|
||||||
}
|
|
||||||
// Get base color texture index
|
|
||||||
if (glTFMaterial.values.find("baseColorTexture") != glTFMaterial.values.end()) {
|
|
||||||
materials[i].baseColorTextureIndex = glTFMaterial.values["baseColorTexture"].TextureIndex();
|
|
||||||
}
|
|
||||||
if (glTFMaterial.values.find("metallicRoughnessTexture") != glTFMaterial.values.end()) {
|
|
||||||
materials[i].matalicRoughTextureIndex = glTFMaterial.values["metallicRoughnessTexture"].TextureIndex();
|
|
||||||
}
|
|
||||||
if (glTFMaterial.additionalValues.find("normalTexture") != glTFMaterial.additionalValues.end())
|
|
||||||
{
|
|
||||||
materials[i].normalMapTextureIndex = glTFMaterial.additionalValues["normalTexture"].TextureIndex();
|
|
||||||
}
|
|
||||||
if (glTFMaterial.emissiveTexture.index != -1)
|
|
||||||
{
|
|
||||||
materials[i].emissiveTextureIndex = glTFMaterial.emissiveTexture.index;
|
|
||||||
}
|
|
||||||
if (glTFMaterial.emissiveFactor.size() == 3)
|
|
||||||
{
|
|
||||||
materials[i].materialData.values.emissiveFactor = glm::make_vec3(glTFMaterial.emissiveFactor.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (glTFMaterial.values.find("baseColorFactor") != glTFMaterial.values.end())
|
|
||||||
{
|
|
||||||
materials[i].materialData.values.baseColorFactor = glm::make_vec4(glTFMaterial.values["baseColorFactor"].ColorFactor().data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanglTFModel::loadNode(const tinygltf::Node& inputNode, const tinygltf::Model& input, VulkanglTFModel::Node* parent, uint32_t nodeIndex,std::vector<uint32_t>& indexBuffer, std::vector<VulkanglTFModel::Vertex>& vertexBuffer)
|
|
||||||
{
|
|
||||||
VulkanglTFModel::Node* node = new VulkanglTFModel::Node{};
|
|
||||||
node->matrix = glm::mat4(1.0f);
|
|
||||||
node->parent = parent;
|
|
||||||
node->index = nodeIndex;
|
|
||||||
|
|
||||||
// Get the local node matrix
|
|
||||||
// It's either made up from translation, rotation, scale or a 4x4 matrix
|
|
||||||
if (inputNode.translation.size() == 3) {
|
|
||||||
node->matrix = glm::translate(node->matrix, glm::vec3(glm::make_vec3(inputNode.translation.data())));
|
|
||||||
}
|
|
||||||
if (inputNode.rotation.size() == 4) {
|
|
||||||
glm::quat q = glm::make_quat(inputNode.rotation.data());
|
|
||||||
node->matrix *= glm::mat4(q);
|
|
||||||
}
|
|
||||||
if (inputNode.scale.size() == 3) {
|
|
||||||
node->matrix = glm::scale(node->matrix, glm::vec3(glm::make_vec3(inputNode.scale.data())));
|
|
||||||
}
|
|
||||||
if (inputNode.matrix.size() == 16) {
|
|
||||||
node->matrix = glm::make_mat4x4(inputNode.matrix.data());
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load node's children
|
|
||||||
if (inputNode.children.size() > 0) {
|
|
||||||
for (size_t i = 0; i < inputNode.children.size(); i++) {
|
|
||||||
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 buffers
|
|
||||||
// In glTF this is done via accessors and buffer views
|
|
||||||
if (inputNode.mesh > -1) {
|
|
||||||
const tinygltf::Mesh mesh = input.meshes[inputNode.mesh];
|
|
||||||
// Iterate through all primitives of this node's mesh
|
|
||||||
for (size_t i = 0; i < mesh.primitives.size(); i++) {
|
|
||||||
const tinygltf::Primitive& glTFPrimitive = mesh.primitives[i];
|
|
||||||
uint32_t firstIndex = static_cast<uint32_t>(indexBuffer.size());
|
|
||||||
uint32_t vertexStart = static_cast<uint32_t>(vertexBuffer.size());
|
|
||||||
uint32_t indexCount = 0;
|
|
||||||
// Vertices
|
|
||||||
{
|
|
||||||
const float* positionBuffer = nullptr;
|
|
||||||
const float* normalsBuffer = nullptr;
|
|
||||||
const float* texCoordsBuffer = nullptr;
|
|
||||||
const float* tangentsBuffer = nullptr;
|
|
||||||
size_t vertexCount = 0;
|
|
||||||
|
|
||||||
// Get buffer data for vertex positions
|
|
||||||
if (glTFPrimitive.attributes.find("POSITION") != glTFPrimitive.attributes.end()) {
|
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("POSITION")->second];
|
|
||||||
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
|
||||||
positionBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
|
||||||
vertexCount = accessor.count;
|
|
||||||
}
|
|
||||||
// Get buffer data for vertex normals
|
|
||||||
if (glTFPrimitive.attributes.find("NORMAL") != glTFPrimitive.attributes.end()) {
|
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("NORMAL")->second];
|
|
||||||
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
|
||||||
normalsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
|
||||||
}
|
|
||||||
// Get buffer data for vertex texture coordinates
|
|
||||||
// glTF supports multiple sets, we only load the first one
|
|
||||||
if (glTFPrimitive.attributes.find("TEXCOORD_0") != glTFPrimitive.attributes.end()) {
|
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TEXCOORD_0")->second];
|
|
||||||
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
|
||||||
texCoordsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (glTFPrimitive.attributes.find("TANGENT") != glTFPrimitive.attributes.end())
|
|
||||||
{
|
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.attributes.find("TANGENT")->second];
|
|
||||||
const tinygltf::BufferView& view = input.bufferViews[accessor.bufferView];
|
|
||||||
tangentsBuffer = reinterpret_cast<const float*>(&(input.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append data to model's vertex buffer
|
|
||||||
for (size_t v = 0; v < vertexCount; v++) {
|
|
||||||
Vertex vert{};
|
|
||||||
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.uv = texCoordsBuffer ? glm::make_vec2(&texCoordsBuffer[v * 2]) : glm::vec3(0.0f);
|
|
||||||
vert.tangent = tangentsBuffer ? glm::normalize(glm::make_vec3(&tangentsBuffer[v * 4])) : glm::vec3(0.0f);
|
|
||||||
vert.color = glm::vec3(1.0f, 1.0f, nodeIndex);//Temp set index in color attribute
|
|
||||||
vertexBuffer.push_back(vert);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Indices
|
|
||||||
{
|
|
||||||
const tinygltf::Accessor& accessor = input.accessors[glTFPrimitive.indices];
|
|
||||||
const tinygltf::BufferView& bufferView = input.bufferViews[accessor.bufferView];
|
|
||||||
const tinygltf::Buffer& buffer = input.buffers[bufferView.buffer];
|
|
||||||
|
|
||||||
indexCount += static_cast<uint32_t>(accessor.count);
|
|
||||||
|
|
||||||
// glTF supports different component types of indices
|
|
||||||
switch (accessor.componentType) {
|
|
||||||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT: {
|
|
||||||
const uint32_t* buf = reinterpret_cast<const uint32_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
|
|
||||||
for (size_t index = 0; index < accessor.count; index++) {
|
|
||||||
indexBuffer.push_back(buf[index] + vertexStart);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT: {
|
|
||||||
const uint16_t* buf = reinterpret_cast<const uint16_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
|
|
||||||
for (size_t index = 0; index < accessor.count; index++) {
|
|
||||||
indexBuffer.push_back(buf[index] + vertexStart);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE: {
|
|
||||||
const uint8_t* buf = reinterpret_cast<const uint8_t*>(&buffer.data[accessor.byteOffset + bufferView.byteOffset]);
|
|
||||||
for (size_t index = 0; index < accessor.count; index++) {
|
|
||||||
indexBuffer.push_back(buf[index] + vertexStart);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
std::cerr << "Index component type " << accessor.componentType << " not supported!" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Primitive primitive{};
|
|
||||||
primitive.firstIndex = firstIndex;
|
|
||||||
primitive.indexCount = indexCount;
|
|
||||||
primitive.materialIndex = glTFPrimitive.material;
|
|
||||||
node->mesh.primitives.push_back(primitive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent) {
|
|
||||||
parent->children.push_back(node);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nodes.push_back(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)
|
|
||||||
{
|
|
||||||
Node* nodeFound = nullptr;
|
|
||||||
if (parent->index == index)
|
|
||||||
{
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
for (auto& child : parent->children)
|
|
||||||
{
|
|
||||||
nodeFound = findNode(child, index);
|
|
||||||
if (nodeFound)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodeFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
VulkanglTFModel::Node* VulkanglTFModel::nodeFromIndex(uint32_t index)
|
|
||||||
{
|
|
||||||
Node* nodeFound = nullptr;
|
|
||||||
for (auto& node : nodes)
|
|
||||||
{
|
|
||||||
nodeFound = findNode(node, index);
|
|
||||||
if (nodeFound)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodeFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanglTFModel::updateAnimation(float deltaTime, vks::Buffer& buffer)
|
|
||||||
{
|
|
||||||
constexpr uint32_t activeAnimation = 0;
|
|
||||||
Animation& animation = animations[activeAnimation];
|
|
||||||
animation.currentTime += deltaTime;
|
|
||||||
if (animation.currentTime > animation.end)
|
|
||||||
{
|
|
||||||
animation.currentTime -= animation.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& channel : animation.channels)
|
|
||||||
{
|
|
||||||
auto& sampler = animation.samplers[channel.samplerIndex];
|
|
||||||
for (size_t i = 0; i < sampler.inputs.size() - 1; ++i)
|
|
||||||
{
|
|
||||||
if (sampler.interpolation != "LINEAR")
|
|
||||||
{
|
|
||||||
std::cout << "This sample only supports linear interpolations\n";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ((animation.currentTime >= sampler.inputs[i]) && (animation.currentTime <= sampler.inputs[i + 1]))
|
|
||||||
{
|
|
||||||
float ratio = (animation.currentTime - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]);
|
|
||||||
if (channel.path == "translation")
|
|
||||||
{
|
|
||||||
channel.node->translation = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio);
|
|
||||||
channel.node->bAnimateNode = true;
|
|
||||||
}
|
|
||||||
if (channel.path == "rotation")
|
|
||||||
{
|
|
||||||
glm::quat q1;
|
|
||||||
q1.x = sampler.outputsVec4[i].x;
|
|
||||||
q1.y = sampler.outputsVec4[i].y;
|
|
||||||
q1.z = sampler.outputsVec4[i].z;
|
|
||||||
q1.w = sampler.outputsVec4[i].w;
|
|
||||||
|
|
||||||
glm::quat q2;
|
|
||||||
q2.x = sampler.outputsVec4[i + 1].x;
|
|
||||||
q2.y = sampler.outputsVec4[i + 1].y;
|
|
||||||
q2.z = sampler.outputsVec4[i + 1].z;
|
|
||||||
q2.w = sampler.outputsVec4[i + 1].w;
|
|
||||||
|
|
||||||
channel.node->rotation = glm::normalize(glm::slerp(q1, q2, ratio));
|
|
||||||
channel.node->bAnimateNode = true;
|
|
||||||
}
|
|
||||||
if (channel.path == "scale")
|
|
||||||
{
|
|
||||||
channel.node->scale = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio);
|
|
||||||
channel.node->bAnimateNode = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::vector<glm::mat4> nodeMatrics(nodeCount);
|
|
||||||
for (auto& node : nodes)
|
|
||||||
{
|
|
||||||
updateNodeMatrix(node, nodeMatrics);
|
|
||||||
}
|
|
||||||
buffer.copyTo(nodeMatrics.data(), nodeCount * sizeof(glm::mat4));
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanglTFModel::updateNodeMatrix(Node* node, std::vector<glm::mat4>& nodeMatrics)
|
|
||||||
{
|
|
||||||
nodeMatrics[node->index] = getNodeMatrix(node);
|
|
||||||
for (auto& child : node->children)
|
|
||||||
{
|
|
||||||
updateNodeMatrix(child, nodeMatrics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::mat4 VulkanglTFModel::getNodeMatrix(Node* node)
|
|
||||||
{
|
|
||||||
glm::mat4 nodeMatrix = node->getLocalMatrix();
|
|
||||||
Node* currentParent = node->parent;
|
|
||||||
while (currentParent)
|
|
||||||
{
|
|
||||||
nodeMatrix = currentParent->getLocalMatrix() * nodeMatrix;
|
|
||||||
currentParent = currentParent->parent;
|
|
||||||
}
|
|
||||||
return nodeMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
glTF rendering functions
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Draw a single node including child nodes (if present)
|
|
||||||
void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants)
|
|
||||||
{
|
|
||||||
if (node->mesh.primitives.size() > 0) {
|
|
||||||
// Pass the node's matrix via push constants
|
|
||||||
// Traverse the node hierarchy to the top-most parent to get the final matrix of the current node
|
|
||||||
glm::mat4 nodeMatrix = node->matrix;
|
|
||||||
VulkanglTFModel::Node* currentParent = node->parent;
|
|
||||||
while (currentParent) {
|
|
||||||
nodeMatrix = currentParent->matrix * nodeMatrix;
|
|
||||||
currentParent = currentParent->parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (VulkanglTFModel::Primitive& primitive : node->mesh.primitives) {
|
|
||||||
if (primitive.indexCount > 0) {
|
|
||||||
// Get the texture index for this primitive
|
|
||||||
if (textures.size() > 0)
|
|
||||||
{
|
|
||||||
VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
|
|
||||||
auto normalMap = textures[materials[primitive.materialIndex].normalMapTextureIndex];
|
|
||||||
auto roughMetalMap = textures[materials[primitive.materialIndex].matalicRoughTextureIndex];
|
|
||||||
|
|
||||||
if (materials[primitive.materialIndex].emissiveTextureIndex >= 0)
|
|
||||||
{
|
|
||||||
auto emissiveMap = textures[materials[primitive.materialIndex].emissiveTextureIndex];
|
|
||||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 4, 1, &images[emissiveMap.imageIndex].descriptorSet, 0, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind the descriptor for the current primitive's texture
|
|
||||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &images[texture.imageIndex].descriptorSet, 0, nullptr);
|
|
||||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 2, 1, &images[normalMap.imageIndex].descriptorSet, 0, nullptr);
|
|
||||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 3, 1, &images[roughMetalMap.imageIndex].descriptorSet, 0, nullptr);
|
|
||||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 5, 1, &materials[primitive.materialIndex].materialData.descriptorSet, 0, nullptr);
|
|
||||||
}
|
|
||||||
vkCmdDrawIndexed(commandBuffer, primitive.indexCount, 1, primitive.firstIndex, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto& child : node->children) {
|
|
||||||
drawNode(commandBuffer, pipelineLayout, child, bPushConstants);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the glTF scene starting at the top-level-nodes
|
|
||||||
void VulkanglTFModel::draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag = true)
|
|
||||||
{
|
|
||||||
// All vertices and indices are stored in single buffers, so we only need to bind once
|
|
||||||
VkDeviceSize offsets[1] = { 0 };
|
|
||||||
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
|
|
||||||
vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
|
||||||
// Render all nodes at top-level
|
|
||||||
for (auto& node : nodes) {
|
|
||||||
drawNode(commandBuffer, pipelineLayout, node, flag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
VulkanExample::VulkanExample():
|
VulkanExample::VulkanExample():
|
||||||
VulkanExampleBase(ENABLE_VALIDATION)
|
VulkanExampleBase(ENABLE_VALIDATION)
|
||||||
{
|
{
|
||||||
title = "homework1";
|
title = "render";
|
||||||
camera.type = Camera::CameraType::lookat;
|
camera.type = Camera::CameraType::lookat;
|
||||||
camera.flipY = true;
|
camera.flipY = true;
|
||||||
camera.setPosition(glm::vec3(0.0f, -0.1f, -1.0f));
|
camera.setPosition(glm::vec3(0.0f, -0.1f, -1.0f));
|
||||||
|
@ -671,7 +197,7 @@
|
||||||
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.pbrLayout, 0, 1, &descriptorSet, 0, nullptr);
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.pbrLayout, 0, 1, &descriptorSet, 0, nullptr);
|
||||||
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.pbrLayout, 6, 1, &skinDescriptorSet, 0, nullptr);
|
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.pbrLayout, 6, 1, &skinDescriptorSet, 0, nullptr);
|
||||||
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.solid);
|
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, wireframe ? pipelines.wireframe : pipelines.solid);
|
||||||
glTFModel.draw(drawCmdBuffers[i], pipelineLayouts.pbrLayout);
|
glTFModel.draw(drawCmdBuffers[i], pipelineLayouts.pbrLayout,false);
|
||||||
vkCmdEndRenderPass(drawCmdBuffers[i]);
|
vkCmdEndRenderPass(drawCmdBuffers[i]);
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#pragma once
|
||||||
/*
|
/*
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
#define TINYGLTF_NO_STB_IMAGE_WRITE
|
#define TINYGLTF_NO_STB_IMAGE_WRITE
|
||||||
#include "tiny_gltf.h"
|
#include "tiny_gltf.h"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "vulkanexamplebase.h"
|
#include "vulkanexamplebase.h"
|
||||||
#include "glTFModel.h"
|
#include "glTFModel.h"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue