commit for revert
							parent
							
								
									5804ca009a
								
							
						
					
					
						commit
						3c7ad199a0
					
				| 
						 | 
					@ -148,6 +148,6 @@ ENDIF(WIN32)
 | 
				
			||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/")
 | 
					set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_subdirectory(base)
 | 
					add_subdirectory(base)
 | 
				
			||||||
add_subdirectory(homework)
 | 
					add_subdirectory(littleRender)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# add_subdirectory(examples)
 | 
					# add_subdirectory(examples)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -1,36 +1,37 @@
 | 
				
			||||||
# Function for building single homework
 | 
					# Function for building single homework
 | 
				
			||||||
 | 
					# Function for building single homework
 | 
				
			||||||
function(buildHomework HOMEWORK_NAME)
 | 
					function(buildHomework HOMEWORK_NAME)
 | 
				
			||||||
	SET(HOMEWORK_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}/${HOMEWORK_NAME})
 | 
						SET(HOMEWORK_FOLDER ${CMAKE_CURRENT_SOURCE_DIR})
 | 
				
			||||||
	message(STATUS "Generating project file for homework in ${HOMEWORK_FOLDER}")
 | 
						message(STATUS "Generating project file for homework in ${HOMEWORK_FOLDER}")
 | 
				
			||||||
	# Main
 | 
						# Main
 | 
				
			||||||
	file(GLOB SOURCE *.cpp ${BASE_HEADERS} ${HOMEWORK_FOLDER}/*.cpp)
 | 
						file(GLOB SOURCE *.cpp ${BASE_HEADERS} ${HOMEWORK_FOLDER}/*.cpp)
 | 
				
			||||||
	SET(MAIN_CPP ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.cpp "homework1/homework1.h")
 | 
						SET(MAIN_CPP ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.cpp)
 | 
				
			||||||
	if(EXISTS ${HOMEWORK_FOLDER}/main.cpp)
 | 
						if(EXISTS ${HOMEWORK_FOLDER}/main.cpp)
 | 
				
			||||||
		SET(MAIN_CPP ${HOMEWORK_FOLDER}/main.cpp "homework1/homework1.h")
 | 
							SET(MAIN_CPP ${HOMEWORK_FOLDER}/main.cpp)
 | 
				
			||||||
	ENDIF()
 | 
						ENDIF()
 | 
				
			||||||
	if(EXISTS ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.h)
 | 
						if(EXISTS ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.h)
 | 
				
			||||||
		SET(MAIN_HEADER ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.h "homework1/homework1.h")
 | 
							SET(MAIN_HEADER ${HOMEWORK_FOLDER}/${HOMEWORK_NAME}.h)
 | 
				
			||||||
	ENDIF()
 | 
						ENDIF()
 | 
				
			||||||
	# imgui homework requires additional source files
 | 
						# imgui homework requires additional source files
 | 
				
			||||||
	IF(${HOMEWORK_NAME} STREQUAL "imgui")
 | 
						IF(${HOMEWORK_NAME} STREQUAL "imgui")
 | 
				
			||||||
		file(GLOB ADD_SOURCE "../external/imgui/*.cpp")
 | 
							file(GLOB ADD_SOURCE "../external/imgui/*.cpp")
 | 
				
			||||||
		SET(SOURCE ${SOURCE} ${ADD_SOURCE} "homework1/homework1.h")
 | 
							SET(SOURCE ${SOURCE} ${ADD_SOURCE})
 | 
				
			||||||
	ENDIF()
 | 
						ENDIF()
 | 
				
			||||||
	# wayland requires additional source files
 | 
						# wayland requires additional source files
 | 
				
			||||||
	IF(USE_WAYLAND_WSI)
 | 
						IF(USE_WAYLAND_WSI)
 | 
				
			||||||
		SET(SOURCE ${SOURCE} ${CMAKE_BINARY_DIR}/xdg-shell-client-protocol.h ${CMAKE_BINARY_DIR}/xdg-shell-protocol.c "homework1/homework1.h")
 | 
							SET(SOURCE ${SOURCE} ${CMAKE_BINARY_DIR}/xdg-shell-client-protocol.h ${CMAKE_BINARY_DIR}/xdg-shell-protocol.c)
 | 
				
			||||||
	ENDIF()
 | 
						ENDIF()
 | 
				
			||||||
	# Add shaders
 | 
						# Add shaders
 | 
				
			||||||
	set(SHADER_DIR_GLSL "../data/homework/shaders/glsl/${HOMEWORK_NAME}")
 | 
						set(SHADER_DIR_GLSL "../data/buster_drone/shaders/glsl")
 | 
				
			||||||
	file(GLOB SHADERS_GLSL "${SHADER_DIR_GLSL}/*.vert" "${SHADER_DIR_GLSL}/*.frag" "${SHADER_DIR_GLSL}/*.comp" "${SHADER_DIR_GLSL}/*.geom" "${SHADER_DIR_GLSL}/*.tesc" "${SHADER_DIR_GLSL}/*.tese" "${SHADER_DIR_GLSL}/*.mesh" "${SHADER_DIR_GLSL}/*.task" "${SHADER_DIR_GLSL}/*.rgen" "${SHADER_DIR_GLSL}/*.rchit" "${SHADER_DIR_GLSL}/*.rmiss" "${SHADER_DIR_GLSL}/*.rcall")
 | 
						file(GLOB SHADERS_GLSL "${SHADER_DIR_GLSL}/*.vert" "${SHADER_DIR_GLSL}/*.frag" "${SHADER_DIR_GLSL}/*.comp" "${SHADER_DIR_GLSL}/*.geom" "${SHADER_DIR_GLSL}/*.tesc" "${SHADER_DIR_GLSL}/*.tese" "${SHADER_DIR_GLSL}/*.mesh" "${SHADER_DIR_GLSL}/*.task" "${SHADER_DIR_GLSL}/*.rgen" "${SHADER_DIR_GLSL}/*.rchit" "${SHADER_DIR_GLSL}/*.rmiss" "${SHADER_DIR_GLSL}/*.rcall")
 | 
				
			||||||
	set(SHADER_DIR_HLSL "../data/homework/shaders/hlsl/${HOMEWORK_NAME}")
 | 
						set(SHADER_DIR_HLSL "../data/buster_drone/shaders/glsl")
 | 
				
			||||||
	file(GLOB SHADERS_HLSL "${SHADER_DIR_HLSL}/*.vert" "${SHADER_DIR_HLSL}/*.frag" "${SHADER_DIR_HLSL}/*.comp" "${SHADER_DIR_HLSL}/*.geom" "${SHADER_DIR_HLSL}/*.tesc" "${SHADER_DIR_HLSL}/*.tese" "${SHADER_DIR_HLSL}/*.mesh" "${SHADER_DIR_HLSL}/*.task" "${SHADER_DIR_HLSL}/*.rgen" "${SHADER_DIR_HLSL}/*.rchit" "${SHADER_DIR_HLSL}/*.rmiss" "${SHADER_DIR_HLSL}/*.rcall")
 | 
						file(GLOB SHADERS_HLSL "${SHADER_DIR_HLSL}/*.vert" "${SHADER_DIR_HLSL}/*.frag" "${SHADER_DIR_HLSL}/*.comp" "${SHADER_DIR_HLSL}/*.geom" "${SHADER_DIR_HLSL}/*.tesc" "${SHADER_DIR_HLSL}/*.tese" "${SHADER_DIR_HLSL}/*.mesh" "${SHADER_DIR_HLSL}/*.task" "${SHADER_DIR_HLSL}/*.rgen" "${SHADER_DIR_HLSL}/*.rchit" "${SHADER_DIR_HLSL}/*.rmiss" "${SHADER_DIR_HLSL}/*.rcall")
 | 
				
			||||||
	source_group("Shaders\\GLSL" FILES ${SHADERS_GLSL})
 | 
						source_group("Shaders\\GLSL" FILES ${SHADERS_GLSL})
 | 
				
			||||||
	source_group("Shaders\\HLSL" FILES ${SHADERS_HLSL})
 | 
						source_group("Shaders\\HLSL" FILES ${SHADERS_HLSL})
 | 
				
			||||||
	# 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} "homework1/homework1.h")
 | 
							add_executable(${HOMEWORK_NAME} WIN32 ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES})
 | 
				
			||||||
		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})
 | 
				
			||||||
| 
						 | 
					@ -78,8 +79,7 @@ function(buildHomeworks)
 | 
				
			||||||
endfunction(buildHomeworks)
 | 
					endfunction(buildHomeworks)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set(HOMEWORKS
 | 
					set(HOMEWORKS
 | 
				
			||||||
	homework0
 | 
						render
 | 
				
			||||||
	homework1
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
buildHomeworks()
 | 
					buildHomeworks()
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,490 @@
 | 
				
			||||||
 | 
					#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,9 +1,8 @@
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <assert.h>
 | 
					#define TINYGLTF_IMPLEMENTATION
 | 
				
			||||||
#include <stdio.h>
 | 
					#define STB_IMAGE_IMPLEMENTATION
 | 
				
			||||||
#include <stdlib.h>
 | 
					#define TINYGLTF_NO_STB_IMAGE_WRITE
 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <vector>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define GLM_FORCE_RADIANS
 | 
					#define GLM_FORCE_RADIANS
 | 
				
			||||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
 | 
					#define GLM_FORCE_DEPTH_ZERO_TO_ONE
 | 
				
			||||||
| 
						 | 
					@ -11,20 +10,10 @@
 | 
				
			||||||
#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
 | 
					 | 
				
			||||||
	#define TINYGLTF_ANDROID_LOAD_FROM_ASSETS
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#include "tiny_gltf.h"
 | 
					#include "tiny_gltf.h"
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "vulkanexamplebase.h"
 | 
					#include "vulkanexamplebase.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define ENABLE_VALIDATION false
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Contains everything required to render a glTF model in Vulkan
 | 
					// Contains everything required to render a glTF model in Vulkan
 | 
				
			||||||
// This class is heavily simplified (compared to glTF's feature set) but retains the basic glTF structure
 | 
					// This class is heavily simplified (compared to glTF's feature set) but retains the basic glTF structure
 | 
				
			||||||
class VulkanglTFModel
 | 
					class VulkanglTFModel
 | 
				
			||||||
| 
						 | 
					@ -189,7 +178,7 @@ public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	 //VulkanglTFModel();
 | 
						//VulkanglTFModel();
 | 
				
			||||||
	~VulkanglTFModel()
 | 
						~VulkanglTFModel()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		for (auto node : nodes) {
 | 
							for (auto node : nodes) {
 | 
				
			||||||
| 
						 | 
					@ -226,179 +215,3 @@ public:
 | 
				
			||||||
	void drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants);
 | 
						void drawNode(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, VulkanglTFModel::Node* node, bool bPushConstants);
 | 
				
			||||||
	void      draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag);
 | 
						void      draw(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout, bool flag);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
class VulkanExample : public VulkanExampleBase
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	bool wireframe = false;
 | 
					 | 
				
			||||||
	bool normalMapping = true;
 | 
					 | 
				
			||||||
	bool ToneMapping = true;
 | 
					 | 
				
			||||||
	bool pbrEnabled = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VulkanglTFModel glTFModel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct ShaderData {
 | 
					 | 
				
			||||||
		vks::Buffer buffer;
 | 
					 | 
				
			||||||
		struct Values {
 | 
					 | 
				
			||||||
			glm::mat4 projection;
 | 
					 | 
				
			||||||
			glm::mat4 model;
 | 
					 | 
				
			||||||
			glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, 5.0f, 1.0f);
 | 
					 | 
				
			||||||
			glm::vec4 viewPos;
 | 
					 | 
				
			||||||
			glm::vec4 bFlagSet = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
 | 
					 | 
				
			||||||
		} values;
 | 
					 | 
				
			||||||
		vks::Buffer skinSSBO;
 | 
					 | 
				
			||||||
	} shaderData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct StagingBuffer {
 | 
					 | 
				
			||||||
		VkBuffer buffer;
 | 
					 | 
				
			||||||
		VkDeviceMemory memory;
 | 
					 | 
				
			||||||
	} vertexStaging, indexStaging;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct Pipelines {
 | 
					 | 
				
			||||||
		VkPipeline solid;
 | 
					 | 
				
			||||||
		VkPipeline wireframe = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
		VkPipeline toneMapping = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
	} pipelines;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct  PipelineLayouts
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		VkPipelineLayout pbrLayout;
 | 
					 | 
				
			||||||
		VkPipelineLayout tonemappingLayout;
 | 
					 | 
				
			||||||
	} pipelineLayouts;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VkPipelineLayout pipelineLayout;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	VkDescriptorSet descriptorSet;
 | 
					 | 
				
			||||||
	VkDescriptorSet skinDescriptorSet;
 | 
					 | 
				
			||||||
	VkDescriptorSet tonemappingDescriptorSet = VK_NULL_HANDLE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct FrameBufferAttachment
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		VkImage image;
 | 
					 | 
				
			||||||
		VkDeviceMemory deviceMemory;
 | 
					 | 
				
			||||||
		VkImageView imageView;
 | 
					 | 
				
			||||||
		VkFormat format;
 | 
					 | 
				
			||||||
		
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		void destroy(VkDevice device)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			vkDestroyImage(device, image, nullptr);
 | 
					 | 
				
			||||||
			vkDestroyImageView(device, imageView,nullptr);
 | 
					 | 
				
			||||||
			vkFreeMemory(device, deviceMemory, nullptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct FrameBuffer
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		int32_t width, height;
 | 
					 | 
				
			||||||
		VkFramebuffer frameBuffer;
 | 
					 | 
				
			||||||
		VkRenderPass renderPass;
 | 
					 | 
				
			||||||
		void setSize(int32_t w, int32_t h)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			this->width = w;
 | 
					 | 
				
			||||||
			this->height = h;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		void destroy(VkDevice device)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			vkDestroyFramebuffer(device, frameBuffer, nullptr);
 | 
					 | 
				
			||||||
			vkDestroyRenderPass(device, renderPass, nullptr);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct PBRFrameBuffer {
 | 
					 | 
				
			||||||
		FrameBufferAttachment color, depth;
 | 
					 | 
				
			||||||
		FrameBuffer fbo;
 | 
					 | 
				
			||||||
		bool bCreate = false;
 | 
					 | 
				
			||||||
	} pbrFrameBuffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VkSampler colorSampler;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct DescriptorSetLayouts {
 | 
					 | 
				
			||||||
		VkDescriptorSetLayout matrices;
 | 
					 | 
				
			||||||
		VkDescriptorSetLayout textures;
 | 
					 | 
				
			||||||
		VkDescriptorSetLayout materialUniform;
 | 
					 | 
				
			||||||
		VkDescriptorSetLayout ssbo;
 | 
					 | 
				
			||||||
		VkDescriptorSetLayout jointMatrices;
 | 
					 | 
				
			||||||
	} descriptorSetLayouts;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct IBLTextures
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		vks::TextureCubeMap skyboxCube;
 | 
					 | 
				
			||||||
		vks::TextureCubeMap irradianceCube;
 | 
					 | 
				
			||||||
		vks::TextureCubeMap prefilteredCube;
 | 
					 | 
				
			||||||
		vks::Texture2D lutBrdf;
 | 
					 | 
				
			||||||
	} ibltextures;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct OffScreen
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		VkImage image;
 | 
					 | 
				
			||||||
		VkImageView view;
 | 
					 | 
				
			||||||
		VkDeviceMemory memory;
 | 
					 | 
				
			||||||
		VkFramebuffer framebuffer;
 | 
					 | 
				
			||||||
	} offscreen;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct IrradiancePushBlock
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		glm::mat4 mvp;
 | 
					 | 
				
			||||||
		// Sampling deltas
 | 
					 | 
				
			||||||
		float deltaPhi = (2.0f * float(M_PI)) / 180.0f;
 | 
					 | 
				
			||||||
		float deltaTheta = (0.5f * float(M_PI)) / 64.0f;
 | 
					 | 
				
			||||||
	} irradiancePushBlock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct PrefilterPushBlock {
 | 
					 | 
				
			||||||
		glm::mat4 mvp;
 | 
					 | 
				
			||||||
		float roughness;
 | 
					 | 
				
			||||||
		uint32_t numSamples = 32u;
 | 
					 | 
				
			||||||
	} prefilterPushBlock;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VulkanglTFModel skyboxModel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VulkanExample();
 | 
					 | 
				
			||||||
	~VulkanExample()
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		// Clean up used Vulkan resources
 | 
					 | 
				
			||||||
		// Note : Inherited destructor cleans up resources stored in base class
 | 
					 | 
				
			||||||
		vkDestroyPipeline(device, pipelines.solid, nullptr);
 | 
					 | 
				
			||||||
		vkDestroyPipeline(device, pipelines.toneMapping, nullptr);
 | 
					 | 
				
			||||||
		if (pipelines.wireframe != VK_NULL_HANDLE) {
 | 
					 | 
				
			||||||
			vkDestroyPipeline(device, pipelines.wireframe, nullptr);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		vkDestroyPipelineLayout(device, pipelineLayouts.pbrLayout, nullptr);
 | 
					 | 
				
			||||||
		vkDestroyPipelineLayout(device, pipelineLayouts.tonemappingLayout, nullptr);
 | 
					 | 
				
			||||||
		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.matrices, nullptr);
 | 
					 | 
				
			||||||
		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.textures, nullptr);
 | 
					 | 
				
			||||||
		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.materialUniform, nullptr);
 | 
					 | 
				
			||||||
		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.ssbo, nullptr);
 | 
					 | 
				
			||||||
		ibltextures.irradianceCube.destroy();
 | 
					 | 
				
			||||||
		ibltextures.skyboxCube.destroy();
 | 
					 | 
				
			||||||
		ibltextures.prefilteredCube.destroy();
 | 
					 | 
				
			||||||
		ibltextures.lutBrdf.destroy();
 | 
					 | 
				
			||||||
		pbrFrameBuffer.color.destroy(device);
 | 
					 | 
				
			||||||
		pbrFrameBuffer.depth.destroy(device);
 | 
					 | 
				
			||||||
		pbrFrameBuffer.fbo.destroy(device);
 | 
					 | 
				
			||||||
		vkDestroySampler(device, colorSampler, nullptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		shaderData.buffer.destroy();
 | 
					 | 
				
			||||||
		shaderData.skinSSBO.destroy();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	void         loadglTFFile(std::string filename, VulkanglTFModel& model, bool bSkyboxFlag);
 | 
					 | 
				
			||||||
	virtual void getEnabledFeatures();
 | 
					 | 
				
			||||||
	void createAttachment(VkFormat format, VkImageUsageFlagBits usage, FrameBufferAttachment* attachment, uint32_t width, uint32_t height);
 | 
					 | 
				
			||||||
	virtual void setupFrameBuffer();
 | 
					 | 
				
			||||||
	void         buildCommandBuffers();
 | 
					 | 
				
			||||||
	void         loadAssets();
 | 
					 | 
				
			||||||
	void         setupDescriptors();
 | 
					 | 
				
			||||||
	void         preparePipelines();
 | 
					 | 
				
			||||||
	void CreateToneMappingPipeline();
 | 
					 | 
				
			||||||
	void GenerateIrradianceCubemap();
 | 
					 | 
				
			||||||
	void GeneratePrefilteredCubemap();
 | 
					 | 
				
			||||||
	void GenerateBRDFLUT();
 | 
					 | 
				
			||||||
	void         prepareUniformBuffers();
 | 
					 | 
				
			||||||
	void         updateUniformBuffers();
 | 
					 | 
				
			||||||
	void         prepare();
 | 
					 | 
				
			||||||
	virtual void render();
 | 
					 | 
				
			||||||
	virtual void viewChanged();
 | 
					 | 
				
			||||||
	virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
| 
						 | 
					@ -16,496 +16,8 @@
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * If you are looking for a complete glTF implementation, check out https://github.com/SaschaWillems/Vulkan-glTF-PBR/
 | 
					 * If you are looking for a complete glTF implementation, check out https://github.com/SaschaWillems/Vulkan-glTF-PBR/
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#include "homework1.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);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -672,7 +184,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,true);
 | 
				
			||||||
			vkCmdEndRenderPass(drawCmdBuffers[i]);
 | 
								vkCmdEndRenderPass(drawCmdBuffers[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,202 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GLM_FORCE_RADIANS
 | 
				
			||||||
 | 
					#define GLM_FORCE_DEPTH_ZERO_TO_ONE
 | 
				
			||||||
 | 
					#include <glm/glm.hpp>
 | 
				
			||||||
 | 
					#include <glm/gtc/matrix_transform.hpp>
 | 
				
			||||||
 | 
					#include <glm/gtc/type_ptr.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef VK_USE_PLATFORM_ANDROID_KHR
 | 
				
			||||||
 | 
						#define TINYGLTF_ANDROID_LOAD_FROM_ASSETS
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "tiny_gltf.h"
 | 
				
			||||||
 | 
					#include "glTFModel.h"
 | 
				
			||||||
 | 
					#include "vulkanexamplebase.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ENABLE_VALIDATION false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class VulkanExample : public VulkanExampleBase
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						bool wireframe = false;
 | 
				
			||||||
 | 
						bool normalMapping = true;
 | 
				
			||||||
 | 
						bool ToneMapping = true;
 | 
				
			||||||
 | 
						bool pbrEnabled = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						VulkanglTFModel glTFModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct ShaderData {
 | 
				
			||||||
 | 
							vks::Buffer buffer;
 | 
				
			||||||
 | 
							struct Values {
 | 
				
			||||||
 | 
								glm::mat4 projection;
 | 
				
			||||||
 | 
								glm::mat4 model;
 | 
				
			||||||
 | 
								glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, 5.0f, 1.0f);
 | 
				
			||||||
 | 
								glm::vec4 viewPos;
 | 
				
			||||||
 | 
								glm::vec4 bFlagSet = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
 | 
				
			||||||
 | 
							} values;
 | 
				
			||||||
 | 
							vks::Buffer skinSSBO;
 | 
				
			||||||
 | 
						} shaderData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct StagingBuffer {
 | 
				
			||||||
 | 
							VkBuffer buffer;
 | 
				
			||||||
 | 
							VkDeviceMemory memory;
 | 
				
			||||||
 | 
						} vertexStaging, indexStaging;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct Pipelines {
 | 
				
			||||||
 | 
							VkPipeline solid;
 | 
				
			||||||
 | 
							VkPipeline wireframe = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
							VkPipeline toneMapping = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
						} pipelines;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct  PipelineLayouts
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							VkPipelineLayout pbrLayout;
 | 
				
			||||||
 | 
							VkPipelineLayout tonemappingLayout;
 | 
				
			||||||
 | 
						} pipelineLayouts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						VkPipelineLayout pipelineLayout;
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						VkDescriptorSet descriptorSet;
 | 
				
			||||||
 | 
						VkDescriptorSet skinDescriptorSet;
 | 
				
			||||||
 | 
						VkDescriptorSet tonemappingDescriptorSet = VK_NULL_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct FrameBufferAttachment
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							VkImage image;
 | 
				
			||||||
 | 
							VkDeviceMemory deviceMemory;
 | 
				
			||||||
 | 
							VkImageView imageView;
 | 
				
			||||||
 | 
							VkFormat format;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							void destroy(VkDevice device)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								vkDestroyImage(device, image, nullptr);
 | 
				
			||||||
 | 
								vkDestroyImageView(device, imageView,nullptr);
 | 
				
			||||||
 | 
								vkFreeMemory(device, deviceMemory, nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct FrameBuffer
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int32_t width, height;
 | 
				
			||||||
 | 
							VkFramebuffer frameBuffer;
 | 
				
			||||||
 | 
							VkRenderPass renderPass;
 | 
				
			||||||
 | 
							void setSize(int32_t w, int32_t h)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								this->width = w;
 | 
				
			||||||
 | 
								this->height = h;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							void destroy(VkDevice device)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								vkDestroyFramebuffer(device, frameBuffer, nullptr);
 | 
				
			||||||
 | 
								vkDestroyRenderPass(device, renderPass, nullptr);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct PBRFrameBuffer {
 | 
				
			||||||
 | 
							FrameBufferAttachment color, depth;
 | 
				
			||||||
 | 
							FrameBuffer fbo;
 | 
				
			||||||
 | 
							bool bCreate = false;
 | 
				
			||||||
 | 
						} pbrFrameBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						VkSampler colorSampler;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct DescriptorSetLayouts {
 | 
				
			||||||
 | 
							VkDescriptorSetLayout matrices;
 | 
				
			||||||
 | 
							VkDescriptorSetLayout textures;
 | 
				
			||||||
 | 
							VkDescriptorSetLayout materialUniform;
 | 
				
			||||||
 | 
							VkDescriptorSetLayout ssbo;
 | 
				
			||||||
 | 
							VkDescriptorSetLayout jointMatrices;
 | 
				
			||||||
 | 
						} descriptorSetLayouts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct IBLTextures
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							vks::TextureCubeMap skyboxCube;
 | 
				
			||||||
 | 
							vks::TextureCubeMap irradianceCube;
 | 
				
			||||||
 | 
							vks::TextureCubeMap prefilteredCube;
 | 
				
			||||||
 | 
							vks::Texture2D lutBrdf;
 | 
				
			||||||
 | 
						} ibltextures;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct OffScreen
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							VkImage image;
 | 
				
			||||||
 | 
							VkImageView view;
 | 
				
			||||||
 | 
							VkDeviceMemory memory;
 | 
				
			||||||
 | 
							VkFramebuffer framebuffer;
 | 
				
			||||||
 | 
						} offscreen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct IrradiancePushBlock
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							glm::mat4 mvp;
 | 
				
			||||||
 | 
							// Sampling deltas
 | 
				
			||||||
 | 
							float deltaPhi = (2.0f * float(M_PI)) / 180.0f;
 | 
				
			||||||
 | 
							float deltaTheta = (0.5f * float(M_PI)) / 64.0f;
 | 
				
			||||||
 | 
						} irradiancePushBlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct PrefilterPushBlock {
 | 
				
			||||||
 | 
							glm::mat4 mvp;
 | 
				
			||||||
 | 
							float roughness;
 | 
				
			||||||
 | 
							uint32_t numSamples = 32u;
 | 
				
			||||||
 | 
						} prefilterPushBlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						VulkanglTFModel skyboxModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						VulkanExample();
 | 
				
			||||||
 | 
						~VulkanExample()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// Clean up used Vulkan resources
 | 
				
			||||||
 | 
							// Note : Inherited destructor cleans up resources stored in base class
 | 
				
			||||||
 | 
							vkDestroyPipeline(device, pipelines.solid, nullptr);
 | 
				
			||||||
 | 
							vkDestroyPipeline(device, pipelines.toneMapping, nullptr);
 | 
				
			||||||
 | 
							if (pipelines.wireframe != VK_NULL_HANDLE) {
 | 
				
			||||||
 | 
								vkDestroyPipeline(device, pipelines.wireframe, nullptr);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vkDestroyPipelineLayout(device, pipelineLayouts.pbrLayout, nullptr);
 | 
				
			||||||
 | 
							vkDestroyPipelineLayout(device, pipelineLayouts.tonemappingLayout, nullptr);
 | 
				
			||||||
 | 
							vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.matrices, nullptr);
 | 
				
			||||||
 | 
							vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.textures, nullptr);
 | 
				
			||||||
 | 
							vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.materialUniform, nullptr);
 | 
				
			||||||
 | 
							vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.ssbo, nullptr);
 | 
				
			||||||
 | 
							ibltextures.irradianceCube.destroy();
 | 
				
			||||||
 | 
							ibltextures.skyboxCube.destroy();
 | 
				
			||||||
 | 
							ibltextures.prefilteredCube.destroy();
 | 
				
			||||||
 | 
							ibltextures.lutBrdf.destroy();
 | 
				
			||||||
 | 
							pbrFrameBuffer.color.destroy(device);
 | 
				
			||||||
 | 
							pbrFrameBuffer.depth.destroy(device);
 | 
				
			||||||
 | 
							pbrFrameBuffer.fbo.destroy(device);
 | 
				
			||||||
 | 
							vkDestroySampler(device, colorSampler, nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							shaderData.buffer.destroy();
 | 
				
			||||||
 | 
							shaderData.skinSSBO.destroy();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void         loadglTFFile(std::string filename, VulkanglTFModel& model, bool bSkyboxFlag);
 | 
				
			||||||
 | 
						virtual void getEnabledFeatures();
 | 
				
			||||||
 | 
						void createAttachment(VkFormat format, VkImageUsageFlagBits usage, FrameBufferAttachment* attachment, uint32_t width, uint32_t height);
 | 
				
			||||||
 | 
						virtual void setupFrameBuffer();
 | 
				
			||||||
 | 
						void         buildCommandBuffers();
 | 
				
			||||||
 | 
						void         loadAssets();
 | 
				
			||||||
 | 
						void         setupDescriptors();
 | 
				
			||||||
 | 
						void         preparePipelines();
 | 
				
			||||||
 | 
						void CreateToneMappingPipeline();
 | 
				
			||||||
 | 
						void GenerateIrradianceCubemap();
 | 
				
			||||||
 | 
						void GeneratePrefilteredCubemap();
 | 
				
			||||||
 | 
						void GenerateBRDFLUT();
 | 
				
			||||||
 | 
						void         prepareUniformBuffers();
 | 
				
			||||||
 | 
						void         updateUniformBuffers();
 | 
				
			||||||
 | 
						void         prepare();
 | 
				
			||||||
 | 
						virtual void render();
 | 
				
			||||||
 | 
						virtual void viewChanged();
 | 
				
			||||||
 | 
						virtual void OnUpdateUIOverlay(vks::UIOverlay* overlay);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Loading…
	
		Reference in New Issue