Rework API graphique Vulkan - EnTT pour ECS + Chargement modèle 3D assimp + SDL3 pour events input et fenetre + mesh texture camera transform ok + attention tous les assets nouveaus ne sont pas commités et il y a du code test en dur dans scene addentity + restructuration globale

This commit is contained in:
Tom Ray
2026-03-14 20:24:17 +01:00
parent 7c352bc280
commit 6695d46bcd
672 changed files with 238656 additions and 1821 deletions

View File

@@ -0,0 +1,40 @@
#include "CModel.hpp"
CModel::CModel(std::vector<CMesh*> meshes) :
m_meshes(meshes),
m_loaded(false) {
}
CModel::~CModel(void) {
}
void CModel::load(void) {
// For each meshes of the model.
for (unsigned int i = 0; i < m_meshes.size(); i++) {
m_meshes[i]->load();
}
m_loaded = true;
}
/*void CModel::draw(glm::mat4 model, glm::mat4 view, glm::mat4 projection, glm::vec3 lightPos, float intensity) {
// For each meshes of the model.
for (unsigned int i = 0; i < m_meshes.size(); i++) {
m_meshes[i]->draw(model, view, projection, lightPos, intensity);
}
}*/
std::vector<CMesh*>& CModel::getMeshes()
{
return m_meshes;
}
void CModel::setMeshes(std::vector<CMesh*> meshes)
{
m_meshes = meshes;
}
bool CModel::isLoaded()
{
return m_loaded;
}

View File

@@ -0,0 +1,34 @@
#ifndef CMODEL_HPP
#define CMODEL_HPP
#include "Mesh/CMesh.hpp"
#include <glm/glm.hpp>
class CModel {
private:
std::vector<CMesh*> m_meshes;
bool m_loaded;
public:
CModel(void) = delete;
CModel(std::vector<CMesh*> meshes);
~CModel(void);
void load(void);
//void draw(glm::mat4 model, glm::mat4 view, glm::mat4 projection, glm::vec3 lightPos, float intensity);
std::vector<CMesh*>& getMeshes(void);
void setMeshes(std::vector<CMesh*> meshes);
bool isLoaded(void);
};
#endif

View File

@@ -0,0 +1,151 @@
#include "CModelLoader.hpp"
#include "../API/GraphicsAPI.hpp"
#include <memory>
CModel* CModelLoader::loadModel(std::string fileName, CResourceManager& rm)
{
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile(fileName,
aiProcess_Triangulate |
aiProcess_FlipUVs |
aiProcess_CalcTangentSpace |
aiProcess_GenNormals
);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
throw std::runtime_error("Assimp: " + std::string(importer.GetErrorString()));
// dossier de base pour les textures relatives
std::vector<CMesh*> meshes;
processNode(scene->mRootNode, scene, fileName, rm, meshes);
// crée et enregistre le model
auto model = std::make_unique<CModel>(meshes);
auto* ptr = model.get();
rm.getModelManager().addModel(fileName,std::move(model));
return ptr;
}
void CModelLoader::processNode(aiNode* node, const aiScene* scene, const std::string& directory, CResourceManager& rm, std::vector<CMesh*>& meshes)
{
// process all the node's meshes (if any)
for (uint32_t i = 0; i < node->mNumMeshes; i++) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(processMesh(mesh, scene, directory, rm));
}
// then do the same for each of its children
for (uint32_t i = 0; i < node->mNumChildren; i++) {
processNode(node->mChildren[i], scene, directory, rm, meshes);
}
}
CMesh* CModelLoader::processMesh(aiMesh* mesh, const aiScene* scene, const std::string& directory, CResourceManager& rm)
{
std::vector<SVertex> vertices;
std::vector<uint32_t> indices;
// vertices
for (uint32_t i = 0; i < mesh->mNumVertices; i++) {
SVertex vertex{};
vertex.m_position = {
mesh->mVertices[i].x,
mesh->mVertices[i].y,
mesh->mVertices[i].z
};
vertex.m_normal = mesh->HasNormals() ?
glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z) :
glm::vec3(0.0f);
vertex.m_texCoords = mesh->mTextureCoords[0] ?
glm::vec2(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y) :
glm::vec2(0.0f);
vertex.m_tangent = mesh->HasTangentsAndBitangents() ?
glm::vec3(mesh->mTangents[i].x, mesh->mTangents[i].y, mesh->mTangents[i].z) :
glm::vec3(0.0f);
vertex.m_bitangent = mesh->HasTangentsAndBitangents() ?
glm::vec3(mesh->mBitangents[i].x, mesh->mBitangents[i].y, mesh->mBitangents[i].z) :
glm::vec3(0.0f);
vertices.push_back(vertex);
}
// indices
for (uint32_t i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (uint32_t j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
// material
CMaterial* material = nullptr;
if (mesh->mMaterialIndex >= 0) {
material = processMaterial(
scene->mMaterials[mesh->mMaterialIndex],
directory,
rm
);
}
// crée et enregistre le mesh
auto newMesh = std::make_unique<CMesh>();
newMesh->setVertices(vertices);
newMesh->setIndexes(indices);
if (material) newMesh->setMaterial(material);
newMesh->load();
auto* ptr = newMesh.get();
rm.getMeshManager().addMesh(directory, std::move(newMesh));
return ptr;
}
CMaterial* CModelLoader::processMaterial(aiMaterial* mat, const std::string& directory, CResourceManager& rm)
{
auto newMat = std::make_unique<CMaterial>();
// albedo
if (mat->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
aiString path;
mat->GetTexture(aiTextureType_DIFFUSE, 0, &path);
newMat->textureAlbedo = loadTexture(
directory + "/" + path.C_Str(), rm
);
}
// normal
if (mat->GetTextureCount(aiTextureType_NORMALS) > 0) {
aiString path;
mat->GetTexture(aiTextureType_NORMALS, 0, &path);
newMat->textureNormal = loadTexture(
directory + "/" + path.C_Str(), rm
);
}
auto shader = GraphicsAPI::getAPI()->getShaderManager().get("triangle");
newMat->shader = static_cast<CShaderImplVulkan*>(shader);
newMat->build();
auto* ptr = newMat.get();
rm.getMaterialManager().addMaterial(directory, std::move(newMat));
return ptr;
}
CTexture* CModelLoader::loadTexture(const std::string& directory, CResourceManager& rm)
{
auto text = rm.getTextureManager().get(directory);
if(rm.getTextureManager().get(directory))
{
return text.value();
}
else {
auto tex = std::make_unique<CTexture>(directory);
tex->init();
auto* ptr = tex.get();
rm.getTextureManager().addTexture(directory, std::move(tex));
return ptr;
}
}

View File

@@ -0,0 +1,58 @@
#ifndef CMODELLOADER_HPP
#define CMODELLOADER_HPP
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include "CResourceManager.hpp"
#include "Mesh/CMesh.hpp"
#include "Texture/CTexture.hpp"
/**
* @brief Class allowing to load models from obj files.
*/
#include <filesystem>
class CModelLoader {
private:
/**
* @brief Compute every node in the loaded meshes.
* @param[in] node The assimp created node.
* @param[in] scene The assimp created scene.
* @param[in] meshes The mesh list.
* @param[in] material A pointer to a material. It's optionnal and override the obj's material.
*/
static void processNode(aiNode* node, const aiScene* scene, const std::string& directory, CResourceManager& rm, std::vector<CMesh*>& meshes);
/**
* @brief Compute every meshes.
* @param[in] mesh The assimp created mesh.
* @param[in] scene The assimp created scene.
* @param[in] meshes The list of meshes.
* @param[in] material A pointer to a material. It's optionnal and override the obj's material.
*/
static CMesh* processMesh(aiMesh* mesh, const aiScene* scene, const std::string& directory, CResourceManager& rm);
/**
* @brief Load Textures from the obj file.
* @param[in] mat The assimp created material.
* @param[in] type The assimp's texture type.
* @param[in] typeName The texture's type.
*/
static CTexture* loadTexture(const std::string& directory, CResourceManager& rm);
static CMaterial* processMaterial(aiMaterial* mat, const std::string& directory, CResourceManager& rm);
public:
/**
* @brief main function of the class, starts loading the obj file.
* @param[in] fileName Path to the OBJ file.
* @param[in] material Pointer to a material. It's optionnal and override the obj's material.
* @return A dynamically allocated CModel.
*/
static CModel* loadModel(std::string filename, CResourceManager& rm);
};
#endif

View File

@@ -0,0 +1,32 @@
#ifndef CMODELMANAGER_HPP
#define CMODELMANAGER_HPP
#include "CModel.hpp"
#include <list>
#include <memory>
class CModelManager
{
public:
std::list<std::unique_ptr<CModel>> loadedModel;
std::unordered_map<std::string, CModel*> modelsMap;
void addModel(std::string name, std::unique_ptr<CModel>&& model)
{
loadedModel.push_back(std::move(model));
modelsMap[name] = loadedModel.back().get();
}
std::optional<CModel*> get(std::string name)
{
auto find = modelsMap.find(name);
if(find != modelsMap.end())
{
return find->second;
}
else {
return nullptr;
}
}
};
#endif

View File

@@ -0,0 +1,28 @@
#ifndef CRESOURCEMANAGER_HPP
#define CRESOURCEMANAGER_HPP
#include "CModelManager.hpp"
#include "Mesh/CMeshManager.hpp"
#include "Material/CMaterialManager.hpp"
#include "Texture/CTextureManager.hpp"
class CResourceManager{
private:
CModelManager modelManager;
CMeshManager meshManager;
CMaterialManager materialManager;
CTextureManager textureManager;
public:
CModelManager& getModelManager() { return modelManager; }
CMeshManager& getMeshManager() { return meshManager; }
CMaterialManager& getMaterialManager() { return materialManager; }
CTextureManager& getTextureManager() {return textureManager;}
void cleanup(){
materialManager.loadedMaterials.clear();
meshManager.loadedMeshes.clear();
modelManager.loadedModel.clear();
textureManager.loadedTextures.clear();
};
};
#endif

View File

@@ -0,0 +1,111 @@
/*#include "CTextTexture.hpp"
CTextTexture::CTextTexture(ETextureType type, std::string filePath, std::string text, unsigned int fontSize, glm::vec3 fontColor, glm::bvec4 fontStyle) :
CAbstractTexture(type),
m_fontFilePath(filePath),
m_renderText(text),
m_fontSize(fontSize),
m_fontColor(fontColor),
m_fontStyle(fontStyle) {
}
std::string CTextTexture::getFontFilePath(void) {
return m_fontFilePath;
}
void CTextTexture::setFontFilePath(std::string filePath) {
m_fontFilePath = filePath;
}
std::string CTextTexture::getText(void) {
return m_renderText;
}
void CTextTexture::setText(std::string text) {
m_renderText = text;
}
void CTextTexture::init(void) {
// Read the font file.
TTF_Font* font = TTF_OpenFont(m_fontFilePath.c_str(), m_fontSize);
SDL_Color color;
color.r = m_fontColor.x / 255u;
color.g = m_fontColor.y / 255u;
color.b = m_fontColor.z / 255u;
int style = TTF_STYLE_NORMAL;
if (m_fontStyle.x) {
style |= TTF_STYLE_BOLD;
}
if (m_fontStyle.y) {
style |= TTF_STYLE_ITALIC;
}
if (m_fontStyle.z) {
style |= TTF_STYLE_UNDERLINE;
}
if (m_fontStyle.z) {
style |= TTF_STYLE_STRIKETHROUGH;
}
TTF_SetFontStyle(font, style);
SDL_Surface* sdlSurface = TTF_RenderText_Blended(font, m_renderText.c_str(), color);
if (sdlSurface == NULL) {
throw CLibException(std::string("Can not render the text : ") + std::string(SDL_GetError()));
}
// Create the id.
unsigned int id;
glGenTextures(1, &id);
setId(id);
// Image format.
GLenum internalFormat(0);
GLenum externalFormat(0);
if (sdlSurface->format->BytesPerPixel == 3) {
// We use RGB as internal format.
internalFormat = GL_RGB;
// Choose the external format.
if (sdlSurface->format->Rmask == 0xff)
externalFormat = GL_RGB;
else
externalFormat = GL_BGR;
}
else if (sdlSurface->format->BytesPerPixel == 4) {
// We use RGBA as internal format.
internalFormat = GL_RGBA;
// Choose the external format.
if (sdlSurface->format->Rmask == 0xff)
externalFormat = GL_RGBA;
else
externalFormat = GL_BGRA;
}
else {
SDL_FreeSurface(sdlSurface);
throw CRuntimeException("Unknow image internal format.");
}
// Lock the texture to use it.
glBindTexture(GL_TEXTURE_2D, getId());
// Fill the GL texture.
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, sdlSurface->w, sdlSurface->h, 0, externalFormat, GL_UNSIGNED_BYTE, sdlSurface->pixels);
// Set filters : the near texture have a linear filter.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// The far texture have a nearest filter, meaning no filter.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Unlock the texture.
glBindTexture(GL_TEXTURE_2D, 0);
// Free the font.
TTF_CloseFont(font);
// Free the surface.
SDL_FreeSurface(sdlSurface);
}*/

View File

@@ -0,0 +1,47 @@
/*#ifndef CTEXTTEXTURE_HPP
#define CTEXTTEXTURE_HPP
#include "../../../Controller/Exception/CLibException.hpp"
#include "../../../Controller/Exception/CRuntimeException.hpp"
#include "../../../Controller/Exception/CLogicException.hpp"
#include "CAbstractTexture.hpp"
#include <string>
#include <SDL.h>
#include <glm/glm.hpp>
#include <SDL_ttf.h>
#ifdef WIN32
#include <gl/glew.h>
#else
#include <GL/glew.h>
#endif
class CTextTexture : CAbstractTexture {
private:
// Path of the file.
std::string m_fontFilePath;
// The size of the font.
unsigned int m_fontSize;
// The color of the font.
glm::vec3 m_fontColor;
// The style of the font (x is bold, y is italic, z is underline, w is strikethrough).
glm::bvec4 m_fontStyle;
// The text to render.
std::string m_renderText;
public:
CTextTexture(void) = delete;
CTextTexture(ETextureType type, std::string filePath, std::string text, unsigned int fontSize, glm::vec3 fontColor, glm::bvec4 fontStyle);
~CTextTexture() {};
std::string getFontFilePath(void);
void setFontFilePath(std::string filePath);
std::string getText(void);
void setText(std::string text);
virtual void init(void);
};
#endif*/

View File

@@ -0,0 +1,137 @@
#include "CMaterial.hpp"
#include "../Mesh/SVertex.hpp"
#include "../../API/VulkanImpl.hpp"
#include <vulkan/vulkan_core.h>
void CMaterial::build()
{
if(isInit)
{
destroy();
}
VulkanImpl* api = dynamic_cast<VulkanImpl*>(GraphicsAPI::getAPI().get());
auto* device= static_cast<vk::raii::Device*>(api->getDevice());
auto shaderStages = shader->getShaderStages(0, vk::ShaderStageFlagBits::eVertex, vk::ShaderStageFlagBits::eFragment);
auto bindingDescription = Vertex::getBindingDescription();
auto attributeDescriptions = Vertex::getAttributeDescriptions();
vk::PipelineVertexInputStateCreateInfo vertexInputInfo{{}, 1, &bindingDescription, static_cast<uint32_t>(attributeDescriptions.size()), attributeDescriptions.data()};
vk::PipelineInputAssemblyStateCreateInfo inputAssembly{{},vk::PrimitiveTopology::eTriangleList};
vk::PipelineViewportStateCreateInfo viewportState{{},1, {},1};
vk::PipelineRasterizationStateCreateInfo rasterizer{{},vk::False, vk::False, vk::PolygonMode::eFill,vk::CullModeFlagBits::eBack, vk::FrontFace::eCounterClockwise, vk::False, {}, {}, 1.0f, 1.0f};
vk::PipelineMultisampleStateCreateInfo multisampling{{}, vk::SampleCountFlagBits::e1, vk::False};
vk::PipelineColorBlendAttachmentState colorBlendAttachment{vk::False, {}, {}, {}, {}, {}, {}, vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA};
vk::PipelineColorBlendStateCreateInfo colorBlending{{},vk::False, vk::LogicOp::eCopy, 1, &colorBlendAttachment};
vk::PipelineDepthStencilStateCreateInfo depthStencil{{},
vk::True,
vk::True,
vk::CompareOp::eLess,
vk::False,
vk::False};
std::vector dynamicStates = {
vk::DynamicState::eViewport,
vk::DynamicState::eScissor};
vk::PipelineDynamicStateCreateInfo dynamicState{{}, static_cast<uint32_t>(dynamicStates.size()), dynamicStates.data()};
std::array<vk::DescriptorSetLayout, 3> layouts{
*api->getCameraLayout(), // set 0
*api->getTransformLayout(), // set 1
*api->getMaterialLayout() // set 2
};
vk::PipelineLayoutCreateInfo pipelineLayoutInfo{{},3,layouts.data(), 0};
pipelineLayout = vk::raii::PipelineLayout(*static_cast<vk::raii::Device*>(api->getDevice()), pipelineLayoutInfo);
vk::StructureChain<vk::GraphicsPipelineCreateInfo, vk::PipelineRenderingCreateInfo> pipelineCreateInfoChain = {
{{},
2,
shaderStages.data(),
&vertexInputInfo,
&inputAssembly,
{},
&viewportState,
&rasterizer,
&multisampling,
&depthStencil,
&colorBlending,
&dynamicState,
pipelineLayout,
nullptr
},
{{},
1,
&api->getSwapChainFormat().format,
api->findDepthFormat()
}
};
pipeline = vk::raii::Pipeline(*static_cast<vk::raii::Device*>(api->getDevice()), nullptr, pipelineCreateInfoChain.get<vk::GraphicsPipelineCreateInfo>());
// descriptor sets du material (set 2)
for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
m_descriptorSets[i] = api->getDescriptorPoolManager().allocate(
*api->getMaterialLayout()
);
std::vector<vk::WriteDescriptorSet> writes;
// binding 1 : albedo (si présent)
vk::DescriptorImageInfo albedoInfo;
if (textureAlbedo) {
albedoInfo = {
*textureAlbedo->getSampler(),
*textureAlbedo->getImageView(),
vk::ImageLayout::eShaderReadOnlyOptimal
};
writes.push_back({
*m_descriptorSets[i].set,
1, 0, 1,
vk::DescriptorType::eCombinedImageSampler,
&albedoInfo
});
}
else {
albedoInfo = {
api->getDefaultTexture()->getSampler(),
api->getDefaultTexture()->getImageView(),
vk::ImageLayout::eShaderReadOnlyOptimal
};
writes.push_back({
*m_descriptorSets[i].set,
1, 0, 1,
vk::DescriptorType::eCombinedImageSampler,
&albedoInfo
});
}
if (!writes.empty())
device->updateDescriptorSets(writes, {});
}
isInit = true;
}
CMaterial::~CMaterial()
{
destroy();
}
void CMaterial::destroy()
{
VulkanImpl* api = dynamic_cast<VulkanImpl*>(GraphicsAPI::getAPI().get());
uint32_t currentFrame = api->getCurrentFrameIndex();
api->destroyPipeline(pipeline, pipelineLayout);
// descriptor sets
for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
api->destroyDescriptorSet(m_descriptorSets[i], i);
}
}

View File

@@ -0,0 +1,30 @@
#ifndef SMATERIAL_HPP
#define SMATERIAL_HPP
#include "../../Shader/Implementations/CShaderImplVulkan.hpp"
#include "../../API/ManagedDescriptorSet.hpp"
#include "SColor.hpp"
#include "../Texture/CTexture.hpp"
class CMaterial {
public:
~CMaterial();
void destroy();
bool isInit = false;
SColor m_color;
CTexture* textureAlbedo = nullptr;
CTexture* textureNormal = nullptr;
CShaderImplVulkan* shader;
vk::raii::PipelineLayout pipelineLayout = nullptr;
vk::raii::Pipeline pipeline = nullptr;
std::array<ManagedDescriptorSet, MAX_FRAMES_IN_FLIGHT> m_descriptorSets;
void build();
vk::raii::Pipeline& getPipeline(){return pipeline;};
vk::raii::PipelineLayout& getPipelineLayout(){return pipelineLayout;};
vk::raii::DescriptorSet& getDescriptorSet(uint32_t frameIndex) {
return m_descriptorSets[frameIndex].set;
}
};
#endif

View File

@@ -0,0 +1,31 @@
#ifndef CMATERIALMANAGER_HPP
#define CMATERIALMANAGER_HPP
#include "CMaterial.hpp"
#include <list>
#include <memory>
#include <unordered_map>
class CMaterialManager
{
public:
std::list<std::unique_ptr<CMaterial>> loadedMaterials;
std::unordered_map<std::string, CMaterial*> materialsMap;
void addMaterial(std::string name, std::unique_ptr<CMaterial>&& mat)
{
loadedMaterials.push_back(std::move(mat));
materialsMap[name] = loadedMaterials.back().get();
}
std::optional<CMaterial*> get(std::string name)
{
auto find = materialsMap.find(name);
if(find != materialsMap.end())
{
return find->second;
}
else {
return nullptr;
}
}
};
#endif

View File

@@ -0,0 +1,39 @@
#ifndef SCOLOR_HPP
#define SCOLOR_HPP
#include <glm/glm.hpp>
/**
* @file SColor.hpp
* @brief File for the color struct of a material.
*/
/**
* @brief the number of type in the enum EColorType.
*/
#define COLOR_TYPE_NUMBER 2
/**
* @brief The enum for color type in the material.
*/
enum EColorType {
COLOR_DIFFUSE,
COLOR_SPECULAR,
};
/**
* @brief The struct color of the material.
*/
typedef struct {
/**
* @brief 4D Vector representing the RGBA color.
*/
glm::vec4 m_vector;
/**
* @brief The color's type.
*/
EColorType m_type;
} SColor;
#endif

View File

@@ -0,0 +1,107 @@
#include "CMesh.hpp"
#include "../../API/VulkanImpl.hpp"
#include <vulkan/vulkan_core.h>
#include <vulkan/vulkan_raii.hpp>
CMesh::CMesh(void) :
m_vertexes() {
}
CMesh::~CMesh(void) {
destroy();
}
void CMesh::destroy()
{
VulkanImpl* api = dynamic_cast<VulkanImpl*>(GraphicsAPI::getAPI().get());
api->destroyBuffer(vertexIndexBuffer, api->getCurrentFrameIndex());
/*if (api && vertexIndexBuffer.buffer != VK_NULL_HANDLE)
vmaDestroyBuffer(api->getAllocator(), vertexIndexBuffer.buffer, vertexIndexBuffer.allocation);*/
}
std::vector<SVertex>& CMesh::getVertices(void) {
return m_vertexes;
}
void CMesh::setVertices(std::vector<SVertex> vertices)
{
m_vertexes = vertices;
}
void CMesh::setIndexes(std::vector<uint32_t> indexes){
m_indexes = indexes;
}
std::vector<uint32_t>& CMesh::getIndexes(void){
return m_indexes;
}
/*void CMesh::load(void) {
vk::BufferCreateInfo bufferInfo{{},sizeof(m_vertexes[0]) * m_vertexes.size(), vk::BufferUsageFlagBits::eVertexBuffer, vk::SharingMode::eExclusive};
VulkanImpl* api = dynamic_cast<VulkanImpl*>(GraphicsAPI::getAPI().get());
vertexBuffer = vk::raii::Buffer(*static_cast<vk::raii::Device*>(api->getDevice()), bufferInfo);
vk::MemoryRequirements memRequirements = vertexBuffer.getMemoryRequirements();
vk::MemoryAllocateInfo memoryAllocateInfo{memRequirements.size, api->findMemoryType(memRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent)};
vertexBufferMemory = vk::raii::DeviceMemory(*static_cast<vk::raii::Device*>(api->getDevice()), memoryAllocateInfo);
vertexBuffer.bindMemory(*vertexBufferMemory, 0);
void *data = vertexBufferMemory.mapMemory(0, bufferInfo.size);
memcpy(data, m_vertexes.data(), bufferInfo.size);
vertexBufferMemory.unmapMemory();
}*/
void CMesh::load(void) {
VulkanImpl* api = dynamic_cast<VulkanImpl*>(GraphicsAPI::getAPI().get());
VmaAllocator allocator = api->getAllocator();
VkDeviceSize vertexSize = sizeof(m_vertexes[0]) * m_vertexes.size();
VkDeviceSize indexSize = sizeof(m_indexes[0]) * m_indexes.size();
VkDeviceSize totalSize = vertexSize + indexSize;
// 1. staging buffer - CPU visible, temporaire
VkBufferCreateInfo stagingBufferInfo{};
stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
stagingBufferInfo.size = totalSize;
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo stagingAllocInfo{};
stagingAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
VMABuffer stagingBuffer;
VmaAllocationInfo stagingInfo{};
vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocInfo,
&stagingBuffer.buffer, &stagingBuffer.allocation, &stagingInfo);
// 2. copie des données CPU → staging
memcpy(stagingInfo.pMappedData, m_vertexes.data(), vertexSize);
memcpy((char*)stagingInfo.pMappedData + vertexSize, m_indexes.data(), indexSize);
vmaFlushAllocation(allocator, stagingBuffer.allocation, 0, VK_WHOLE_SIZE);
// 3. vertex buffer final - DEVICE_LOCAL, rapide
VkBufferCreateInfo vertexBufferInfo{};
vertexBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
vertexBufferInfo.size = totalSize;
vertexBufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VmaAllocationCreateInfo vertexAllocInfo{};
vertexAllocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
vmaCreateBuffer(allocator, &vertexBufferInfo, &vertexAllocInfo,
&vertexIndexBuffer.buffer, &vertexIndexBuffer.allocation, nullptr);
// 4. copie GPU staging → vertex buffer via command buffer
api->copyBuffer(stagingBuffer.buffer, vertexIndexBuffer.buffer, totalSize);
m_vertexOffset = 0;
m_indexOffset = vertexSize;
m_indexCount = m_indexes.size();
// 5. détruire le staging buffer
vmaDestroyBuffer(allocator, stagingBuffer.buffer, stagingBuffer.allocation);
}
/*void CMesh::draw(glm::mat4 model, glm::mat4 view, glm::mat4 projection, glm::vec3 lightPos, float intensity) {
}*/

View File

@@ -0,0 +1,53 @@
#ifndef CMESH_HPP
#define CMESH_HPP
#include "SVertex.hpp"
#include "../Material/CMaterial.hpp"
#include <cstdint>
#include <vector>
#include <vulkan/vulkan_raii.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include "../../API/VMABuffer.hpp"
class CMesh {
private:
std::vector<Vertex> m_vertexes;
std::vector<uint32_t> m_indexes;
VMABuffer vertexIndexBuffer;
VkDeviceSize m_vertexOffset = 0;
VkDeviceSize m_indexOffset = 0;
uint32_t m_indexCount = 0;
CMaterial* m_material;
public:
CMesh(void);
~CMesh(void);
std::vector<SVertex>& getVertices(void);
std::vector<uint32_t>& getIndexes(void);
void setVertices(std::vector<SVertex> vertices);
void setIndexes(std::vector<uint32_t> indexes);
void setMaterial(CMaterial* mat){m_material = mat;};
void load(void);
//void draw(glm::mat4 model, glm::mat4 view, glm::mat4 projection, glm::vec3 lightPos, float intensity);
CMaterial* getMaterial(){return m_material;};
VMABuffer& getVertexIndexBuffer(){return vertexIndexBuffer;};
VkDeviceSize getVertexOffset(){return m_vertexOffset;}
VkDeviceSize getIndexOffset(){return m_indexOffset;};
uint32_t getIndexCount(){return m_indexCount;};
void destroy();
};
#endif

View File

@@ -0,0 +1,31 @@
#ifndef CMESHMANAGER_HPP
#define CMESHMANAGER_HPP
#include "CMesh.hpp"
#include <list>
#include <memory>
class CMeshManager
{
public:
std::list<std::unique_ptr<CMesh>> loadedMeshes;
std::unordered_map<std::string, CMesh*> meshesMap;
void addMesh(std::string name, std::unique_ptr<CMesh>&& mesh)
{
loadedMeshes.push_back(std::move(mesh));
meshesMap[name] = loadedMeshes.back().get();
}
std::optional<CMesh*> get(std::string name)
{
auto find = meshesMap.find(name);
if(find != meshesMap.end())
{
return find->second;
}
else {
return nullptr;
}
}
};
#endif

View File

@@ -0,0 +1,70 @@
#ifndef SVERTEX_HPP
#define SVERTEX_HPP
#include "vulkan/vulkan.hpp"
#include <glm/glm.hpp>
/**
* @file SVertex.hpp
* @brief File for the vertex struct.
*/
/**
* @brief the struct Vertex, use to represent vertex and vertex attribute.
*/
typedef struct SVertex{
/**
* @brief Position attribute.
* 3D vector.
*/
glm::vec3 m_position;
/**
* @brief Position attribute.
* 3D vector.
*/
glm::vec4 m_color;
/**
* @brief Normal attribute.
* 3D vector.
*/
glm::vec3 m_normal;
/**
* @brief Texture coordinates attribute.
* 2D vector.
*/
glm::vec2 m_texCoords;
/**
* @brief Tangent attribute.
* 3D vector.
*/
glm::vec3 m_tangent;
/**
* @brief Bitangent attribute.
* 3D vector (perpendicular to m_tangent vector).
*/
glm::vec3 m_bitangent;
// m_normal, m_tangent, m_bitangent are supposed to create a vector base to compute normal map.
static vk::VertexInputBindingDescription getBindingDescription() {
return { 0, sizeof(SVertex), vk::VertexInputRate::eVertex };
}
static std::array<vk::VertexInputAttributeDescription, 6> getAttributeDescriptions() {
return {
vk::VertexInputAttributeDescription( 0, 0, vk::Format::eR32G32B32Sfloat, offsetof(SVertex, m_position) ),
vk::VertexInputAttributeDescription( 1, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(SVertex, m_color) ),
vk::VertexInputAttributeDescription( 2, 0, vk::Format::eR32G32B32Sfloat, offsetof(SVertex, m_normal) ),
vk::VertexInputAttributeDescription( 3, 0, vk::Format::eR32G32Sfloat, offsetof(SVertex, m_texCoords) ),
vk::VertexInputAttributeDescription( 4, 0, vk::Format::eR32G32B32Sfloat, offsetof(SVertex, m_tangent) ),
vk::VertexInputAttributeDescription( 5, 0, vk::Format::eR32G32B32Sfloat, offsetof(SVertex, m_bitangent) )
};
}
} Vertex;
#endif

View File

@@ -0,0 +1,219 @@
#include "CTexture.hpp"
#include "../../API/VulkanImpl.hpp"
CTexture::CTexture(std::string filePath) :
m_filePath(filePath) {
}
std::string CTexture::getFilePath(void) {
return m_filePath;
}
void CTexture::setFilePath(std::string filePath) {
m_filePath = filePath;
}
void CTexture::init(void) {
VulkanImpl* api = dynamic_cast<VulkanImpl*>(GraphicsAPI::getAPI().get());
auto* device= static_cast<vk::raii::Device*>(api->getDevice());
// Read the image file.
SDL_Surface* sdlSurface = IMG_Load(m_filePath.c_str());
if(sdlSurface == nullptr)
{
//error here
}
SDL_PixelFormat sdlFormat = sdlSurface->format;
SDL_Surface* converted = nullptr;
if (sdlFormat == SDL_PIXELFORMAT_RGBA32) {
// déjà RGBA, pas de conversion
converted = sdlSurface;
m_format = vk::Format::eR8G8B8A8Srgb;
} else if (sdlFormat == SDL_PIXELFORMAT_BGRA32) {
// déjà BGRA, pas de conversion mais format différent
converted = sdlSurface;
m_format = vk::Format::eB8G8R8A8Srgb;
} else {
// tout autre format → on force RGBA32
converted = SDL_ConvertSurface(sdlSurface, SDL_PIXELFORMAT_RGBA32);
if (!converted)
throw std::runtime_error("Failed to convert surface: " + m_filePath);
SDL_DestroySurface(sdlSurface);
m_format = vk::Format::eR8G8B8A8Srgb;
}
m_width = converted->w;
m_height = converted->h;
VkDeviceSize imageSize = m_width * m_height * 4;
// 2. staging buffer
VkBufferCreateInfo stagingBufferInfo{};
stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
stagingBufferInfo.size = imageSize;
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo stagingAllocInfo{};
stagingAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
VMABuffer stagingBuffer;
VmaAllocationInfo stagingInfo{};
vmaCreateBuffer(api->getAllocator(), &stagingBufferInfo, &stagingAllocInfo,
&stagingBuffer.buffer, &stagingBuffer.allocation, &stagingInfo);
memcpy(stagingInfo.pMappedData, converted->pixels, imageSize);
// Free the surface.
SDL_DestroySurface(converted);
// 3. crée l'image GPU
api->createImage(
m_width, m_height,
m_format,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
vk::MemoryPropertyFlagBits::eDeviceLocal,
m_textureImage,
m_textureImageMemory
);
// 4. transitions et copie
api->transitionImageLayout(m_textureImage,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal);
{
auto cmd = api->beginSingleTimeCommands();
vk::BufferImageCopy region{
0, 0, 0,
{vk::ImageAspectFlagBits::eColor, 0, 0, 1},
{0, 0, 0},
{static_cast<uint32_t>(m_width), static_cast<uint32_t>(m_height), 1}
};
cmd.copyBufferToImage(
vk::Buffer(stagingBuffer.buffer),
m_textureImage,
vk::ImageLayout::eTransferDstOptimal,
region
);
api->endSingleTimeCommands(cmd);
}
api->transitionImageLayout(m_textureImage,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal);
vmaDestroyBuffer(api->getAllocator(), stagingBuffer.buffer, stagingBuffer.allocation);
// 5. image view
m_imageView = api->createImageView(m_textureImage, m_format, vk::ImageAspectFlagBits::eColor);
// 6. sampler
vk::SamplerCreateInfo samplerInfo{};
samplerInfo.magFilter = vk::Filter::eLinear;
samplerInfo.minFilter = vk::Filter::eLinear;
samplerInfo.addressModeU = vk::SamplerAddressMode::eRepeat;
samplerInfo.addressModeV = vk::SamplerAddressMode::eRepeat;
samplerInfo.addressModeW = vk::SamplerAddressMode::eRepeat;
samplerInfo.anisotropyEnable = vk::True;
samplerInfo.maxAnisotropy = api->getPhysicalDevice().getProperties().limits.maxSamplerAnisotropy;
samplerInfo.borderColor = vk::BorderColor::eIntOpaqueBlack;
samplerInfo.unnormalizedCoordinates = vk::False;
samplerInfo.compareEnable = vk::False;
samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear;
m_sampler = vk::raii::Sampler(*device, samplerInfo);
}
void CTexture::initDefaultTexture()
{
VulkanImpl* api = dynamic_cast<VulkanImpl*>(GraphicsAPI::getAPI().get());
auto* device= static_cast<vk::raii::Device*>(api->getDevice());
// pixel blanc 1x1 RGBA
uint32_t whitePixel = 0x00000ff0;
uint32_t width = 1;
uint32_t height = 1;
VkDeviceSize imageSize = sizeof(uint32_t);
// staging buffer
VkBufferCreateInfo stagingBufferInfo{};
stagingBufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
stagingBufferInfo.size = imageSize;
stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo stagingAllocInfo{};
stagingAllocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
VMABuffer stagingBuffer;
VmaAllocationInfo stagingInfo{};
vmaCreateBuffer(api->getAllocator(), &stagingBufferInfo, &stagingAllocInfo,
&stagingBuffer.buffer, &stagingBuffer.allocation, &stagingInfo);
memcpy(stagingInfo.pMappedData, &whitePixel, imageSize);
// image GPU
api->createImage(width, height,
vk::Format::eR8G8B8A8Srgb,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
vk::MemoryPropertyFlagBits::eDeviceLocal,
m_textureImage,
m_textureImageMemory
);
api->transitionImageLayout(m_textureImage,
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal);
{
auto cmd = api->beginSingleTimeCommands();
vk::BufferImageCopy region{
0, 0, 0,
{vk::ImageAspectFlagBits::eColor, 0, 0, 1},
{0, 0, 0},
{width, height, 1}
};
cmd.copyBufferToImage(
vk::Buffer(stagingBuffer.buffer),
m_textureImage,
vk::ImageLayout::eTransferDstOptimal,
region
);
api->endSingleTimeCommands(cmd);
}
api->transitionImageLayout(m_textureImage,
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal);
vmaDestroyBuffer(api->getAllocator(), stagingBuffer.buffer, stagingBuffer.allocation);
// image view
m_imageView = api->createImageView(
m_textureImage,
vk::Format::eR8G8B8A8Srgb,
vk::ImageAspectFlagBits::eColor
);
// sampler
vk::SamplerCreateInfo samplerInfo{};
samplerInfo.magFilter = vk::Filter::eLinear;
samplerInfo.minFilter = vk::Filter::eLinear;
samplerInfo.addressModeU = vk::SamplerAddressMode::eRepeat;
samplerInfo.addressModeV = vk::SamplerAddressMode::eRepeat;
samplerInfo.addressModeW = vk::SamplerAddressMode::eRepeat;
samplerInfo.anisotropyEnable = vk::False;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.borderColor = vk::BorderColor::eIntOpaqueBlack;
samplerInfo.unnormalizedCoordinates = vk::False;
samplerInfo.compareEnable = vk::False;
samplerInfo.mipmapMode = vk::SamplerMipmapMode::eLinear;
m_sampler = vk::raii::Sampler(*device, samplerInfo);
}

View File

@@ -0,0 +1,45 @@
#ifndef CTEXTURE_HPP
#define CTEXTURE_HPP
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <string>
#include <vulkan/vulkan_raii.hpp>
class CTexture{
private:
// Path of the file.
std::string m_filePath;
vk::raii::Image m_textureImage = nullptr;
vk::raii::DeviceMemory m_textureImageMemory = nullptr;
vk::raii::ImageView m_imageView = nullptr;
vk::raii::Sampler m_sampler = nullptr;
uint32_t m_width = 0;
uint32_t m_height = 0;
vk::Format m_format = vk::Format::eR8G8B8A8Srgb;
public:
CTexture(void) = delete;
CTexture(const CTexture&) = delete;
CTexture& operator=(const CTexture&) = delete;
~CTexture(void) {
m_sampler = nullptr;
m_imageView = nullptr;
m_textureImage = nullptr;
m_textureImageMemory = nullptr;
};
CTexture(std::string filePath);
std::string getFilePath(void);
void setFilePath(std::string filePath);
void init(void);
void initDefaultTexture();
vk::raii::ImageView& getImageView() { return m_imageView; }
vk::raii::Sampler& getSampler() { return m_sampler; }
uint32_t getWidth() const { return m_width; }
uint32_t getHeight() const { return m_height; }
};
#endif

View File

@@ -0,0 +1,32 @@
#ifndef CTEXTUREMANAGER_HPP
#define CTEXTUREMANAGER_HPP
#include "CTexture.hpp"
#include <list>
#include <memory>
#include <optional>
#include <unordered_map>
class CTextureManager
{
public:
std::list<std::unique_ptr<CTexture>> loadedTextures;
std::unordered_map<std::string, CTexture*> texturesMap;
void addTexture(std::string name, std::unique_ptr<CTexture>&& text)
{
loadedTextures.push_back(std::move(text));
texturesMap[name] = loadedTextures.back().get();
}
std::optional<CTexture*> get(std::string name)
{
auto find = texturesMap.find(name);
if(find != texturesMap.end())
{
return find->second;
}
else {
return nullptr;
}
}
};
#endif