reconstruct gltf loader
							parent
							
								
									f5096914ae
								
							
						
					
					
						commit
						93ceae3657
					
				| 
						 | 
				
			
			@ -432,148 +432,190 @@ void glTFModel::Texture::fromglTfImage(tinygltf::Image& gltfImage, std::string p
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
void VulkanglTFModel::loadTextures(tinygltf::Model& input)
 | 
			
		||||
void glTFModel::Model::loadImages(tinygltf::Model& gltfModel, vks::VulkanDevice* device, VkQueue transferQueue)
 | 
			
		||||
{
 | 
			
		||||
	textures.resize(input.textures.size());
 | 
			
		||||
	for (size_t i = 0; i < input.textures.size(); i++) {
 | 
			
		||||
		textures[i].imageIndex = input.textures[i].source;
 | 
			
		||||
	for (tinygltf::Image& image : gltfModel.images) {
 | 
			
		||||
		glTFModel::Texture texture;
 | 
			
		||||
		texture.fromglTfImage(image, path, device, transferQueue);
 | 
			
		||||
		textures.push_back(texture);
 | 
			
		||||
	}
 | 
			
		||||
	// Create an empty texture to be used for empty material images
 | 
			
		||||
	createEmptyTexture(transferQueue);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VulkanglTFModel::loadAnimations(tinygltf::Model& input)
 | 
			
		||||
 | 
			
		||||
void glTFModel::Model::loadAnimations(tinygltf::Model& gltfModel)
 | 
			
		||||
{
 | 
			
		||||
	animations.resize(input.animations.size());
 | 
			
		||||
	for (tinygltf::Animation& anim : gltfModel.animations) {
 | 
			
		||||
		glTFModel::Animation animation{};
 | 
			
		||||
		animation.name = anim.name;
 | 
			
		||||
		if (anim.name.empty()) {
 | 
			
		||||
			animation.name = std::to_string(animations.size());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	for (size_t i = 0; i < input.animations.size(); ++i)
 | 
			
		||||
	{
 | 
			
		||||
		auto glTFAnimation = input.animations[i];
 | 
			
		||||
		animations[i].name = glTFAnimation.name;
 | 
			
		||||
		// Samplers
 | 
			
		||||
		for (auto& samp : anim.samplers) {
 | 
			
		||||
			glTFModel::AnimationSampler sampler{};
 | 
			
		||||
 | 
			
		||||
		//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;
 | 
			
		||||
			if (samp.interpolation == "LINEAR") {
 | 
			
		||||
				sampler.interpolation = AnimationSampler::InterpolationType::LINEAR;
 | 
			
		||||
			}
 | 
			
		||||
			if (samp.interpolation == "STEP") {
 | 
			
		||||
				sampler.interpolation = AnimationSampler::InterpolationType::STEP;
 | 
			
		||||
			}
 | 
			
		||||
			if (samp.interpolation == "CUBICSPLINE") {
 | 
			
		||||
				sampler.interpolation = AnimationSampler::InterpolationType::CUBICSPLINE;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Read sampler keyframe input time values
 | 
			
		||||
			// Read sampler 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]);
 | 
			
		||||
				const tinygltf::Accessor& accessor = gltfModel.accessors[samp.input];
 | 
			
		||||
				const tinygltf::BufferView& bufferView = gltfModel.bufferViews[accessor.bufferView];
 | 
			
		||||
				const tinygltf::Buffer& buffer = gltfModel.buffers[bufferView.buffer];
 | 
			
		||||
 | 
			
		||||
				assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
 | 
			
		||||
 | 
			
		||||
				float* buf = new float[accessor.count];
 | 
			
		||||
				memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(float));
 | 
			
		||||
				for (size_t index = 0; index < accessor.count; index++) {
 | 
			
		||||
					sampler.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;
 | 
			
		||||
				delete[] buf;
 | 
			
		||||
				for (auto input : sampler.inputs) {
 | 
			
		||||
					if (input < animation.start) {
 | 
			
		||||
						animation.start = input;
 | 
			
		||||
					};
 | 
			
		||||
					if (input > animations[i].end)
 | 
			
		||||
					{
 | 
			
		||||
						animations[i].end = input;
 | 
			
		||||
					if (input > animation.end) {
 | 
			
		||||
						animation.end = input;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Read sampler keyframe output translate/rotate/scale values
 | 
			
		||||
			// Read sampler output T/R/S 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));
 | 
			
		||||
				const tinygltf::Accessor& accessor = gltfModel.accessors[samp.output];
 | 
			
		||||
				const tinygltf::BufferView& bufferView = gltfModel.bufferViews[accessor.bufferView];
 | 
			
		||||
				const tinygltf::Buffer& buffer = gltfModel.buffers[bufferView.buffer];
 | 
			
		||||
 | 
			
		||||
				assert(accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
 | 
			
		||||
 | 
			
		||||
				switch (accessor.type) {
 | 
			
		||||
				case TINYGLTF_TYPE_VEC3: {
 | 
			
		||||
					glm::vec3* buf = new glm::vec3[accessor.count];
 | 
			
		||||
					memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(glm::vec3));
 | 
			
		||||
					for (size_t index = 0; index < accessor.count; index++) {
 | 
			
		||||
						sampler.outputsVec4.push_back(glm::vec4(buf[index], 0.0f));
 | 
			
		||||
					}
 | 
			
		||||
					delete[] buf;
 | 
			
		||||
					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]);
 | 
			
		||||
				case TINYGLTF_TYPE_VEC4: {
 | 
			
		||||
					glm::vec4* buf = new glm::vec4[accessor.count];
 | 
			
		||||
					memcpy(buf, &buffer.data[accessor.byteOffset + bufferView.byteOffset], accessor.count * sizeof(glm::vec4));
 | 
			
		||||
					for (size_t index = 0; index < accessor.count; index++) {
 | 
			
		||||
						sampler.outputsVec4.push_back(buf[index]);
 | 
			
		||||
					}
 | 
			
		||||
					delete[] buf;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				default:
 | 
			
		||||
				{
 | 
			
		||||
				default: {
 | 
			
		||||
					std::cout << "unknown type" << std::endl;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			animation.samplers.push_back(sampler);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		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);
 | 
			
		||||
		// Channels
 | 
			
		||||
		for (auto& source : anim.channels) {
 | 
			
		||||
			glTFModel::AnimationChannel channel{};
 | 
			
		||||
 | 
			
		||||
			if (source.target_path == "rotation") {
 | 
			
		||||
				channel.path = AnimationChannel::PathType::ROTATION;
 | 
			
		||||
			}
 | 
			
		||||
			if (source.target_path == "translation") {
 | 
			
		||||
				channel.path = AnimationChannel::PathType::TRANSLATION;
 | 
			
		||||
			}
 | 
			
		||||
			if (source.target_path == "scale") {
 | 
			
		||||
				channel.path = AnimationChannel::PathType::SCALE;
 | 
			
		||||
			}
 | 
			
		||||
			if (source.target_path == "weights") {
 | 
			
		||||
				std::cout << "weights not yet supported, skipping channel" << std::endl;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			channel.samplerIndex = source.sampler;
 | 
			
		||||
			channel.node = nodeFromIndex(source.target_node);
 | 
			
		||||
			if (!channel.node) {
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			animation.channels.push_back(channel);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		animations.push_back(animation);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
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());
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	glTF material
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
void glTFModel::Model::loadMaterials(tinygltf::Model& gltfModel)
 | 
			
		||||
{
 | 
			
		||||
	
 | 
			
		||||
	for (tinygltf::Material& mat : gltfModel.materials) {
 | 
			
		||||
		glTFModel::Material material(device);
 | 
			
		||||
		if (mat.values.find("baseColorTexture") != mat.values.end()) {
 | 
			
		||||
			material.baseColorTexture = getTexture(gltfModel.textures[mat.values["baseColorTexture"].TextureIndex()].source);
 | 
			
		||||
		}
 | 
			
		||||
		// Metallic roughness workflow
 | 
			
		||||
		if (mat.values.find("metallicRoughnessTexture") != mat.values.end()) {
 | 
			
		||||
			material.metallicRoughnessTexture = getTexture(gltfModel.textures[mat.values["metallicRoughnessTexture"].TextureIndex()].source);
 | 
			
		||||
		}
 | 
			
		||||
		if (mat.values.find("roughnessFactor") != mat.values.end()) {
 | 
			
		||||
			material.roughnessFactor = static_cast<float>(mat.values["roughnessFactor"].Factor());
 | 
			
		||||
		}
 | 
			
		||||
		if (mat.values.find("metallicFactor") != mat.values.end()) {
 | 
			
		||||
			material.metallicFactor = static_cast<float>(mat.values["metallicFactor"].Factor());
 | 
			
		||||
		}
 | 
			
		||||
		if (mat.values.find("baseColorFactor") != mat.values.end()) {
 | 
			
		||||
			material.baseColorFactor = glm::make_vec4(mat.values["baseColorFactor"].ColorFactor().data());
 | 
			
		||||
		}
 | 
			
		||||
		if (mat.additionalValues.find("normalTexture") != mat.additionalValues.end()) {
 | 
			
		||||
			material.normalTexture = getTexture(gltfModel.textures[mat.additionalValues["normalTexture"].TextureIndex()].source);
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			material.normalTexture = &emptyTexture;
 | 
			
		||||
		}
 | 
			
		||||
		if (mat.additionalValues.find("emissiveTexture") != mat.additionalValues.end()) {
 | 
			
		||||
			material.emissiveTexture = getTexture(gltfModel.textures[mat.additionalValues["emissiveTexture"].TextureIndex()].source);
 | 
			
		||||
		}
 | 
			
		||||
		if (mat.additionalValues.find("occlusionTexture") != mat.additionalValues.end()) {
 | 
			
		||||
			material.occlusionTexture = getTexture(gltfModel.textures[mat.additionalValues["occlusionTexture"].TextureIndex()].source);
 | 
			
		||||
		}
 | 
			
		||||
		if (mat.additionalValues.find("alphaMode") != mat.additionalValues.end()) {
 | 
			
		||||
			tinygltf::Parameter param = mat.additionalValues["alphaMode"];
 | 
			
		||||
			if (param.string_value == "BLEND") {
 | 
			
		||||
				material.alphaMode = Material::ALPHAMODE_BLEND;
 | 
			
		||||
			}
 | 
			
		||||
			if (param.string_value == "MASK") {
 | 
			
		||||
				material.alphaMode = Material::ALPHAMODE_MASK;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (mat.additionalValues.find("alphaCutoff") != mat.additionalValues.end()) {
 | 
			
		||||
			material.alphaCutoff = static_cast<float>(mat.additionalValues["alphaCutoff"].Factor());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		materials.push_back(material);
 | 
			
		||||
	}
 | 
			
		||||
	// Push a default material at the end of the list for meshes with no material assigned
 | 
			
		||||
	materials.push_back(Material(device));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void glTFModel::Material::createDescriptorSet(VkDescriptorPool descriptorPool, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorBindingFlags)
 | 
			
		||||
{
 | 
			
		||||
	VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
 | 
			
		||||
| 
						 | 
				
			
			@ -902,7 +944,6 @@ void glTFModel::Model::loadNode(glTFModel::Node* parent, const tinygltf::Node& n
 | 
			
		|||
	newNode->skinIndex = node.skin;
 | 
			
		||||
	newNode->matrix = glm::mat4(1.0f);
 | 
			
		||||
	newNode->parent = parent;
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	// Get the local node matrix
 | 
			
		||||
	// It's either made up from translation, rotation, scale or a 4x4 matrix
 | 
			
		||||
| 
						 | 
				
			
			@ -1112,7 +1153,294 @@ void glTFModel::Model::loadNode(glTFModel::Node* parent, const tinygltf::Node& n
 | 
			
		|||
	linearNodes.push_back(newNode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)
 | 
			
		||||
// file loader function
 | 
			
		||||
void glTFModel::Model::loadFromFile(std::string filename, vks::VulkanDevice* device, VkQueue transferQueue, uint32_t fileLoadingFlags, float scale)
 | 
			
		||||
{
 | 
			
		||||
	tinygltf::Model gltfModel;
 | 
			
		||||
	tinygltf::TinyGLTF gltfContext;
 | 
			
		||||
	if (fileLoadingFlags & FileLoadingFlags::DontLoadImages) {
 | 
			
		||||
		gltfContext.SetImageLoader(loadImageDataFuncEmpty, nullptr);
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		gltfContext.SetImageLoader(loadImageDataFunc, nullptr);
 | 
			
		||||
	}
 | 
			
		||||
#if defined(__ANDROID__)
 | 
			
		||||
	// On Android all assets are packed with the apk in a compressed form, so we need to open them using the asset manager
 | 
			
		||||
	// We let tinygltf handle this, by passing the asset manager of our app
 | 
			
		||||
	tinygltf::asset_manager = androidApp->activity->assetManager;
 | 
			
		||||
#endif
 | 
			
		||||
	size_t pos = filename.find_last_of('/');
 | 
			
		||||
	path = filename.substr(0, pos);
 | 
			
		||||
 | 
			
		||||
	std::string error, warning;
 | 
			
		||||
 | 
			
		||||
	this->device = device;
 | 
			
		||||
 | 
			
		||||
#if defined(__ANDROID__)
 | 
			
		||||
	// On Android all assets are packed with the apk in a compressed form, so we need to open them using the asset manager
 | 
			
		||||
	// We let tinygltf handle this, by passing the asset manager of our app
 | 
			
		||||
	tinygltf::asset_manager = androidApp->activity->assetManager;
 | 
			
		||||
#endif
 | 
			
		||||
	bool fileLoaded = gltfContext.LoadASCIIFromFile(&gltfModel, &error, &warning, filename);
 | 
			
		||||
 | 
			
		||||
	std::vector<uint32_t> indexBuffer;
 | 
			
		||||
	std::vector<Vertex> vertexBuffer;
 | 
			
		||||
 | 
			
		||||
	if (fileLoaded) {
 | 
			
		||||
		if (!(fileLoadingFlags & FileLoadingFlags::DontLoadImages)) {
 | 
			
		||||
			loadImages(gltfModel, device, transferQueue);
 | 
			
		||||
		}
 | 
			
		||||
		loadMaterials(gltfModel);
 | 
			
		||||
		const tinygltf::Scene& scene = gltfModel.scenes[gltfModel.defaultScene > -1 ? gltfModel.defaultScene : 0];
 | 
			
		||||
		for (size_t i = 0; i < scene.nodes.size(); i++) {
 | 
			
		||||
			const tinygltf::Node node = gltfModel.nodes[scene.nodes[i]];
 | 
			
		||||
			loadNode(nullptr, node, scene.nodes[i], gltfModel, indexBuffer, vertexBuffer, scale);
 | 
			
		||||
		}
 | 
			
		||||
		if (gltfModel.animations.size() > 0) {
 | 
			
		||||
			loadAnimations(gltfModel);
 | 
			
		||||
		}
 | 
			
		||||
		loadSkins(gltfModel);
 | 
			
		||||
 | 
			
		||||
		for (auto node : linearNodes) {
 | 
			
		||||
			// Assign skins
 | 
			
		||||
			if (node->skinIndex > -1) {
 | 
			
		||||
				node->skin = skins[node->skinIndex];
 | 
			
		||||
			}
 | 
			
		||||
			// Initial pose
 | 
			
		||||
			if (node->mesh) {
 | 
			
		||||
				node->update();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		// TODO: throw
 | 
			
		||||
		vks::tools::exitFatal("Could not load glTF file \"" + filename + "\": " + error, -1);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Pre-Calculations for requested features
 | 
			
		||||
	if ((fileLoadingFlags & FileLoadingFlags::PreTransformVertices) || (fileLoadingFlags & FileLoadingFlags::PreMultiplyVertexColors) || (fileLoadingFlags & FileLoadingFlags::FlipY)) {
 | 
			
		||||
		const bool preTransform = fileLoadingFlags & FileLoadingFlags::PreTransformVertices;
 | 
			
		||||
		const bool preMultiplyColor = fileLoadingFlags & FileLoadingFlags::PreMultiplyVertexColors;
 | 
			
		||||
		const bool flipY = fileLoadingFlags & FileLoadingFlags::FlipY;
 | 
			
		||||
		for (Node* node : linearNodes) {
 | 
			
		||||
			if (node->mesh) {
 | 
			
		||||
				const glm::mat4 localMatrix = node->getMatrix();
 | 
			
		||||
				for (Primitive* primitive : node->mesh->primitives) {
 | 
			
		||||
					for (uint32_t i = 0; i < primitive->vertexCount; i++) {
 | 
			
		||||
						Vertex& vertex = vertexBuffer[primitive->firstVertex + i];
 | 
			
		||||
						// Pre-transform vertex positions by node-hierarchy
 | 
			
		||||
						if (preTransform) {
 | 
			
		||||
							vertex.pos = glm::vec3(localMatrix * glm::vec4(vertex.pos, 1.0f));
 | 
			
		||||
							vertex.normal = glm::normalize(glm::mat3(localMatrix) * vertex.normal);
 | 
			
		||||
						}
 | 
			
		||||
						// Flip Y-Axis of vertex positions
 | 
			
		||||
						if (flipY) {
 | 
			
		||||
							vertex.pos.y *= -1.0f;
 | 
			
		||||
							vertex.normal.y *= -1.0f;
 | 
			
		||||
						}
 | 
			
		||||
						// Pre-Multiply vertex colors with material base color
 | 
			
		||||
						if (preMultiplyColor) {
 | 
			
		||||
							vertex.color = primitive->material.baseColorFactor * vertex.color;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (auto extension : gltfModel.extensionsUsed) {
 | 
			
		||||
		if (extension == "KHR_materials_pbrSpecularGlossiness") {
 | 
			
		||||
			std::cout << "Required extension: " << extension;
 | 
			
		||||
			metallicRoughnessWorkflow = false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	size_t vertexBufferSize = vertexBuffer.size() * sizeof(Vertex);
 | 
			
		||||
	size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t);
 | 
			
		||||
	indices.count = static_cast<uint32_t>(indexBuffer.size());
 | 
			
		||||
	vertices.count = static_cast<uint32_t>(vertexBuffer.size());
 | 
			
		||||
 | 
			
		||||
	assert((vertexBufferSize > 0) && (indexBufferSize > 0));
 | 
			
		||||
 | 
			
		||||
	struct StagingBuffer {
 | 
			
		||||
		VkBuffer buffer;
 | 
			
		||||
		VkDeviceMemory memory;
 | 
			
		||||
	} vertexStaging, indexStaging;
 | 
			
		||||
 | 
			
		||||
	// Create staging buffers
 | 
			
		||||
	// Vertex data
 | 
			
		||||
	VK_CHECK_RESULT(device->createBuffer(
 | 
			
		||||
		VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
 | 
			
		||||
		VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
 | 
			
		||||
		vertexBufferSize,
 | 
			
		||||
		&vertexStaging.buffer,
 | 
			
		||||
		&vertexStaging.memory,
 | 
			
		||||
		vertexBuffer.data()));
 | 
			
		||||
	// Index data
 | 
			
		||||
	VK_CHECK_RESULT(device->createBuffer(
 | 
			
		||||
		VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
 | 
			
		||||
		VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
 | 
			
		||||
		indexBufferSize,
 | 
			
		||||
		&indexStaging.buffer,
 | 
			
		||||
		&indexStaging.memory,
 | 
			
		||||
		indexBuffer.data()));
 | 
			
		||||
 | 
			
		||||
	// Create device local buffers
 | 
			
		||||
	// Vertex buffer
 | 
			
		||||
	VK_CHECK_RESULT(device->createBuffer(
 | 
			
		||||
		VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | memoryPropertyFlags,
 | 
			
		||||
		VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
 | 
			
		||||
		vertexBufferSize,
 | 
			
		||||
		&vertices.buffer,
 | 
			
		||||
		&vertices.memory));
 | 
			
		||||
	// Index buffer
 | 
			
		||||
	VK_CHECK_RESULT(device->createBuffer(
 | 
			
		||||
		VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | memoryPropertyFlags,
 | 
			
		||||
		VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
 | 
			
		||||
		indexBufferSize,
 | 
			
		||||
		&indices.buffer,
 | 
			
		||||
		&indices.memory));
 | 
			
		||||
 | 
			
		||||
	// Copy from staging buffers
 | 
			
		||||
	VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
 | 
			
		||||
 | 
			
		||||
	VkBufferCopy copyRegion = {};
 | 
			
		||||
 | 
			
		||||
	copyRegion.size = vertexBufferSize;
 | 
			
		||||
	vkCmdCopyBuffer(copyCmd, vertexStaging.buffer, vertices.buffer, 1, ©Region);
 | 
			
		||||
 | 
			
		||||
	copyRegion.size = indexBufferSize;
 | 
			
		||||
	vkCmdCopyBuffer(copyCmd, indexStaging.buffer, indices.buffer, 1, ©Region);
 | 
			
		||||
 | 
			
		||||
	device->flushCommandBuffer(copyCmd, transferQueue, true);
 | 
			
		||||
 | 
			
		||||
	vkDestroyBuffer(device->logicalDevice, vertexStaging.buffer, nullptr);
 | 
			
		||||
	vkFreeMemory(device->logicalDevice, vertexStaging.memory, nullptr);
 | 
			
		||||
	vkDestroyBuffer(device->logicalDevice, indexStaging.buffer, nullptr);
 | 
			
		||||
	vkFreeMemory(device->logicalDevice, indexStaging.memory, nullptr);
 | 
			
		||||
 | 
			
		||||
	getSceneDimensions();
 | 
			
		||||
 | 
			
		||||
	// Setup descriptors
 | 
			
		||||
	uint32_t uboCount{ 0 };
 | 
			
		||||
	uint32_t imageCount{ 0 };
 | 
			
		||||
	for (auto node : linearNodes) {
 | 
			
		||||
		if (node->mesh) {
 | 
			
		||||
			uboCount++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for (auto material : materials) {
 | 
			
		||||
		if (material.baseColorTexture != nullptr) {
 | 
			
		||||
			imageCount++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	std::vector<VkDescriptorPoolSize> poolSizes = {
 | 
			
		||||
		{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uboCount },
 | 
			
		||||
	};
 | 
			
		||||
	if (imageCount > 0) {
 | 
			
		||||
		if (descriptorBindingFlags & DescriptorBindingFlags::ImageBaseColor) {
 | 
			
		||||
			poolSizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageCount });
 | 
			
		||||
		}
 | 
			
		||||
		if (descriptorBindingFlags & DescriptorBindingFlags::ImageNormalMap) {
 | 
			
		||||
			poolSizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageCount });
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	VkDescriptorPoolCreateInfo descriptorPoolCI{};
 | 
			
		||||
	descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
 | 
			
		||||
	descriptorPoolCI.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
 | 
			
		||||
	descriptorPoolCI.pPoolSizes = poolSizes.data();
 | 
			
		||||
	descriptorPoolCI.maxSets = uboCount + imageCount;
 | 
			
		||||
	VK_CHECK_RESULT(vkCreateDescriptorPool(device->logicalDevice, &descriptorPoolCI, nullptr, &descriptorPool));
 | 
			
		||||
 | 
			
		||||
	// Descriptors for per-node uniform buffers
 | 
			
		||||
	{
 | 
			
		||||
		// Layout is global, so only create if it hasn't already been created before
 | 
			
		||||
		if (descriptorSetLayoutUbo == VK_NULL_HANDLE) {
 | 
			
		||||
			std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
 | 
			
		||||
				vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
 | 
			
		||||
			};
 | 
			
		||||
			VkDescriptorSetLayoutCreateInfo descriptorLayoutCI{};
 | 
			
		||||
			descriptorLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
 | 
			
		||||
			descriptorLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
 | 
			
		||||
			descriptorLayoutCI.pBindings = setLayoutBindings.data();
 | 
			
		||||
			VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device->logicalDevice, &descriptorLayoutCI, nullptr, &descriptorSetLayoutUbo));
 | 
			
		||||
		}
 | 
			
		||||
		for (auto node : nodes) {
 | 
			
		||||
			prepareNodeDescriptor(node, descriptorSetLayoutUbo);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Descriptors for per-material images
 | 
			
		||||
	{
 | 
			
		||||
		// Layout is global, so only create if it hasn't already been created before
 | 
			
		||||
		if (descriptorSetLayoutImage == VK_NULL_HANDLE) {
 | 
			
		||||
			std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings{};
 | 
			
		||||
			if (descriptorBindingFlags & DescriptorBindingFlags::ImageBaseColor) {
 | 
			
		||||
				setLayoutBindings.push_back(vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, static_cast<uint32_t>(setLayoutBindings.size())));
 | 
			
		||||
			}
 | 
			
		||||
			if (descriptorBindingFlags & DescriptorBindingFlags::ImageNormalMap) {
 | 
			
		||||
				setLayoutBindings.push_back(vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, static_cast<uint32_t>(setLayoutBindings.size())));
 | 
			
		||||
			}
 | 
			
		||||
			VkDescriptorSetLayoutCreateInfo descriptorLayoutCI{};
 | 
			
		||||
			descriptorLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
 | 
			
		||||
			descriptorLayoutCI.bindingCount = static_cast<uint32_t>(setLayoutBindings.size());
 | 
			
		||||
			descriptorLayoutCI.pBindings = setLayoutBindings.data();
 | 
			
		||||
			VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device->logicalDevice, &descriptorLayoutCI, nullptr, &descriptorSetLayoutImage));
 | 
			
		||||
		}
 | 
			
		||||
		for (auto& material : materials) {
 | 
			
		||||
			if (material.baseColorTexture != nullptr) {
 | 
			
		||||
				material.createDescriptorSet(descriptorPool, glTFModel::descriptorSetLayoutImage, descriptorBindingFlags);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glTFModel::Model::bindBuffers(VkCommandBuffer commandBuffer)
 | 
			
		||||
{
 | 
			
		||||
	const VkDeviceSize offsets[1] = { 0 };
 | 
			
		||||
	vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
 | 
			
		||||
	vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
 | 
			
		||||
	buffersBound = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	helper function
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
void glTFModel::Model::getNodeDimensions(Node* node, glm::vec3& min, glm::vec3& max)
 | 
			
		||||
{
 | 
			
		||||
	if (node->mesh) {
 | 
			
		||||
		for (Primitive* primitive : node->mesh->primitives) {
 | 
			
		||||
			glm::vec4 locMin = glm::vec4(primitive->dimensions.min, 1.0f) * node->getMatrix();
 | 
			
		||||
			glm::vec4 locMax = glm::vec4(primitive->dimensions.max, 1.0f) * node->getMatrix();
 | 
			
		||||
			if (locMin.x < min.x) { min.x = locMin.x; }
 | 
			
		||||
			if (locMin.y < min.y) { min.y = locMin.y; }
 | 
			
		||||
			if (locMin.z < min.z) { min.z = locMin.z; }
 | 
			
		||||
			if (locMax.x > max.x) { max.x = locMax.x; }
 | 
			
		||||
			if (locMax.y > max.y) { max.y = locMax.y; }
 | 
			
		||||
			if (locMax.z > max.z) { max.z = locMax.z; }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for (auto child : node->children) {
 | 
			
		||||
		getNodeDimensions(child, min, max);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void glTFModel::Model::getSceneDimensions()
 | 
			
		||||
{
 | 
			
		||||
	dimensions.min = glm::vec3(FLT_MAX);
 | 
			
		||||
	dimensions.max = glm::vec3(-FLT_MAX);
 | 
			
		||||
	for (auto node : nodes) {
 | 
			
		||||
		getNodeDimensions(node, dimensions.min, dimensions.max);
 | 
			
		||||
	}
 | 
			
		||||
	dimensions.size = dimensions.max - dimensions.min;
 | 
			
		||||
	dimensions.center = (dimensions.min + dimensions.max) / 2.0f;
 | 
			
		||||
	dimensions.radius = glm::distance(dimensions.min, dimensions.max) / 2.0f;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
glTFModel::Node* glTFModel::Model::findNode(Node* parent, uint32_t index)
 | 
			
		||||
{
 | 
			
		||||
	Node* nodeFound = nullptr;
 | 
			
		||||
	if (parent->index == index)
 | 
			
		||||
| 
						 | 
				
			
			@ -1130,7 +1458,7 @@ VulkanglTFModel::Node* VulkanglTFModel::findNode(Node* parent, uint32_t index)
 | 
			
		|||
	return nodeFound;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VulkanglTFModel::Node* VulkanglTFModel::nodeFromIndex(uint32_t index)
 | 
			
		||||
glTFModel::Node* glTFModel::Model::nodeFromIndex(uint32_t index)
 | 
			
		||||
{
 | 
			
		||||
	Node* nodeFound = nullptr;
 | 
			
		||||
	for (auto& node : nodes)
 | 
			
		||||
| 
						 | 
				
			
			@ -1144,67 +1472,72 @@ VulkanglTFModel::Node* VulkanglTFModel::nodeFromIndex(uint32_t index)
 | 
			
		|||
	return nodeFound;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VulkanglTFModel::updateAnimation(float deltaTime, vks::Buffer& buffer)
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
	gltf update function
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void glTFModel::Model::updateAnimation(uint32_t index, float time)
 | 
			
		||||
{
 | 
			
		||||
	constexpr uint32_t activeAnimation = 0;
 | 
			
		||||
	Animation& animation = animations[activeAnimation];
 | 
			
		||||
	animation.currentTime += deltaTime;
 | 
			
		||||
	if (animation.currentTime > animation.end)
 | 
			
		||||
	{
 | 
			
		||||
		animation.currentTime -= animation.end;
 | 
			
		||||
	if (index > static_cast<uint32_t>(animations.size()) - 1) {
 | 
			
		||||
		std::cout << "No animation with index " << index << std::endl;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	Animation& animation = animations[index];
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	bool updated = false;
 | 
			
		||||
	for (auto& channel : animation.channels) {
 | 
			
		||||
		glTFModel::AnimationSampler& sampler = animation.samplers[channel.samplerIndex];
 | 
			
		||||
		if (sampler.inputs.size() > sampler.outputsVec4.size()) {
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
					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;
 | 
			
		||||
		for (auto i = 0; i < sampler.inputs.size() - 1; i++) {
 | 
			
		||||
			if ((time >= sampler.inputs[i]) && (time <= sampler.inputs[i + 1])) {
 | 
			
		||||
				float u = std::max(0.0f, time - sampler.inputs[i]) / (sampler.inputs[i + 1] - sampler.inputs[i]);
 | 
			
		||||
				if (u <= 1.0f) {
 | 
			
		||||
					switch (channel.path) {
 | 
			
		||||
					case glTFModel::AnimationChannel::PathType::TRANSLATION: {
 | 
			
		||||
						glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
 | 
			
		||||
						channel.node->translation = glm::vec3(trans);
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
					case glTFModel::AnimationChannel::PathType::SCALE: {
 | 
			
		||||
						glm::vec4 trans = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], u);
 | 
			
		||||
						channel.node->scale = glm::vec3(trans);
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
					case glTFModel::AnimationChannel::PathType::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, u));
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
					}
 | 
			
		||||
					updated = true;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	std::vector<glm::mat4> nodeMatrics(nodeCount);
 | 
			
		||||
	for (auto& node : nodes)
 | 
			
		||||
	{
 | 
			
		||||
		updateNodeMatrix(node, nodeMatrics);
 | 
			
		||||
	if (updated) {
 | 
			
		||||
		for (auto& node : nodes) {
 | 
			
		||||
			node->update();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	buffer.copyTo(nodeMatrics.data(), nodeCount * sizeof(glm::mat4));
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
void VulkanglTFModel::updateNodeMatrix(Node* node, std::vector<glm::mat4>& nodeMatrics)
 | 
			
		||||
{
 | 
			
		||||
	nodeMatrics[node->index] = getNodeMatrix(node);
 | 
			
		||||
| 
						 | 
				
			
			@ -1225,79 +1558,73 @@ glm::mat4 VulkanglTFModel::getNodeMatrix(Node* node)
 | 
			
		|||
	}
 | 
			
		||||
	return nodeMatrix;
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
void glTFModel::Model::prepareNodeDescriptor(glTFModel::Node* node, VkDescriptorSetLayout descriptorSetLayout) {
 | 
			
		||||
	if (node->mesh) {
 | 
			
		||||
		VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
 | 
			
		||||
		descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
 | 
			
		||||
		descriptorSetAllocInfo.descriptorPool = descriptorPool;
 | 
			
		||||
		descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayout;
 | 
			
		||||
		descriptorSetAllocInfo.descriptorSetCount = 1;
 | 
			
		||||
		VK_CHECK_RESULT(vkAllocateDescriptorSets(device->logicalDevice, &descriptorSetAllocInfo, &node->mesh->uniformBuffer.descriptorSet));
 | 
			
		||||
 | 
			
		||||
		VkWriteDescriptorSet writeDescriptorSet{};
 | 
			
		||||
		writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 | 
			
		||||
		writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
 | 
			
		||||
		writeDescriptorSet.descriptorCount = 1;
 | 
			
		||||
		writeDescriptorSet.dstSet = node->mesh->uniformBuffer.descriptorSet;
 | 
			
		||||
		writeDescriptorSet.dstBinding = 0;
 | 
			
		||||
		writeDescriptorSet.pBufferInfo = &node->mesh->uniformBuffer.descriptor;
 | 
			
		||||
 | 
			
		||||
		vkUpdateDescriptorSets(device->logicalDevice, 1, &writeDescriptorSet, 0, nullptr);
 | 
			
		||||
	}
 | 
			
		||||
	for (auto& child : node->children) {
 | 
			
		||||
		prepareNodeDescriptor(child, descriptorSetLayout);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	glTF rendering functions
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Draw a single node including child nodes (if present)
 | 
			
		||||
void VulkanglTFModel::drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants)
 | 
			
		||||
void glTFModel::Model::drawNode(Node* node, VkCommandBuffer commandBuffer, uint32_t renderFlags, VkPipelineLayout pipelineLayout, uint32_t bindImageSet)
 | 
			
		||||
{
 | 
			
		||||
	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)
 | 
			
		||||
				{
 | 
			
		||||
					//binding base color texture
 | 
			
		||||
					if (materials[primitive.materialIndex].baseColorTextureIndex < textures.size())
 | 
			
		||||
					{
 | 
			
		||||
						VulkanglTFModel::Texture texture = textures[materials[primitive.materialIndex].baseColorTextureIndex];
 | 
			
		||||
						vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &images[texture.imageIndex].descriptorSet, 0, nullptr);
 | 
			
		||||
 | 
			
		||||
					}
 | 
			
		||||
					
 | 
			
		||||
					//normal map binding
 | 
			
		||||
					if (materials[primitive.materialIndex].normalMapTextureIndex < textures.size())
 | 
			
		||||
					{
 | 
			
		||||
						auto normalMap = textures[materials[primitive.materialIndex].normalMapTextureIndex];
 | 
			
		||||
						vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 2, 1, &images[normalMap.imageIndex].descriptorSet, 0, nullptr);
 | 
			
		||||
					}
 | 
			
		||||
					//rough map binding
 | 
			
		||||
					if (materials[primitive.materialIndex].matalicRoughTextureIndex < textures.size())
 | 
			
		||||
					{
 | 
			
		||||
						auto roughMetalMap = textures[materials[primitive.materialIndex].matalicRoughTextureIndex];
 | 
			
		||||
						vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 3, 1, &images[roughMetalMap.imageIndex].descriptorSet, 0, nullptr);
 | 
			
		||||
					}
 | 
			
		||||
					
 | 
			
		||||
					//emissive texture binding
 | 
			
		||||
					if (materials[primitive.materialIndex].emissiveTextureIndex >= 0 && materials[primitive.materialIndex].emissiveTextureIndex < textures.size())
 | 
			
		||||
					{
 | 
			
		||||
						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, 5, 1, &materials[primitive.materialIndex].materialData.descriptorSet, 0, nullptr);
 | 
			
		||||
	if (node->mesh) {
 | 
			
		||||
		for (Primitive* primitive : node->mesh->primitives) {
 | 
			
		||||
			bool skip = false;
 | 
			
		||||
			const glTFModel::Material& material = primitive->material;
 | 
			
		||||
			if (renderFlags & RenderFlags::RenderOpaqueNodes) {
 | 
			
		||||
				skip = (material.alphaMode != Material::ALPHAMODE_OPAQUE);
 | 
			
		||||
			}
 | 
			
		||||
			if (renderFlags & RenderFlags::RenderAlphaMaskedNodes) {
 | 
			
		||||
				skip = (material.alphaMode != Material::ALPHAMODE_MASK);
 | 
			
		||||
			}
 | 
			
		||||
			if (renderFlags & RenderFlags::RenderAlphaBlendedNodes) {
 | 
			
		||||
				skip = (material.alphaMode != Material::ALPHAMODE_BLEND);
 | 
			
		||||
			}
 | 
			
		||||
			if (!skip) {
 | 
			
		||||
				if (renderFlags & RenderFlags::BindImages) {
 | 
			
		||||
					vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, bindImageSet, 1, &material.descriptorSet, 0, nullptr);
 | 
			
		||||
				}
 | 
			
		||||
				vkCmdDrawIndexed(commandBuffer, primitive.indexCount, 1, primitive.firstIndex, 0, 0);
 | 
			
		||||
				vkCmdDrawIndexed(commandBuffer, primitive->indexCount, 1, primitive->firstIndex, 0, 0);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for (auto& child : node->children) {
 | 
			
		||||
		drawNode(commandBuffer, pipelineLayout, child, bPushConstants);
 | 
			
		||||
		drawNode(child, commandBuffer, renderFlags, pipelineLayout, bindImageSet);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Draw the glTF scene starting at the top-level-nodes
 | 
			
		||||
void VulkanglTFModel::draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag = true)
 | 
			
		||||
void glTFModel::Model::draw(VkCommandBuffer commandBuffer, uint32_t renderFlags, VkPipelineLayout pipelineLayout, uint32_t bindImageSet)
 | 
			
		||||
{
 | 
			
		||||
	// 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
 | 
			
		||||
	if (!buffersBound) {
 | 
			
		||||
		const VkDeviceSize offsets[1] = { 0 };
 | 
			
		||||
		vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertices.buffer, offsets);
 | 
			
		||||
		vkCmdBindIndexBuffer(commandBuffer, indices.buffer, 0, VK_INDEX_TYPE_UINT32);
 | 
			
		||||
	}
 | 
			
		||||
	for (auto& node : nodes) {
 | 
			
		||||
		drawNode(commandBuffer, pipelineLayout, node, flag);
 | 
			
		||||
		drawNode(node, commandBuffer, renderFlags, pipelineLayout, bindImageSet);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue