2017-01-17 20:25:16 +01:00
/*
* Vulkan Model loader using ASSIMP
*
* Copyright ( C ) 2016 - 2017 by Sascha Willems - www . saschawillems . de
*
* This code is licensed under the MIT license ( MIT ) ( http : //opensource.org/licenses/MIT)
*/
# pragma once
# include <stdlib.h>
# include <string>
# include <fstream>
# include <vector>
# include "vulkan/vulkan.h"
# include <assimp/Importer.hpp>
# include <assimp/scene.h>
# include <assimp/postprocess.h>
# include <assimp/cimport.h>
# include <glm/glm.hpp>
# include <glm/gtc/matrix_transform.hpp>
# include <glm/gtc/type_ptr.hpp>
2017-02-12 10:16:07 +01:00
# include "VulkanDevice.hpp"
2017-02-12 10:44:51 +01:00
# include "VulkanBuffer.hpp"
2017-01-17 20:25:16 +01:00
# if defined(__ANDROID__)
# include <android/asset_manager.h>
# endif
namespace vks
{
2017-01-29 12:05:20 +01:00
/** @brief Vertex layout components */
typedef enum Component {
VERTEX_COMPONENT_POSITION = 0x0 ,
VERTEX_COMPONENT_NORMAL = 0x1 ,
VERTEX_COMPONENT_COLOR = 0x2 ,
VERTEX_COMPONENT_UV = 0x3 ,
VERTEX_COMPONENT_TANGENT = 0x4 ,
VERTEX_COMPONENT_BITANGENT = 0x5 ,
VERTEX_COMPONENT_DUMMY_FLOAT = 0x6 ,
VERTEX_COMPONENT_DUMMY_VEC4 = 0x7
} Component ;
2017-01-17 20:25:16 +01:00
/** @brief Stores vertex layout components for model loading and Vulkan vertex input and atribute bindings */
struct VertexLayout {
public :
2017-01-29 12:05:20 +01:00
/** @brief Components used to generate vertices from */
2017-01-17 20:25:16 +01:00
std : : vector < Component > components ;
VertexLayout ( std : : vector < Component > components )
{
this - > components = std : : move ( components ) ;
}
uint32_t stride ( )
{
uint32_t res = 0 ;
for ( auto & component : components )
{
switch ( component )
{
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_UV :
2017-01-17 20:25:16 +01:00
res + = 2 * sizeof ( float ) ;
break ;
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_DUMMY_FLOAT :
2017-01-17 20:25:16 +01:00
res + = sizeof ( float ) ;
break ;
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_DUMMY_VEC4 :
2017-01-17 20:25:16 +01:00
res + = 4 * sizeof ( float ) ;
break ;
default :
2017-01-29 12:05:20 +01:00
// All components except the ones listed above are made up of 3 floats
2017-01-17 20:25:16 +01:00
res + = 3 * sizeof ( float ) ;
}
}
return res ;
}
} ;
2017-01-19 19:32:42 +01:00
/** @brief Used to parametrize model loading */
struct ModelCreateInfo {
glm : : vec3 center ;
glm : : vec3 scale ;
glm : : vec2 uvscale ;
2019-04-23 08:15:23 +02:00
VkMemoryPropertyFlags memoryPropertyFlags = 0 ;
2017-01-19 19:32:42 +01:00
2019-04-23 08:15:23 +02:00
ModelCreateInfo ( ) : center ( glm : : vec3 ( 0.0f ) ) , scale ( glm : : vec3 ( 1.0f ) ) , uvscale ( glm : : vec2 ( 1.0f ) ) { } ;
2017-01-19 19:32:42 +01:00
2017-02-13 19:32:44 +01:00
ModelCreateInfo ( glm : : vec3 scale , glm : : vec2 uvscale , glm : : vec3 center )
2017-01-19 19:32:42 +01:00
{
this - > center = center ;
this - > scale = scale ;
this - > uvscale = uvscale ;
}
ModelCreateInfo ( float scale , float uvscale , float center )
{
this - > center = glm : : vec3 ( center ) ;
this - > scale = glm : : vec3 ( scale ) ;
2017-02-13 19:34:05 +01:00
this - > uvscale = glm : : vec2 ( uvscale ) ;
2017-01-19 19:32:42 +01:00
}
} ;
2017-01-17 20:25:16 +01:00
struct Model {
2017-02-09 22:22:49 +01:00
VkDevice device = nullptr ;
2017-02-12 10:44:51 +01:00
vks : : Buffer vertices ;
vks : : Buffer indices ;
2017-01-17 20:25:16 +01:00
uint32_t indexCount = 0 ;
uint32_t vertexCount = 0 ;
/** @brief Stores vertex and index base and counts for each part of a model */
struct ModelPart {
uint32_t vertexBase ;
uint32_t vertexCount ;
uint32_t indexBase ;
uint32_t indexCount ;
} ;
std : : vector < ModelPart > parts ;
static const int defaultFlags = aiProcess_FlipWindingOrder | aiProcess_Triangulate | aiProcess_PreTransformVertices | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals ;
struct Dimension
{
glm : : vec3 min = glm : : vec3 ( FLT_MAX ) ;
glm : : vec3 max = glm : : vec3 ( - FLT_MAX ) ;
glm : : vec3 size ;
} dim ;
/** @brief Release all Vulkan resources of this model */
void destroy ( )
2017-02-09 22:22:49 +01:00
{
assert ( device ) ;
2017-01-17 20:25:16 +01:00
vkDestroyBuffer ( device , vertices . buffer , nullptr ) ;
vkFreeMemory ( device , vertices . memory , nullptr ) ;
if ( indices . buffer ! = VK_NULL_HANDLE )
{
vkDestroyBuffer ( device , indices . buffer , nullptr ) ;
vkFreeMemory ( device , indices . memory , nullptr ) ;
}
}
2017-01-19 19:32:42 +01:00
/**
* Loads a 3 D model from a file into Vulkan buffers
*
* @ param device Pointer to the Vulkan device used to generated the vertex and index buffers on
* @ param filename File to load ( must be a model format supported by ASSIMP )
* @ param layout Vertex layout components ( position , normals , tangents , etc . )
* @ param createInfo MeshCreateInfo structure for load time settings like scale , center , etc .
* @ param copyQueue Queue used for the memory staging copy commands ( must support transfer )
*/
2019-04-23 08:15:23 +02:00
bool loadFromFile ( const std : : string & filename , vks : : VertexLayout layout , vks : : ModelCreateInfo * createInfo , vks : : VulkanDevice * device , VkQueue copyQueue )
2017-01-17 20:25:16 +01:00
{
this - > device = device - > logicalDevice ;
Assimp : : Importer Importer ;
const aiScene * pScene ;
// Load file
# if defined(__ANDROID__)
// Meshes are stored inside the apk on Android (compressed)
// So they need to be loaded via the asset manager
2017-01-18 19:21:40 +01:00
AAsset * asset = AAssetManager_open ( androidApp - > activity - > assetManager , filename . c_str ( ) , AASSET_MODE_STREAMING ) ;
2017-03-09 20:00:55 +01:00
if ( ! asset ) {
LOGE ( " Could not load mesh from \" %s \" ! " , filename . c_str ( ) ) ;
return false ;
}
2017-01-17 20:25:16 +01:00
assert ( asset ) ;
size_t size = AAsset_getLength ( asset ) ;
assert ( size > 0 ) ;
void * meshData = malloc ( size ) ;
AAsset_read ( asset , meshData , size ) ;
AAsset_close ( asset ) ;
2019-04-23 08:15:23 +02:00
pScene = Importer . ReadFileFromMemory ( meshData , size , defaultFlags ) ;
2017-01-17 20:25:16 +01:00
free ( meshData ) ;
# else
2019-04-23 08:15:23 +02:00
pScene = Importer . ReadFile ( filename . c_str ( ) , defaultFlags ) ;
2017-12-11 22:14:34 +01:00
if ( ! pScene ) {
std : : string error = Importer . GetErrorString ( ) ;
2018-01-21 18:28:17 +01:00
vks : : tools : : exitFatal ( error + " \n \n The file may be part of the additional asset pack. \n \n Run \" download_assets.py \" in the repository root to download the latest version. " , - 1 ) ;
2017-12-11 22:14:34 +01:00
}
2017-01-17 20:25:16 +01:00
# endif
if ( pScene )
{
parts . clear ( ) ;
parts . resize ( pScene - > mNumMeshes ) ;
glm : : vec3 scale ( 1.0f ) ;
glm : : vec2 uvscale ( 1.0f ) ;
glm : : vec3 center ( 0.0f ) ;
if ( createInfo )
{
scale = createInfo - > scale ;
uvscale = createInfo - > uvscale ;
center = createInfo - > center ;
}
std : : vector < float > vertexBuffer ;
std : : vector < uint32_t > indexBuffer ;
vertexCount = 0 ;
indexCount = 0 ;
// Load meshes
for ( unsigned int i = 0 ; i < pScene - > mNumMeshes ; i + + )
{
const aiMesh * paiMesh = pScene - > mMeshes [ i ] ;
parts [ i ] = { } ;
parts [ i ] . vertexBase = vertexCount ;
parts [ i ] . indexBase = indexCount ;
vertexCount + = pScene - > mMeshes [ i ] - > mNumVertices ;
aiColor3D pColor ( 0.f , 0.f , 0.f ) ;
pScene - > mMaterials [ paiMesh - > mMaterialIndex ] - > Get ( AI_MATKEY_COLOR_DIFFUSE , pColor ) ;
const aiVector3D Zero3D ( 0.0f , 0.0f , 0.0f ) ;
for ( unsigned int j = 0 ; j < paiMesh - > mNumVertices ; j + + )
{
const aiVector3D * pPos = & ( paiMesh - > mVertices [ j ] ) ;
const aiVector3D * pNormal = & ( paiMesh - > mNormals [ j ] ) ;
const aiVector3D * pTexCoord = ( paiMesh - > HasTextureCoords ( 0 ) ) ? & ( paiMesh - > mTextureCoords [ 0 ] [ j ] ) : & Zero3D ;
const aiVector3D * pTangent = ( paiMesh - > HasTangentsAndBitangents ( ) ) ? & ( paiMesh - > mTangents [ j ] ) : & Zero3D ;
const aiVector3D * pBiTangent = ( paiMesh - > HasTangentsAndBitangents ( ) ) ? & ( paiMesh - > mBitangents [ j ] ) : & Zero3D ;
for ( auto & component : layout . components )
{
switch ( component ) {
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_POSITION :
2017-01-17 20:25:16 +01:00
vertexBuffer . push_back ( pPos - > x * scale . x + center . x ) ;
vertexBuffer . push_back ( - pPos - > y * scale . y + center . y ) ;
vertexBuffer . push_back ( pPos - > z * scale . z + center . z ) ;
break ;
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_NORMAL :
2017-01-17 20:25:16 +01:00
vertexBuffer . push_back ( pNormal - > x ) ;
vertexBuffer . push_back ( - pNormal - > y ) ;
vertexBuffer . push_back ( pNormal - > z ) ;
break ;
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_UV :
2017-01-17 20:25:16 +01:00
vertexBuffer . push_back ( pTexCoord - > x * uvscale . s ) ;
vertexBuffer . push_back ( pTexCoord - > y * uvscale . t ) ;
break ;
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_COLOR :
2017-01-17 20:25:16 +01:00
vertexBuffer . push_back ( pColor . r ) ;
vertexBuffer . push_back ( pColor . g ) ;
vertexBuffer . push_back ( pColor . b ) ;
break ;
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_TANGENT :
2017-01-17 20:25:16 +01:00
vertexBuffer . push_back ( pTangent - > x ) ;
vertexBuffer . push_back ( pTangent - > y ) ;
vertexBuffer . push_back ( pTangent - > z ) ;
break ;
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_BITANGENT :
2017-01-17 20:25:16 +01:00
vertexBuffer . push_back ( pBiTangent - > x ) ;
vertexBuffer . push_back ( pBiTangent - > y ) ;
vertexBuffer . push_back ( pBiTangent - > z ) ;
break ;
2017-01-29 12:05:20 +01:00
// Dummy components for padding
case VERTEX_COMPONENT_DUMMY_FLOAT :
2017-01-17 20:25:16 +01:00
vertexBuffer . push_back ( 0.0f ) ;
break ;
2017-01-29 12:05:20 +01:00
case VERTEX_COMPONENT_DUMMY_VEC4 :
2017-01-17 20:25:16 +01:00
vertexBuffer . push_back ( 0.0f ) ;
vertexBuffer . push_back ( 0.0f ) ;
vertexBuffer . push_back ( 0.0f ) ;
vertexBuffer . push_back ( 0.0f ) ;
break ;
} ;
}
dim . max . x = fmax ( pPos - > x , dim . max . x ) ;
dim . max . y = fmax ( pPos - > y , dim . max . y ) ;
dim . max . z = fmax ( pPos - > z , dim . max . z ) ;
dim . min . x = fmin ( pPos - > x , dim . min . x ) ;
dim . min . y = fmin ( pPos - > y , dim . min . y ) ;
dim . min . z = fmin ( pPos - > z , dim . min . z ) ;
}
dim . size = dim . max - dim . min ;
parts [ i ] . vertexCount = paiMesh - > mNumVertices ;
uint32_t indexBase = static_cast < uint32_t > ( indexBuffer . size ( ) ) ;
for ( unsigned int j = 0 ; j < paiMesh - > mNumFaces ; j + + )
{
const aiFace & Face = paiMesh - > mFaces [ j ] ;
if ( Face . mNumIndices ! = 3 )
continue ;
indexBuffer . push_back ( indexBase + Face . mIndices [ 0 ] ) ;
indexBuffer . push_back ( indexBase + Face . mIndices [ 1 ] ) ;
indexBuffer . push_back ( indexBase + Face . mIndices [ 2 ] ) ;
parts [ i ] . indexCount + = 3 ;
indexCount + = 3 ;
}
}
uint32_t vBufferSize = static_cast < uint32_t > ( vertexBuffer . size ( ) ) * sizeof ( float ) ;
uint32_t iBufferSize = static_cast < uint32_t > ( indexBuffer . size ( ) ) * sizeof ( uint32_t ) ;
// Use staging buffer to move vertex and index buffer to device local memory
// Create staging buffers
2017-02-12 10:44:51 +01:00
vks : : Buffer vertexStaging , indexStaging ;
2017-01-17 20:25:16 +01:00
// Vertex buffer
VK_CHECK_RESULT ( device - > createBuffer (
VK_BUFFER_USAGE_TRANSFER_SRC_BIT ,
2017-12-01 16:25:23 +00:00
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
2017-01-17 20:25:16 +01:00
& vertexStaging ,
vBufferSize ,
vertexBuffer . data ( ) ) ) ;
// Index buffer
VK_CHECK_RESULT ( device - > createBuffer (
VK_BUFFER_USAGE_TRANSFER_SRC_BIT ,
2017-12-01 16:25:23 +00:00
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ,
2017-01-17 20:25:16 +01:00
& indexStaging ,
iBufferSize ,
indexBuffer . data ( ) ) ) ;
// Create device local target buffers
// Vertex buffer
VK_CHECK_RESULT ( device - > createBuffer (
2019-04-23 08:15:23 +02:00
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | createInfo - > memoryPropertyFlags ,
2017-01-17 20:25:16 +01:00
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ,
& vertices ,
vBufferSize ) ) ;
// Index buffer
VK_CHECK_RESULT ( device - > createBuffer (
2019-04-23 08:15:23 +02:00
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | createInfo - > memoryPropertyFlags ,
2017-01-17 20:25:16 +01:00
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ,
& indices ,
iBufferSize ) ) ;
// Copy from staging buffers
VkCommandBuffer copyCmd = device - > createCommandBuffer ( VK_COMMAND_BUFFER_LEVEL_PRIMARY , true ) ;
VkBufferCopy copyRegion { } ;
copyRegion . size = vertices . size ;
vkCmdCopyBuffer ( copyCmd , vertexStaging . buffer , vertices . buffer , 1 , & copyRegion ) ;
copyRegion . size = indices . size ;
vkCmdCopyBuffer ( copyCmd , indexStaging . buffer , indices . buffer , 1 , & copyRegion ) ;
device - > flushCommandBuffer ( copyCmd , copyQueue ) ;
// Destroy staging resources
vkDestroyBuffer ( device - > logicalDevice , vertexStaging . buffer , nullptr ) ;
vkFreeMemory ( device - > logicalDevice , vertexStaging . memory , nullptr ) ;
vkDestroyBuffer ( device - > logicalDevice , indexStaging . buffer , nullptr ) ;
vkFreeMemory ( device - > logicalDevice , indexStaging . memory , nullptr ) ;
return true ;
}
else
{
printf ( " Error parsing '%s': '%s' \n " , filename . c_str ( ) , Importer . GetErrorString ( ) ) ;
# if defined(__ANDROID__)
LOGE ( " Error parsing '%s': '%s' " , filename . c_str ( ) , Importer . GetErrorString ( ) ) ;
# endif
return false ;
}
} ;
2017-01-19 19:32:42 +01:00
/**
* Loads a 3 D model from a file into Vulkan buffers
*
* @ param device Pointer to the Vulkan device used to generated the vertex and index buffers on
* @ param filename File to load ( must be a model format supported by ASSIMP )
* @ param layout Vertex layout components ( position , normals , tangents , etc . )
* @ param scale Load time scene scale
* @ param copyQueue Queue used for the memory staging copy commands ( must support transfer )
*/
2019-04-23 08:15:23 +02:00
bool loadFromFile ( const std : : string & filename , vks : : VertexLayout layout , float scale , vks : : VulkanDevice * device , VkQueue copyQueue )
2017-01-19 19:32:42 +01:00
{
2017-01-29 12:05:20 +01:00
vks : : ModelCreateInfo modelCreateInfo ( scale , 1.0f , 0.0f ) ;
2019-04-23 08:15:23 +02:00
return loadFromFile ( filename , layout , & modelCreateInfo , device , copyQueue ) ;
2017-01-19 19:32:42 +01:00
}
2017-01-17 20:25:16 +01:00
} ;
} ;