ready for pbr and tonemapping
							parent
							
								
									39ba805802
								
							
						
					
					
						commit
						077d91916f
					
				|  | @ -21,27 +21,6 @@ | ||||||
| #include "homework1.h" | #include "homework1.h" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| VulkanglTFModel::~VulkanglTFModel() |  | ||||||
| 	{ |  | ||||||
| 		for (auto node : nodes) { |  | ||||||
| 			delete node; |  | ||||||
| 		} |  | ||||||
| 		// Release all Vulkan resources allocated for the model
 |  | ||||||
| 		vkDestroyBuffer(vulkanDevice->logicalDevice, vertices.buffer, nullptr); |  | ||||||
| 		vkFreeMemory(vulkanDevice->logicalDevice, vertices.memory, nullptr); |  | ||||||
| 		vkDestroyBuffer(vulkanDevice->logicalDevice, indices.buffer, nullptr); |  | ||||||
| 		vkFreeMemory(vulkanDevice->logicalDevice, indices.memory, nullptr); |  | ||||||
| 		for (Image image : images) { |  | ||||||
| 			vkDestroyImageView(vulkanDevice->logicalDevice, image.texture.view, nullptr); |  | ||||||
| 			vkDestroyImage(vulkanDevice->logicalDevice, image.texture.image, nullptr); |  | ||||||
| 			vkDestroySampler(vulkanDevice->logicalDevice, image.texture.sampler, nullptr); |  | ||||||
| 			vkFreeMemory(vulkanDevice->logicalDevice, image.texture.deviceMemory, nullptr); |  | ||||||
| 		} |  | ||||||
| 		for (Skin skin : skins) |  | ||||||
| 		{ |  | ||||||
| 			skin.ssbo.destroy(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 		glTF loading functions | 		glTF loading functions | ||||||
|  | @ -206,46 +185,50 @@ VulkanglTFModel::~VulkanglTFModel() | ||||||
| 	{ | 	{ | ||||||
| 		 | 		 | ||||||
| 		skins.resize(input.skins.size()); | 		skins.resize(input.skins.size()); | ||||||
| 
 | 		if (skins.size() > 0) | ||||||
| 		for (size_t i = 0; i < input.skins.size(); i++) |  | ||||||
| 		{ | 		{ | ||||||
| 			tinygltf::Skin glTFSkin = input.skins[i]; | 			for (size_t i = 0; i < input.skins.size(); i++) | ||||||
| 
 |  | ||||||
| 			skins[i].name = glTFSkin.name; |  | ||||||
| 			//follow the tree structure,find the root node of skeleton by index
 |  | ||||||
| 			skins[i].skeletonRoot = nodeFromIndex(glTFSkin.skeleton); |  | ||||||
| 
 |  | ||||||
| 			//join nodes
 |  | ||||||
| 			for (int jointIndex : glTFSkin.joints) |  | ||||||
| 			{ |  | ||||||
| 				Node* node = nodeFromIndex(jointIndex); |  | ||||||
| 				if (node) |  | ||||||
| 				{ | 				{ | ||||||
| 					skins[i].joints.push_back(node); | 					tinygltf::Skin glTFSkin = input.skins[i]; | ||||||
| 				} | 
 | ||||||
| 			} | 					skins[i].name = glTFSkin.name; | ||||||
| 			//get the inverse bind matrices
 | 					//follow the tree structure,find the root node of skeleton by index
 | ||||||
| 			if (glTFSkin.inverseBindMatrices > -1) | 					skins[i].skeletonRoot = nodeFromIndex(glTFSkin.skeleton); | ||||||
| 			{ | 
 | ||||||
| 				const tinygltf::Accessor& accessor = input.accessors[glTFSkin.inverseBindMatrices]; | 					//join nodes
 | ||||||
| 				const tinygltf::BufferView& bufferview = input.bufferViews[accessor.bufferView]; | 					for (int jointIndex : glTFSkin.joints) | ||||||
| 				const tinygltf::Buffer& buffer = input.buffers[bufferview.buffer]; | 						{ | ||||||
| 				skins[i].inverseBindMatrices.resize(accessor.count); | 							Node* node = nodeFromIndex(jointIndex); | ||||||
| 				memcpy(skins[i].inverseBindMatrices.data(), &buffer.data[accessor.byteOffset + bufferview.byteOffset], accessor.count * sizeof(glm::mat4)); | 							if (node) | ||||||
|  | 							{ | ||||||
|  | 								skins[i].joints.push_back(node); | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 					//get the inverse bind matrices
 | ||||||
|  | 					if (glTFSkin.inverseBindMatrices > -1) | ||||||
|  | 						{ | ||||||
|  | 							const tinygltf::Accessor& accessor = input.accessors[glTFSkin.inverseBindMatrices]; | ||||||
|  | 							const tinygltf::BufferView& bufferview = input.bufferViews[accessor.bufferView]; | ||||||
|  | 							const tinygltf::Buffer& buffer = input.buffers[bufferview.buffer]; | ||||||
|  | 							skins[i].inverseBindMatrices.resize(accessor.count); | ||||||
|  | 							memcpy(skins[i].inverseBindMatrices.data(), &buffer.data[accessor.byteOffset + bufferview.byteOffset], accessor.count * sizeof(glm::mat4)); | ||||||
|  | 
 | ||||||
|  | 							//create a host visible shader buffer to store inverse bind matrices for this skin
 | ||||||
|  | 							VK_CHECK_RESULT( | ||||||
|  | 								vulkanDevice->createBuffer( | ||||||
|  | 								VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, | ||||||
|  | 								VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, | ||||||
|  | 								&skins[i].ssbo, | ||||||
|  | 								sizeof(glm::mat4) * skins[i].inverseBindMatrices.size(), | ||||||
|  | 								skins[i].inverseBindMatrices.data())); | ||||||
|  | 							VK_CHECK_RESULT(skins[i].ssbo.map()); | ||||||
|  | 						} | ||||||
|  | 				} | ||||||
| 
 | 
 | ||||||
| 				//create a host visible shader buffer to store inverse bind matrices for this skin
 |  | ||||||
| 				VK_CHECK_RESULT( |  | ||||||
| 					vulkanDevice->createBuffer( |  | ||||||
| 					VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, |  | ||||||
| 					VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, |  | ||||||
| 					&skins[i].ssbo, |  | ||||||
| 					sizeof(glm::mat4) * skins[i].inverseBindMatrices.size(), |  | ||||||
| 					skins[i].inverseBindMatrices.data())); |  | ||||||
| 				VK_CHECK_RESULT(skins[i].ssbo.map()); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	//glTF nodes loading helper function
 | 	//glTF nodes loading helper function
 | ||||||
|  | @ -542,6 +525,7 @@ VulkanglTFModel::~VulkanglTFModel() | ||||||
| 					if (channel.path == "translation") | 					if (channel.path == "translation") | ||||||
| 					{ | 					{ | ||||||
| 						channel.node->translation = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio); | 						channel.node->translation = glm::mix(sampler.outputsVec4[i], sampler.outputsVec4[i + 1], ratio); | ||||||
|  | 						channel.node->bAnimateNode = true; | ||||||
| 					} | 					} | ||||||
| 					if (channel.path == "rotation") | 					if (channel.path == "rotation") | ||||||
| 					{ | 					{ | ||||||
|  | @ -596,7 +580,7 @@ VulkanglTFModel::~VulkanglTFModel() | ||||||
| 			} | 			} | ||||||
| 			// Pass the final matrix to the vertex shader using push constants
 | 			// Pass the final matrix to the vertex shader using push constants
 | ||||||
| 			vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &nodeMatrix); | 			vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &nodeMatrix); | ||||||
| 			if (skins.size() != 0) | 			if (skins.size() > 0) | ||||||
| 			{ | 			{ | ||||||
| 				vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &skins[node.skin].descriptorSet, 0, nullptr); | 				vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 1, 1, &skins[node.skin].descriptorSet, 0, nullptr); | ||||||
| 			} | 			} | ||||||
|  | @ -645,24 +629,7 @@ VulkanExample::VulkanExample(): | ||||||
| 		camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); | 		camera.setPerspective(60.0f, (float)width / (float)height, 0.1f, 256.0f); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| VulkanExample::~VulkanExample() |  | ||||||
| 	{ |  | ||||||
| 		// Clean up used Vulkan resources
 |  | ||||||
| 		// Note : Inherited destructor cleans up resources stored in base class
 |  | ||||||
| 		vkDestroyPipeline(device, pipelines.solid, nullptr); |  | ||||||
| 		if (pipelines.wireframe != VK_NULL_HANDLE) { |  | ||||||
| 			vkDestroyPipeline(device, pipelines.wireframe, nullptr); |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		vkDestroyPipelineLayout(device, pipelineLayout, nullptr); |  | ||||||
| 		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.matrices, nullptr); |  | ||||||
| 		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.textures, nullptr); |  | ||||||
| 		vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.jointMatrices, nullptr); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 		shaderData.buffer.destroy(); |  | ||||||
| 		 |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| void VulkanExample::getEnabledFeatures() | void VulkanExample::getEnabledFeatures() | ||||||
| { | { | ||||||
|  | @ -747,6 +714,7 @@ void VulkanExample::getEnabledFeatures() | ||||||
| 			 | 			 | ||||||
| 			glTFModel.loadAnimations(glTFInput); | 			glTFModel.loadAnimations(glTFInput); | ||||||
| 			// update joint in nodes
 | 			// update joint in nodes
 | ||||||
|  | 			 | ||||||
| 			for (auto node : glTFModel.nodes) | 			for (auto node : glTFModel.nodes) | ||||||
| 			{ | 			{ | ||||||
| 				glTFModel.updateJoints(node); | 				glTFModel.updateJoints(node); | ||||||
|  | @ -1014,7 +982,7 @@ void VulkanExample::getEnabledFeatures() | ||||||
| 		} | 		} | ||||||
| 		if (!paused) | 		if (!paused) | ||||||
| 		{ | 		{ | ||||||
| 			glTFModel.updateAnimation(frameTimer,shaderData.buffer); | 			glTFModel.updateAnimation(frameTimer,shaderData.skinSSBO); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -91,7 +91,7 @@ public: | ||||||
| 		} | 		} | ||||||
| 		glm::mat4 matrix; | 		glm::mat4 matrix; | ||||||
| 		bool bAnimateNode = false; | 		bool bAnimateNode = false; | ||||||
| 		/*
 | 		 | ||||||
| 		~Node() { | 		~Node() { | ||||||
| 			for (auto& child : children) { | 			for (auto& child : children) { | ||||||
| 				delete child; | 				delete child; | ||||||
|  | @ -99,15 +99,28 @@ public: | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 		 | 		 | ||||||
| 		*/ |  | ||||||
| 		 |  | ||||||
| 
 | 
 | ||||||
| 	}; | 	}; | ||||||
|  | 	// material data for pbr
 | ||||||
|  | 	struct MaterialData | ||||||
|  | 	{ | ||||||
|  | 		vks::Buffer buffer; | ||||||
|  | 		VkDescriptorSet descriptorSet; | ||||||
|  | 		struct Values | ||||||
|  | 		{ | ||||||
|  | 			glm::vec3 emissiveFactor; | ||||||
|  | 			glm::vec4 baseColorFactor; | ||||||
|  | 		}values; | ||||||
| 
 | 
 | ||||||
|  | 	}; | ||||||
| 	// A glTF material stores information in e.g. the texture that is attached to it and colors
 | 	// A glTF material stores information in e.g. the texture that is attached to it and colors
 | ||||||
| 	struct Material { | 	struct Material { | ||||||
| 		glm::vec4 baseColorFactor = glm::vec4(1.0f); | 		glm::vec4 baseColorFactor = glm::vec4(1.0f); | ||||||
| 		uint32_t baseColorTextureIndex; | 		uint32_t baseColorTextureIndex; | ||||||
|  | 		uint32_t normalMapTextureIndex; | ||||||
|  | 		uint32_t matalicRoughTextureIndex; | ||||||
|  | 		int32_t emissiveTextureIndex = -1; | ||||||
|  | 		MaterialData materialData; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	// Contains the texture for a single glTF image
 | 	// Contains the texture for a single glTF image
 | ||||||
|  | @ -177,7 +190,27 @@ public: | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	 //VulkanglTFModel();
 | 	 //VulkanglTFModel();
 | ||||||
| 	~VulkanglTFModel(); | 	~VulkanglTFModel() | ||||||
|  | 	{ | ||||||
|  | 		for (auto node : nodes) { | ||||||
|  | 			delete node; | ||||||
|  | 		} | ||||||
|  | 		// Release all Vulkan resources allocated for the model
 | ||||||
|  | 		vkDestroyBuffer(vulkanDevice->logicalDevice, vertices.buffer, nullptr); | ||||||
|  | 		vkFreeMemory(vulkanDevice->logicalDevice, vertices.memory, nullptr); | ||||||
|  | 		vkDestroyBuffer(vulkanDevice->logicalDevice, indices.buffer, nullptr); | ||||||
|  | 		vkFreeMemory(vulkanDevice->logicalDevice, indices.memory, nullptr); | ||||||
|  | 		for (auto& material : materials) | ||||||
|  | 		{ | ||||||
|  | 			material.materialData.buffer.destroy(); | ||||||
|  | 		} | ||||||
|  | 		for (Image image : images) { | ||||||
|  | 			vkDestroyImageView(vulkanDevice->logicalDevice, image.texture.view, nullptr); | ||||||
|  | 			vkDestroyImage(vulkanDevice->logicalDevice, image.texture.image, nullptr); | ||||||
|  | 			vkDestroySampler(vulkanDevice->logicalDevice, image.texture.sampler, nullptr); | ||||||
|  | 			vkFreeMemory(vulkanDevice->logicalDevice, image.texture.deviceMemory, nullptr); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	void      loadImages(tinygltf::Model& input); | 	void      loadImages(tinygltf::Model& input); | ||||||
| 	void      loadTextures(tinygltf::Model& input); | 	void      loadTextures(tinygltf::Model& input); | ||||||
| 	void      loadMaterials(tinygltf::Model& input); | 	void      loadMaterials(tinygltf::Model& input); | ||||||
|  | @ -198,6 +231,9 @@ class VulkanExample : public VulkanExampleBase | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	bool wireframe = false; | 	bool wireframe = false; | ||||||
|  | 	bool normalMapping = true; | ||||||
|  | 	bool ToneMapping = true; | ||||||
|  | 	bool pbrEnabled = true; | ||||||
| 
 | 
 | ||||||
| 	VulkanglTFModel glTFModel; | 	VulkanglTFModel glTFModel; | ||||||
| 
 | 
 | ||||||
|  | @ -208,28 +244,117 @@ public: | ||||||
| 			glm::mat4 model; | 			glm::mat4 model; | ||||||
| 			glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, 5.0f, 1.0f); | 			glm::vec4 lightPos = glm::vec4(5.0f, 5.0f, 5.0f, 1.0f); | ||||||
| 			glm::vec4 viewPos; | 			glm::vec4 viewPos; | ||||||
|  | 			glm::vec4 bFlagSet = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f); | ||||||
| 		} values; | 		} values; | ||||||
| 		vks::Buffer animationBuffer; | 		vks::Buffer skinSSBO; | ||||||
| 	} shaderData; | 	} shaderData; | ||||||
| 
 | 
 | ||||||
| 	struct Pipelines { | 	struct Pipelines { | ||||||
| 		VkPipeline solid; | 		VkPipeline solid; | ||||||
| 		VkPipeline wireframe = VK_NULL_HANDLE; | 		VkPipeline wireframe = VK_NULL_HANDLE; | ||||||
|  | 		VkPipeline toneMapping = VK_NULL_HANDLE; | ||||||
| 	} pipelines; | 	} pipelines; | ||||||
| 
 | 
 | ||||||
|  | 	struct  PipelineLayouts | ||||||
|  | 	{ | ||||||
|  | 		VkPipelineLayout pbrLayout; | ||||||
|  | 		VkPipelineLayout tonemappingLayout; | ||||||
|  | 	} pipelineLayouts; | ||||||
|  | 
 | ||||||
| 	VkPipelineLayout pipelineLayout; | 	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 { | 	struct DescriptorSetLayouts { | ||||||
| 		VkDescriptorSetLayout matrices; | 		VkDescriptorSetLayout matrices; | ||||||
| 		VkDescriptorSetLayout textures; | 		VkDescriptorSetLayout textures; | ||||||
|  | 		VkDescriptorSetLayout materialUniform; | ||||||
|  | 		VkDescriptorSetLayout ssbo; | ||||||
| 		VkDescriptorSetLayout jointMatrices; | 		VkDescriptorSetLayout jointMatrices; | ||||||
| 	} descriptorSetLayouts; | 	} descriptorSetLayouts; | ||||||
| 
 | 
 | ||||||
| 	VkDescriptorSet descriptorSet; | 	struct IBLTextures | ||||||
|  | 	{ | ||||||
|  | 		vks::TextureCubeMap skyboxCube; | ||||||
|  | 		vks::TextureCubeMap irradianceCube; | ||||||
|  | 		vks::TextureCubeMap prefilteredCube; | ||||||
|  | 		vks::Texture2D lutBrdf; | ||||||
|  | 	} ibltextures; | ||||||
|  | 
 | ||||||
|  | 	VulkanglTFModel skyboxModel; | ||||||
| 
 | 
 | ||||||
| 	VulkanExample(); | 	VulkanExample(); | ||||||
| 	~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); | 	void         loadglTFFile(std::string filename); | ||||||
| 	virtual void getEnabledFeatures(); | 	virtual void getEnabledFeatures(); | ||||||
| 	void         buildCommandBuffers(); | 	void         buildCommandBuffers(); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue