2016-03-15 22:38:32 +01:00
/*
* Vulkan Example - CPU based fire particle system
*
* Copyright ( C ) 2016 by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <assert.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/matrix_inverse.hpp>
# include <vulkan/vulkan.h>
# include "vulkanexamplebase.h"
2017-02-12 10:44:51 +01:00
# include "VulkanBuffer.hpp"
2017-02-11 10:15:53 +01:00
# include "VulkanTexture.hpp"
# include "VulkanModel.hpp"
2016-03-15 22:38:32 +01:00
# define VERTEX_BUFFER_BIND_ID 0
# define ENABLE_VALIDATION false
# define PARTICLE_COUNT 512
# define PARTICLE_SIZE 10.0f
# define FLAME_RADIUS 8.0f
# define PARTICLE_TYPE_FLAME 0
# define PARTICLE_TYPE_SMOKE 1
struct Particle {
glm : : vec4 pos ;
glm : : vec4 color ;
float alpha ;
float size ;
float rotation ;
uint32_t type ;
// Attributes not used in shader
glm : : vec4 vel ;
float rotationSpeed ;
} ;
class VulkanExample : public VulkanExampleBase
{
public :
struct {
struct {
2017-02-09 21:55:35 +01:00
vks : : Texture2D smoke ;
vks : : Texture2D fire ;
2017-02-11 10:15:53 +01:00
// Use a custom sampler to change sampler attributes required for rotating the uvs in the shader for alpha blended textures
2016-03-15 22:53:23 +01:00
VkSampler sampler ;
2016-03-15 22:38:32 +01:00
} particles ;
struct {
2017-02-09 21:55:35 +01:00
vks : : Texture2D colorMap ;
vks : : Texture2D normalMap ;
2016-03-15 22:38:32 +01:00
} floor ;
} textures ;
2017-02-11 10:15:53 +01:00
// Vertex layout for the models
vks : : VertexLayout vertexLayout = vks : : VertexLayout ( {
vks : : VERTEX_COMPONENT_POSITION ,
vks : : VERTEX_COMPONENT_UV ,
vks : : VERTEX_COMPONENT_NORMAL ,
vks : : VERTEX_COMPONENT_TANGENT ,
vks : : VERTEX_COMPONENT_BITANGENT ,
} ) ;
2016-03-15 22:38:32 +01:00
struct {
2017-02-11 10:15:53 +01:00
vks : : Model environment ;
} models ;
2016-03-15 22:38:32 +01:00
glm : : vec3 emitterPos = glm : : vec3 ( 0.0f , - FLAME_RADIUS + 2.0f , 0.0f ) ;
glm : : vec3 minVel = glm : : vec3 ( - 3.0f , 0.5f , - 3.0f ) ;
glm : : vec3 maxVel = glm : : vec3 ( 3.0f , 7.0f , 3.0f ) ;
struct {
2016-03-17 20:28:07 +01:00
VkBuffer buffer ;
VkDeviceMemory memory ;
// Store the mapped address of the particle data for reuse
void * mappedMemory ;
// Size of the particle buffer in bytes
size_t size ;
} particles ;
2016-03-15 22:38:32 +01:00
struct {
2017-02-12 10:44:51 +01:00
vks : : Buffer fire ;
vks : : Buffer environment ;
2016-12-24 12:48:01 +01:00
} uniformBuffers ;
2016-03-15 22:38:32 +01:00
2016-12-24 12:48:01 +01:00
struct UBOVS {
2016-03-15 22:38:32 +01:00
glm : : mat4 projection ;
glm : : mat4 model ;
glm : : vec2 viewportDim ;
float pointSize = PARTICLE_SIZE ;
} uboVS ;
2016-12-24 12:48:01 +01:00
struct UBOEnv {
2016-03-15 22:38:32 +01:00
glm : : mat4 projection ;
glm : : mat4 model ;
glm : : mat4 normal ;
glm : : vec4 lightPos = glm : : vec4 ( 0.0f , 0.0f , 0.0f , 0.0f ) ;
glm : : vec4 cameraPos ;
} uboEnv ;
struct {
VkPipeline particles ;
VkPipeline environment ;
} pipelines ;
VkPipelineLayout pipelineLayout ;
VkDescriptorSetLayout descriptorSetLayout ;
2017-02-11 10:15:53 +01:00
struct {
VkDescriptorSet particles ;
VkDescriptorSet environment ;
} descriptorSets ;
2016-03-15 22:38:32 +01:00
std : : vector < Particle > particleBuffer ;
VulkanExample ( ) : VulkanExampleBase ( ENABLE_VALIDATION )
{
2016-06-05 20:14:04 +02:00
zoom = - 75.0f ;
2016-03-19 20:02:08 +01:00
rotation = { - 15.0f , 45.0f , 0.0f } ;
2016-06-05 20:14:04 +02:00
enableTextOverlay = true ;
2017-03-17 21:34:32 +01:00
title = " Vulkan Example - CPU particle system " ;
2016-03-17 20:28:07 +01:00
zoomSpeed * = 1.5f ;
2016-03-15 22:38:32 +01:00
timerSpeed * = 8.0f ;
srand ( time ( NULL ) ) ;
}
~ VulkanExample ( )
{
// Clean up used Vulkan resources
// Note : Inherited destructor cleans up resources stored in base class
2017-02-09 21:55:35 +01:00
textures . particles . smoke . destroy ( ) ;
textures . particles . fire . destroy ( ) ;
textures . floor . colorMap . destroy ( ) ;
textures . floor . normalMap . destroy ( ) ;
2016-03-15 22:38:32 +01:00
vkDestroyPipeline ( device , pipelines . particles , nullptr ) ;
vkDestroyPipeline ( device , pipelines . environment , nullptr ) ;
vkDestroyPipelineLayout ( device , pipelineLayout , nullptr ) ;
vkDestroyDescriptorSetLayout ( device , descriptorSetLayout , nullptr ) ;
2016-03-17 20:28:07 +01:00
vkUnmapMemory ( device , particles . memory ) ;
vkDestroyBuffer ( device , particles . buffer , nullptr ) ;
vkFreeMemory ( device , particles . memory , nullptr ) ;
2016-03-15 22:38:32 +01:00
2016-12-24 12:48:01 +01:00
uniformBuffers . environment . destroy ( ) ;
uniformBuffers . fire . destroy ( ) ;
2016-03-15 22:38:32 +01:00
2017-02-11 10:15:53 +01:00
models . environment . destroy ( ) ;
2016-03-15 22:53:23 +01:00
vkDestroySampler ( device , textures . particles . sampler , nullptr ) ;
2016-03-15 22:38:32 +01:00
}
void buildCommandBuffers ( )
{
2017-02-12 11:12:42 +01:00
VkCommandBufferBeginInfo cmdBufInfo = vks : : initializers : : commandBufferBeginInfo ( ) ;
2016-03-15 22:38:32 +01:00
VkClearValue clearValues [ 2 ] ;
clearValues [ 0 ] . color = defaultClearColor ;
clearValues [ 1 ] . depthStencil = { 1.0f , 0 } ;
2017-02-12 11:12:42 +01:00
VkRenderPassBeginInfo renderPassBeginInfo = vks : : initializers : : renderPassBeginInfo ( ) ;
2016-03-15 22:38:32 +01:00
renderPassBeginInfo . renderPass = renderPass ;
renderPassBeginInfo . renderArea . offset . x = 0 ;
renderPassBeginInfo . renderArea . offset . y = 0 ;
renderPassBeginInfo . renderArea . extent . width = width ;
renderPassBeginInfo . renderArea . extent . height = height ;
renderPassBeginInfo . clearValueCount = 2 ;
renderPassBeginInfo . pClearValues = clearValues ;
for ( int32_t i = 0 ; i < drawCmdBuffers . size ( ) ; + + i )
{
// Set target frame buffer
renderPassBeginInfo . framebuffer = frameBuffers [ i ] ;
2016-06-05 20:14:04 +02:00
VK_CHECK_RESULT ( vkBeginCommandBuffer ( drawCmdBuffers [ i ] , & cmdBufInfo ) ) ;
2016-03-15 22:38:32 +01:00
vkCmdBeginRenderPass ( drawCmdBuffers [ i ] , & renderPassBeginInfo , VK_SUBPASS_CONTENTS_INLINE ) ;
2017-02-12 11:12:42 +01:00
VkViewport viewport = vks : : initializers : : viewport ( ( float ) width , ( float ) height , 0.0f , 1.0f ) ;
2016-03-15 22:38:32 +01:00
vkCmdSetViewport ( drawCmdBuffers [ i ] , 0 , 1 , & viewport ) ;
2017-02-12 11:12:42 +01:00
VkRect2D scissor = vks : : initializers : : rect2D ( width , height , 0 , 0 ) ;
2016-03-15 22:38:32 +01:00
vkCmdSetScissor ( drawCmdBuffers [ i ] , 0 , 1 , & scissor ) ;
2017-02-11 10:15:53 +01:00
VkDeviceSize offsets [ 1 ] = { 0 } ;
2016-03-15 22:38:32 +01:00
2017-02-11 10:15:53 +01:00
// Environment
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & descriptorSets . environment , 0 , NULL ) ;
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelines . environment ) ;
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , VERTEX_BUFFER_BIND_ID , 1 , & models . environment . vertices . buffer , offsets ) ;
vkCmdBindIndexBuffer ( drawCmdBuffers [ i ] , models . environment . indices . buffer , 0 , VK_INDEX_TYPE_UINT32 ) ;
vkCmdDrawIndexed ( drawCmdBuffers [ i ] , models . environment . indexCount , 1 , 0 , 0 , 0 ) ;
// Particle system (no index buffer)
vkCmdBindDescriptorSets ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelineLayout , 0 , 1 , & descriptorSets . particles , 0 , NULL ) ;
2016-03-15 22:38:32 +01:00
vkCmdBindPipeline ( drawCmdBuffers [ i ] , VK_PIPELINE_BIND_POINT_GRAPHICS , pipelines . particles ) ;
2016-03-17 20:28:07 +01:00
vkCmdBindVertexBuffers ( drawCmdBuffers [ i ] , VERTEX_BUFFER_BIND_ID , 1 , & particles . buffer , offsets ) ;
2016-03-15 22:38:32 +01:00
vkCmdDraw ( drawCmdBuffers [ i ] , PARTICLE_COUNT , 1 , 0 , 0 ) ;
vkCmdEndRenderPass ( drawCmdBuffers [ i ] ) ;
2016-06-05 20:14:04 +02:00
VK_CHECK_RESULT ( vkEndCommandBuffer ( drawCmdBuffers [ i ] ) ) ;
2016-03-15 22:38:32 +01:00
}
}
float rnd ( float range )
{
2017-02-11 10:15:53 +01:00
return range * ( rand ( ) / float ( RAND_MAX ) ) ;
2016-03-15 22:38:32 +01:00
}
void initParticle ( Particle * particle , glm : : vec3 emitterPos )
{
particle - > vel = glm : : vec4 ( 0.0f , minVel . y + rnd ( maxVel . y - minVel . y ) , 0.0f , 0.0f ) ;
particle - > alpha = rnd ( 0.75f ) ;
particle - > size = 1.0f + rnd ( 0.5f ) ;
particle - > color = glm : : vec4 ( 1.0f ) ;
particle - > type = PARTICLE_TYPE_FLAME ;
2017-02-11 10:15:53 +01:00
particle - > rotation = rnd ( 2.0f * float ( M_PI ) ) ;
2016-03-15 22:38:32 +01:00
particle - > rotationSpeed = rnd ( 2.0f ) - rnd ( 2.0f ) ;
// Get random sphere point
2017-02-11 10:15:53 +01:00
float theta = rnd ( 2.0f * float ( M_PI ) ) ;
float phi = rnd ( float ( M_PI ) ) - float ( M_PI ) / 2.0f ;
2016-03-15 22:38:32 +01:00
float r = rnd ( FLAME_RADIUS ) ;
particle - > pos . x = r * cos ( theta ) * cos ( phi ) ;
particle - > pos . y = r * sin ( phi ) ;
particle - > pos . z = r * sin ( theta ) * cos ( phi ) ;
particle - > pos + = glm : : vec4 ( emitterPos , 0.0f ) ;
}
void transitionParticle ( Particle * particle )
{
switch ( particle - > type )
{
case PARTICLE_TYPE_FLAME :
// Flame particles have a chance of turning into smoke
if ( rnd ( 1.0f ) < 0.05f )
{
particle - > alpha = 0.0f ;
particle - > color = glm : : vec4 ( 0.25f + rnd ( 0.25f ) ) ;
particle - > pos . x * = 0.5f ;
particle - > pos . z * = 0.5f ;
particle - > vel = glm : : vec4 ( rnd ( 1.0f ) - rnd ( 1.0f ) , ( minVel . y * 2 ) + rnd ( maxVel . y - minVel . y ) , rnd ( 1.0f ) - rnd ( 1.0f ) , 0.0f ) ;
particle - > size = 1.0f + rnd ( 0.5f ) ;
particle - > rotationSpeed = rnd ( 1.0f ) - rnd ( 1.0f ) ;
particle - > type = PARTICLE_TYPE_SMOKE ;
}
else
{
initParticle ( particle , emitterPos ) ;
}
break ;
case PARTICLE_TYPE_SMOKE :
// Respawn at end of life
initParticle ( particle , emitterPos ) ;
break ;
}
}
void prepareParticles ( )
{
particleBuffer . resize ( PARTICLE_COUNT ) ;
for ( auto & particle : particleBuffer )
{
initParticle ( & particle , emitterPos ) ;
particle . alpha = 1.0f - ( abs ( particle . pos . y ) / ( FLAME_RADIUS * 2.0f ) ) ;
}
2016-03-17 20:28:07 +01:00
particles . size = particleBuffer . size ( ) * sizeof ( Particle ) ;
2017-01-07 20:46:28 +01:00
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
2016-03-15 22:38:32 +01:00
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT ,
2017-01-07 20:46:28 +01:00
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
2016-03-17 20:28:07 +01:00
particles . size ,
& particles . buffer ,
2017-01-07 20:46:28 +01:00
& particles . memory ,
particleBuffer . data ( ) ) ) ;
2016-03-17 20:28:07 +01:00
// Map the memory and store the pointer for reuse
2016-06-05 20:14:04 +02:00
VK_CHECK_RESULT ( vkMapMemory ( device , particles . memory , 0 , particles . size , 0 , & particles . mappedMemory ) ) ;
2016-03-15 22:38:32 +01:00
}
void updateParticles ( )
{
float particleTimer = frameTimer * 0.45f ;
for ( auto & particle : particleBuffer )
{
switch ( particle . type )
{
case PARTICLE_TYPE_FLAME :
particle . pos . y - = particle . vel . y * particleTimer * 3.5f ;
particle . alpha + = particleTimer * 2.5f ;
particle . size - = particleTimer * 0.5f ;
break ;
case PARTICLE_TYPE_SMOKE :
particle . pos - = particle . vel * frameTimer * 1.0f ;
particle . alpha + = particleTimer * 1.25f ;
particle . size + = particleTimer * 0.125f ;
particle . color - = particleTimer * 0.05f ;
break ;
}
particle . rotation + = particleTimer * particle . rotationSpeed ;
// Transition particle state
if ( particle . alpha > 2.0f )
{
transitionParticle ( & particle ) ;
}
}
size_t size = particleBuffer . size ( ) * sizeof ( Particle ) ;
2016-03-17 20:28:07 +01:00
memcpy ( particles . mappedMemory , particleBuffer . data ( ) , size ) ;
2016-03-15 22:38:32 +01:00
}
2017-02-11 10:15:53 +01:00
void loadAssets ( )
2016-03-15 22:38:32 +01:00
{
2017-03-17 21:34:32 +01:00
// Textures
std : : string texFormatSuffix ;
VkFormat texFormat ;
// Get supported compressed texture format
if ( vulkanDevice - > features . textureCompressionBC ) {
texFormatSuffix = " _bc3_unorm " ;
texFormat = VK_FORMAT_BC3_UNORM_BLOCK ;
}
else if ( vulkanDevice - > features . textureCompressionASTC_LDR ) {
texFormatSuffix = " _astc_8x8_unorm " ;
texFormat = VK_FORMAT_ASTC_8x8_UNORM_BLOCK ;
}
else if ( vulkanDevice - > features . textureCompressionETC2 ) {
texFormatSuffix = " _etc2_unorm " ;
texFormat = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK ;
}
else {
vks : : tools : : exitFatal ( " Device does not support any compressed texture format! " , " Error " ) ;
}
2016-03-15 22:38:32 +01:00
// Particles
2017-03-17 21:34:32 +01:00
textures . particles . smoke . loadFromFile ( getAssetPath ( ) + " textures/particle_smoke.ktx " , VK_FORMAT_R8G8B8A8_UNORM , vulkanDevice , queue ) ;
textures . particles . fire . loadFromFile ( getAssetPath ( ) + " textures/particle_fire.ktx " , VK_FORMAT_R8G8B8A8_UNORM , vulkanDevice , queue ) ;
2016-03-15 22:53:23 +01:00
2016-03-15 22:38:32 +01:00
// Floor
2017-03-17 21:34:32 +01:00
textures . floor . colorMap . loadFromFile ( getAssetPath ( ) + " textures/fireplace_colormap " + texFormatSuffix + " .ktx " , texFormat , vulkanDevice , queue ) ;
textures . floor . normalMap . loadFromFile ( getAssetPath ( ) + " textures/fireplace_normalmap " + texFormatSuffix + " .ktx " , texFormat , vulkanDevice , queue ) ;
2016-03-15 22:53:23 +01:00
// Create a custom sampler to be used with the particle textures
// Create sampler
2017-02-12 11:12:42 +01:00
VkSamplerCreateInfo samplerCreateInfo = vks : : initializers : : samplerCreateInfo ( ) ;
2016-03-15 22:53:23 +01:00
samplerCreateInfo . magFilter = VK_FILTER_LINEAR ;
samplerCreateInfo . minFilter = VK_FILTER_LINEAR ;
samplerCreateInfo . mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR ;
// Different address mode
samplerCreateInfo . addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ;
samplerCreateInfo . addressModeV = samplerCreateInfo . addressModeU ;
samplerCreateInfo . addressModeW = samplerCreateInfo . addressModeU ;
samplerCreateInfo . mipLodBias = 0.0f ;
samplerCreateInfo . compareOp = VK_COMPARE_OP_NEVER ;
samplerCreateInfo . minLod = 0.0f ;
// Both particle textures have the same number of mip maps
2017-02-11 10:15:53 +01:00
samplerCreateInfo . maxLod = float ( textures . particles . fire . mipLevels ) ;
2016-03-15 22:53:23 +01:00
// Enable anisotropic filtering
2017-06-17 16:07:38 +02:00
samplerCreateInfo . maxAnisotropy = 8.0f ;
2016-03-15 22:53:23 +01:00
samplerCreateInfo . anisotropyEnable = VK_TRUE ;
// Use a different border color (than the normal texture loader) for additive blending
samplerCreateInfo . borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK ;
2016-06-05 20:14:04 +02:00
VK_CHECK_RESULT ( vkCreateSampler ( device , & samplerCreateInfo , nullptr , & textures . particles . sampler ) ) ;
2016-03-15 22:38:32 +01:00
2017-02-11 10:15:53 +01:00
models . environment . loadFromFile ( getAssetPath ( ) + " models/fireplace.obj " , vertexLayout , 10.0f , vulkanDevice , queue ) ;
2016-03-15 22:38:32 +01:00
}
void setupDescriptorPool ( )
{
// Example uses one ubo and one image sampler
std : : vector < VkDescriptorPoolSize > poolSizes =
{
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 2 ) ,
vks : : initializers : : descriptorPoolSize ( VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 4 )
2016-03-15 22:38:32 +01:00
} ;
VkDescriptorPoolCreateInfo descriptorPoolInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorPoolCreateInfo (
2016-03-15 22:38:32 +01:00
poolSizes . size ( ) ,
poolSizes . data ( ) ,
2 ) ;
2016-06-05 20:14:04 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorPool ( device , & descriptorPoolInfo , nullptr , & descriptorPool ) ) ;
2016-03-15 22:38:32 +01:00
}
void setupDescriptorSetLayout ( )
{
std : : vector < VkDescriptorSetLayoutBinding > setLayoutBindings =
{
// Binding 0 : Vertex shader uniform buffer
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutBinding (
2016-03-15 22:38:32 +01:00
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
VK_SHADER_STAGE_VERTEX_BIT ,
0 ) ,
// Binding 1 : Fragment shader image sampler
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutBinding (
2016-03-15 22:38:32 +01:00
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
VK_SHADER_STAGE_FRAGMENT_BIT ,
1 ) ,
// Binding 1 : Fragment shader image sampler
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutBinding (
2016-03-15 22:38:32 +01:00
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
VK_SHADER_STAGE_FRAGMENT_BIT ,
2 )
} ;
VkDescriptorSetLayoutCreateInfo descriptorLayout =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetLayoutCreateInfo (
2016-03-15 22:38:32 +01:00
setLayoutBindings . data ( ) ,
setLayoutBindings . size ( ) ) ;
2016-06-05 20:14:04 +02:00
VK_CHECK_RESULT ( vkCreateDescriptorSetLayout ( device , & descriptorLayout , nullptr , & descriptorSetLayout ) ) ;
2016-03-15 22:38:32 +01:00
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineLayoutCreateInfo (
2016-03-15 22:38:32 +01:00
& descriptorSetLayout ,
1 ) ;
2016-06-05 20:14:04 +02:00
VK_CHECK_RESULT ( vkCreatePipelineLayout ( device , & pPipelineLayoutCreateInfo , nullptr , & pipelineLayout ) ) ;
2016-03-15 22:38:32 +01:00
}
void setupDescriptorSets ( )
{
2016-12-24 12:48:01 +01:00
std : : vector < VkWriteDescriptorSet > writeDescriptorSets ;
2016-03-15 22:38:32 +01:00
VkDescriptorSetAllocateInfo allocInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorSetAllocateInfo (
2016-03-15 22:38:32 +01:00
descriptorPool ,
& descriptorSetLayout ,
1 ) ;
2017-02-11 10:15:53 +01:00
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSets . particles ) ) ;
2016-03-15 22:38:32 +01:00
// Image descriptor for the color map texture
VkDescriptorImageInfo texDescriptorSmoke =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorImageInfo (
2016-03-15 22:53:23 +01:00
textures . particles . sampler ,
2016-03-15 22:38:32 +01:00
textures . particles . smoke . view ,
VK_IMAGE_LAYOUT_GENERAL ) ;
VkDescriptorImageInfo texDescriptorFire =
2017-02-12 11:12:42 +01:00
vks : : initializers : : descriptorImageInfo (
2016-03-15 22:53:23 +01:00
textures . particles . sampler ,
2016-03-15 22:38:32 +01:00
textures . particles . fire . view ,
VK_IMAGE_LAYOUT_GENERAL ) ;
2016-12-24 12:48:01 +01:00
writeDescriptorSets = {
2017-02-11 10:15:53 +01:00
// Binding 0: Vertex shader uniform buffer
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2017-02-11 10:15:53 +01:00
descriptorSets . particles ,
2016-03-15 22:38:32 +01:00
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
0 ,
2016-12-24 12:48:01 +01:00
& uniformBuffers . fire . descriptor ) ,
2017-02-11 10:15:53 +01:00
// Binding 1: Smoke texture
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2017-02-11 10:15:53 +01:00
descriptorSets . particles ,
2016-03-15 22:38:32 +01:00
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
1 ,
& texDescriptorSmoke ) ,
2017-02-11 10:15:53 +01:00
// Binding 1: Fire texture array
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2017-02-11 10:15:53 +01:00
descriptorSets . particles ,
2016-03-15 22:38:32 +01:00
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
2 ,
& texDescriptorFire )
} ;
vkUpdateDescriptorSets ( device , writeDescriptorSets . size ( ) , writeDescriptorSets . data ( ) , 0 , NULL ) ;
// Environment
2017-02-11 10:15:53 +01:00
VK_CHECK_RESULT ( vkAllocateDescriptorSets ( device , & allocInfo , & descriptorSets . environment ) ) ;
2016-03-15 22:38:32 +01:00
2016-12-24 12:48:01 +01:00
writeDescriptorSets = {
2017-02-11 10:15:53 +01:00
// Binding 0: Vertex shader uniform buffer
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2017-02-11 10:15:53 +01:00
descriptorSets . environment ,
2016-03-15 22:38:32 +01:00
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ,
0 ,
2016-12-24 12:48:01 +01:00
& uniformBuffers . environment . descriptor ) ,
2017-02-11 10:15:53 +01:00
// Binding 1: Color map
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2017-02-11 10:15:53 +01:00
descriptorSets . environment ,
2016-03-15 22:38:32 +01:00
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
1 ,
2017-02-11 10:15:53 +01:00
& textures . floor . colorMap . descriptor ) ,
// Binding 2: Normal map
2017-02-12 11:12:42 +01:00
vks : : initializers : : writeDescriptorSet (
2017-02-11 10:15:53 +01:00
descriptorSets . environment ,
2016-03-15 22:38:32 +01:00
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ,
2 ,
2017-02-11 10:15:53 +01:00
& textures . floor . normalMap . descriptor ) ,
2016-12-24 12:48:01 +01:00
} ;
2016-03-15 22:38:32 +01:00
vkUpdateDescriptorSets ( device , writeDescriptorSets . size ( ) , writeDescriptorSets . data ( ) , 0 , NULL ) ;
}
void preparePipelines ( )
{
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineInputAssemblyStateCreateInfo (
2016-03-15 22:38:32 +01:00
VK_PRIMITIVE_TOPOLOGY_POINT_LIST ,
0 ,
VK_FALSE ) ;
VkPipelineRasterizationStateCreateInfo rasterizationState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineRasterizationStateCreateInfo (
2016-03-15 22:38:32 +01:00
VK_POLYGON_MODE_FILL ,
VK_CULL_MODE_BACK_BIT ,
VK_FRONT_FACE_CLOCKWISE ,
0 ) ;
VkPipelineColorBlendAttachmentState blendAttachmentState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineColorBlendAttachmentState (
2016-03-15 22:38:32 +01:00
0xf ,
VK_FALSE ) ;
VkPipelineColorBlendStateCreateInfo colorBlendState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineColorBlendStateCreateInfo (
2016-03-15 22:38:32 +01:00
1 ,
& blendAttachmentState ) ;
VkPipelineDepthStencilStateCreateInfo depthStencilState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineDepthStencilStateCreateInfo (
2016-03-15 22:38:32 +01:00
VK_TRUE ,
VK_TRUE ,
VK_COMPARE_OP_LESS_OR_EQUAL ) ;
VkPipelineViewportStateCreateInfo viewportState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineViewportStateCreateInfo ( 1 , 1 , 0 ) ;
2016-03-15 22:38:32 +01:00
VkPipelineMultisampleStateCreateInfo multisampleState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineMultisampleStateCreateInfo (
2016-03-15 22:38:32 +01:00
VK_SAMPLE_COUNT_1_BIT ,
0 ) ;
std : : vector < VkDynamicState > dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT ,
VK_DYNAMIC_STATE_SCISSOR
} ;
VkPipelineDynamicStateCreateInfo dynamicState =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineDynamicStateCreateInfo (
2016-03-15 22:38:32 +01:00
dynamicStateEnables . data ( ) ,
dynamicStateEnables . size ( ) ,
0 ) ;
// Load shaders
std : : array < VkPipelineShaderStageCreateInfo , 2 > shaderStages ;
VkGraphicsPipelineCreateInfo pipelineCreateInfo =
2017-02-12 11:12:42 +01:00
vks : : initializers : : pipelineCreateInfo (
2016-03-15 22:38:32 +01:00
pipelineLayout ,
renderPass ,
0 ) ;
pipelineCreateInfo . pInputAssemblyState = & inputAssemblyState ;
pipelineCreateInfo . pRasterizationState = & rasterizationState ;
pipelineCreateInfo . pColorBlendState = & colorBlendState ;
pipelineCreateInfo . pMultisampleState = & multisampleState ;
pipelineCreateInfo . pViewportState = & viewportState ;
pipelineCreateInfo . pDepthStencilState = & depthStencilState ;
pipelineCreateInfo . pDynamicState = & dynamicState ;
pipelineCreateInfo . stageCount = shaderStages . size ( ) ;
pipelineCreateInfo . pStages = shaderStages . data ( ) ;
2017-02-11 10:15:53 +01:00
// Particle rendering pipeline
{
// Shaders
shaderStages [ 0 ] = loadShader ( getAssetPath ( ) + " shaders/particlefire/particle.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getAssetPath ( ) + " shaders/particlefire/particle.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
// Vertex input state
VkVertexInputBindingDescription vertexInputBinding =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputBindingDescription ( VERTEX_BUFFER_BIND_ID , sizeof ( Particle ) , VK_VERTEX_INPUT_RATE_VERTEX ) ;
2017-02-11 10:15:53 +01:00
std : : vector < VkVertexInputAttributeDescription > vertexInputAttributes = {
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 0 , VK_FORMAT_R32G32B32A32_SFLOAT , offsetof ( Particle , pos ) ) , // Location 0: Position
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 1 , VK_FORMAT_R32G32B32A32_SFLOAT , offsetof ( Particle , color ) ) , // Location 1: Color
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 2 , VK_FORMAT_R32_SFLOAT , offsetof ( Particle , alpha ) ) , // Location 2: Alpha
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 3 , VK_FORMAT_R32_SFLOAT , offsetof ( Particle , size ) ) , // Location 3: Size
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 4 , VK_FORMAT_R32_SFLOAT , offsetof ( Particle , rotation ) ) , // Location 4: Rotation
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 5 , VK_FORMAT_R32_SINT , offsetof ( Particle , type ) ) , // Location 5: Particle type
2017-02-11 10:15:53 +01:00
} ;
2017-02-12 11:12:42 +01:00
VkPipelineVertexInputStateCreateInfo vertexInputState = vks : : initializers : : pipelineVertexInputStateCreateInfo ( ) ;
2017-02-11 10:15:53 +01:00
vertexInputState . vertexBindingDescriptionCount = 1 ;
vertexInputState . pVertexBindingDescriptions = & vertexInputBinding ;
vertexInputState . vertexAttributeDescriptionCount = static_cast < uint32_t > ( vertexInputAttributes . size ( ) ) ;
vertexInputState . pVertexAttributeDescriptions = vertexInputAttributes . data ( ) ;
pipelineCreateInfo . pVertexInputState = & vertexInputState ;
// Dont' write to depth buffer
depthStencilState . depthWriteEnable = VK_FALSE ;
// Premulitplied alpha
blendAttachmentState . blendEnable = VK_TRUE ;
blendAttachmentState . srcColorBlendFactor = VK_BLEND_FACTOR_ONE ;
blendAttachmentState . dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA ;
blendAttachmentState . colorBlendOp = VK_BLEND_OP_ADD ;
blendAttachmentState . srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE ;
blendAttachmentState . dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO ;
blendAttachmentState . alphaBlendOp = VK_BLEND_OP_ADD ;
blendAttachmentState . colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT ;
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipelines . particles ) ) ;
}
2016-03-15 22:38:32 +01:00
// Environment rendering pipeline (normal mapped)
2017-02-11 10:15:53 +01:00
{
// Shaders
shaderStages [ 0 ] = loadShader ( getAssetPath ( ) + " shaders/particlefire/normalmap.vert.spv " , VK_SHADER_STAGE_VERTEX_BIT ) ;
shaderStages [ 1 ] = loadShader ( getAssetPath ( ) + " shaders/particlefire/normalmap.frag.spv " , VK_SHADER_STAGE_FRAGMENT_BIT ) ;
// Vertex input state
VkVertexInputBindingDescription vertexInputBinding =
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputBindingDescription ( VERTEX_BUFFER_BIND_ID , vertexLayout . stride ( ) , VK_VERTEX_INPUT_RATE_VERTEX ) ;
2017-02-11 10:15:53 +01:00
std : : vector < VkVertexInputAttributeDescription > vertexInputAttributes = {
2017-02-12 11:12:42 +01:00
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 0 , VK_FORMAT_R32G32B32_SFLOAT , 0 ) , // Location 0: Position
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 1 , VK_FORMAT_R32G32_SFLOAT , sizeof ( float ) * 3 ) , // Location 1: UV
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 2 , VK_FORMAT_R32G32B32_SFLOAT , sizeof ( float ) * 5 ) , // Location 2: Normal
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 3 , VK_FORMAT_R32G32B32_SFLOAT , sizeof ( float ) * 8 ) , // Location 3: Tangent
vks : : initializers : : vertexInputAttributeDescription ( VERTEX_BUFFER_BIND_ID , 4 , VK_FORMAT_R32G32B32_SFLOAT , sizeof ( float ) * 11 ) , // Location 4: Bitangen
2017-02-11 10:15:53 +01:00
} ;
2017-02-12 11:12:42 +01:00
VkPipelineVertexInputStateCreateInfo vertexInputState = vks : : initializers : : pipelineVertexInputStateCreateInfo ( ) ;
2017-02-11 10:15:53 +01:00
vertexInputState . vertexBindingDescriptionCount = 1 ;
vertexInputState . pVertexBindingDescriptions = & vertexInputBinding ;
vertexInputState . vertexAttributeDescriptionCount = static_cast < uint32_t > ( vertexInputAttributes . size ( ) ) ;
vertexInputState . pVertexAttributeDescriptions = vertexInputAttributes . data ( ) ;
pipelineCreateInfo . pVertexInputState = & vertexInputState ;
blendAttachmentState . blendEnable = VK_FALSE ;
depthStencilState . depthWriteEnable = VK_TRUE ;
inputAssemblyState . topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST ;
VK_CHECK_RESULT ( vkCreateGraphicsPipelines ( device , pipelineCache , 1 , & pipelineCreateInfo , nullptr , & pipelines . environment ) ) ;
}
2016-03-15 22:38:32 +01:00
}
// Prepare and initialize uniform buffer containing shader uniforms
void prepareUniformBuffers ( )
{
// Vertex shader uniform buffer block
2016-12-24 12:48:01 +01:00
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
2016-03-15 22:38:32 +01:00
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ,
2016-06-05 20:14:04 +02:00
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
2016-12-24 12:48:01 +01:00
& uniformBuffers . fire ,
sizeof ( uboVS ) ) ) ;
2016-03-15 22:38:32 +01:00
// Vertex shader uniform buffer block
2016-12-24 12:48:01 +01:00
VK_CHECK_RESULT ( vulkanDevice - > createBuffer (
2016-03-15 22:38:32 +01:00
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ,
2016-06-05 20:14:04 +02:00
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
2016-12-24 12:48:01 +01:00
& uniformBuffers . environment ,
sizeof ( uboEnv ) ) ) ;
// Map persistent
VK_CHECK_RESULT ( uniformBuffers . fire . map ( ) ) ;
VK_CHECK_RESULT ( uniformBuffers . environment . map ( ) ) ;
2016-03-15 22:38:32 +01:00
updateUniformBuffers ( ) ;
}
void updateUniformBufferLight ( )
{
// Environment
2017-02-11 10:15:53 +01:00
uboEnv . lightPos . x = sin ( timer * 2.0f * float ( M_PI ) ) * 1.5f ;
2016-03-15 22:38:32 +01:00
uboEnv . lightPos . y = 0.0f ;
2017-02-11 10:15:53 +01:00
uboEnv . lightPos . z = cos ( timer * 2.0f * float ( M_PI ) ) * 1.5f ;
2016-12-24 12:48:01 +01:00
memcpy ( uniformBuffers . environment . mapped , & uboEnv , sizeof ( uboEnv ) ) ;
2016-03-15 22:38:32 +01:00
}
void updateUniformBuffers ( )
{
// Vertex shader
glm : : mat4 viewMatrix = glm : : mat4 ( ) ;
uboVS . projection = glm : : perspective ( glm : : radians ( 60.0f ) , ( float ) width / ( float ) height , 0.001f , 256.0f ) ;
viewMatrix = glm : : translate ( viewMatrix , glm : : vec3 ( 0.0f , 0.0f , zoom ) ) ;
uboVS . model = glm : : mat4 ( ) ;
uboVS . model = viewMatrix * glm : : translate ( uboVS . model , glm : : vec3 ( 0.0f , 15.0f , 0.0f ) ) ;
uboVS . model = glm : : rotate ( uboVS . model , glm : : radians ( rotation . x ) , glm : : vec3 ( 1.0f , 0.0f , 0.0f ) ) ;
uboVS . model = glm : : rotate ( uboVS . model , glm : : radians ( rotation . y ) , glm : : vec3 ( 0.0f , 1.0f , 0.0f ) ) ;
uboVS . model = glm : : rotate ( uboVS . model , glm : : radians ( rotation . z ) , glm : : vec3 ( 0.0f , 0.0f , 1.0f ) ) ;
uboVS . viewportDim = glm : : vec2 ( ( float ) width , ( float ) height ) ;
2016-12-24 12:48:01 +01:00
memcpy ( uniformBuffers . fire . mapped , & uboVS , sizeof ( uboVS ) ) ;
2016-03-15 22:38:32 +01:00
// Environment
uboEnv . projection = uboVS . projection ;
uboEnv . model = uboVS . model ;
uboEnv . normal = glm : : inverseTranspose ( uboEnv . model ) ;
uboEnv . cameraPos = glm : : vec4 ( 0.0 , 0.0 , zoom , 0.0 ) ;
2016-12-24 12:48:01 +01:00
memcpy ( uniformBuffers . environment . mapped , & uboEnv , sizeof ( uboEnv ) ) ;
2016-03-15 22:38:32 +01:00
}
2016-06-05 20:14:04 +02:00
void draw ( )
{
VulkanExampleBase : : prepareFrame ( ) ;
// Command buffer to be sumitted to the queue
submitInfo . commandBufferCount = 1 ;
submitInfo . pCommandBuffers = & drawCmdBuffers [ currentBuffer ] ;
// Submit to queue
VK_CHECK_RESULT ( vkQueueSubmit ( queue , 1 , & submitInfo , VK_NULL_HANDLE ) ) ;
VulkanExampleBase : : submitFrame ( ) ;
}
2016-03-15 22:38:32 +01:00
void prepare ( )
{
VulkanExampleBase : : prepare ( ) ;
2017-02-11 10:15:53 +01:00
loadAssets ( ) ;
2016-03-15 22:38:32 +01:00
prepareParticles ( ) ;
prepareUniformBuffers ( ) ;
setupDescriptorSetLayout ( ) ;
preparePipelines ( ) ;
setupDescriptorPool ( ) ;
setupDescriptorSets ( ) ;
buildCommandBuffers ( ) ;
prepared = true ;
}
virtual void render ( )
{
if ( ! prepared )
return ;
draw ( ) ;
if ( ! paused )
{
updateUniformBufferLight ( ) ;
updateParticles ( ) ;
}
}
virtual void viewChanged ( )
{
updateUniformBuffers ( ) ;
}
} ;
2016-12-13 19:25:56 +01:00
VULKAN_EXAMPLE_MAIN ( )