diff --git a/src/mesh.cpp b/src/mesh.cpp index 061c6dd..77ec91e 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -78,7 +78,7 @@ void Mesh::initGL() { auto posBuffer = new TBuffer(positions3D, Buffer::VBO); posBuffer->setVertexAttrib(0, 3); - addBuffer(posBuffer, POSITION_BUFFER); + addBuffer(posBuffer, POSITION3D_BUFFER); // init normals vbo if(!normals.empty()) @@ -100,7 +100,7 @@ void Mesh::initGL() { auto posBuffer = new TBuffer(positions2D, Buffer::VBO); posBuffer->setVertexAttrib(0, 2); - addBuffer(posBuffer, POSITION_BUFFER); + addBuffer(posBuffer, POSITION2D_BUFFER); } else { @@ -184,22 +184,33 @@ void Mesh::destroyGL() clearBuffers(); } +void Mesh::clearData() +{ + positions3D.clear(); + normals.clear(); + tangents.clear(); + positions2D.clear(); + texCoords.clear(); + instances_offsets.clear(); + indices.clear(); +} + unsigned int Mesh::getFlags() { unsigned int flags = material->getFlags(); - if(!indices.empty()) + if(!(indices.empty() || buffersId[INDICES_BUFFER] == -1)) flags |= 1 << MESH_INDEXED; - if(!texCoords.empty()) + if(!(texCoords.empty() || buffersId[TEXCOORD_BUFFER] == -1)) flags |= 1 << MESH_TEXTURABLE; - if(!instances_offsets.empty()) + if(!(instances_offsets.empty() || buffersId[INSTANCE_BUFFER] == -1)) flags |= 1 << MESH_INSTANCED; - if(!positions3D.empty()) + if(!(positions3D.empty() || buffersId[POSITION3D_BUFFER] == -1)) { flags |= 1 << MESH_3D; - if(!tangents.empty()) + if((!tangents.empty() || buffersId[TANGENT_BUFFER] == -1)) flags |= 1 << MESH_TANGENT_SPACE; if(isDoubleSided) flags |= 1 << MESH_DOUBLE_SIDED; @@ -399,3 +410,176 @@ void Mesh::computeBoundingBox(glm::vec3 &min, glm::vec3 &max) max.z = pos.z; } } + +// serialisation methods + +struct MeshHeader +{ + unsigned int flags; + int nbPositions; + int depth; + int nbInstances_offsets; + int nbIndices; + int nameLength; +}; + +template +bool writeBuffer(const std::vector &vec, std::FILE *file) +{ + size_t nbWritten = std::fwrite(vec.data(), sizeof(T), vec.size(), file); + return (nbWritten == vec.size()); +} + +template +bool readBuffer(std::vector &vec, std::FILE *file) +{ + size_t nbRead = std::fread(vec.data(), sizeof(T), vec.size(), file); + return (nbRead == vec.size()); +} + +bool Mesh::serialize(Mesh* mesh, FILE *file) +{ + // creating header + MeshHeader header; + header.flags = mesh->getFlags(); + if(header.flags & (1 << Mesh::MESH_3D)) + header.nbPositions = mesh->positions3D.size(); + else + { + header.nbPositions = mesh->positions2D.size(); + header.depth = mesh->getDepth(); + } + header.nbIndices = mesh->indices.size(); + header.nbInstances_offsets = mesh->instances_offsets.size(); + header.nameLength = mesh->getName().size(); + + if(header.nbPositions == 0) + return false; + + // writing header + size_t nbWritten; + + nbWritten = std::fwrite(&header, sizeof(MeshHeader), 1, file); + if(nbWritten != 1) + return false; + + if(header.nameLength != 0) + { + nbWritten = std::fwrite(mesh->getName().data(), header.nameLength, 1, file); + if(nbWritten != 1) + return false; + } + + // writing buffers + if(header.flags & (1 << Mesh::MESH_3D)) + { + if(!writeBuffer(mesh->positions3D, file)) + return false; + if(mesh->normals.size() == 0) + mesh->computeNormals(); + if(!writeBuffer(mesh->normals, file)) + return false; + if(header.flags & (1 << Mesh::MESH_TANGENT_SPACE)) + { + if(!writeBuffer(mesh->tangents, file)) + return false; + } + } + else + { + if(!writeBuffer(mesh->positions2D, file)) + return false; + } + + if(header.nbInstances_offsets) + { + if(!writeBuffer(mesh->instances_offsets, file)) + return false; + } + + if(header.nbIndices) + { + if(!writeBuffer(mesh->indices, file)) + return false; + } + + if(header.flags & (1 << Mesh::MESH_TEXTURABLE)) + { + if(!writeBuffer(mesh->texCoords, file)) + return false; + } + + return true; +} + +Mesh* Mesh::deserialize(FILE *file) +{ + MeshHeader header; + size_t nbRead = std::fread(&header, sizeof(MeshHeader), 1, file); + + // deserializing mesh + Mesh *mesh = NULL; + if(nbRead == 1 && header.nbPositions != 0) + mesh = new Mesh(); + else + return NULL; + + std::string name = "mesh"; + if(header.nameLength != 0) + { + name.resize(header.nameLength); + if(!fread(&(name[0]), header.nameLength, 1, file)) + { delete(mesh); return NULL; } + } + mesh->setName(name); + + if(header.flags & (1 << Mesh::MESH_3D)) + { + mesh->positions3D.reserve(header.nbPositions); + if(!readBuffer(mesh->positions3D, file)) + { delete(mesh); return NULL; } + mesh->normals.reserve(header.nbPositions); + if(!readBuffer(mesh->normals, file)) + { delete(mesh); return NULL; } + if(header.flags & (1 << Mesh::MESH_TANGENT_SPACE)) + { + mesh->tangents.reserve(header.nbPositions); + if(!readBuffer(mesh->tangents, file)) + { delete(mesh); return NULL; } + } + } + else + { + mesh->positions2D.reserve(header.nbPositions); + if(!readBuffer(mesh->positions2D, file)) + { delete(mesh); return NULL; } + mesh->setDepth(header.depth); + } + + if(header.nbInstances_offsets) + { + mesh->instances_offsets.reserve(header.nbInstances_offsets); + if(!readBuffer(mesh->instances_offsets, file)) + { delete(mesh); return NULL; } + } + + if(header.nbIndices) + { + mesh->indices.reserve(header.nbIndices); + if(!readBuffer(mesh->indices, file)) + { delete(mesh); return NULL; } + } + + if(header.flags & (1 << Mesh::MESH_TEXTURABLE)) + { + mesh->texCoords.reserve(header.nbPositions); + if(!readBuffer(mesh->texCoords, file)) + { delete(mesh); return NULL; } + } + + mesh->setIsBillboard(header.flags & (1 << Mesh::MESH_BILLBOARD)); + mesh->setIsDoubleSided(header.flags & (1 << Mesh::MESH_DOUBLE_SIDED)); + mesh->setIsShadowCaster(header.flags & (1 << Mesh::MESH_SHADOWED)); + + return mesh; +} diff --git a/src/mesh.h b/src/mesh.h index a9a0069..ddf68fe 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -102,6 +102,11 @@ public: void initGL(); void draw(Shader* shader, bool drawNormals = true, bool drawTexCoord = true, bool drawTangents = true); void destroyGL(); + + /** + * @brief clearData clears all data vectors, but keeps GPU data allocated. + */ + void clearData(); /** * @brief getFlags returns the flags that defines the specificities of the mesh and his material @@ -197,6 +202,24 @@ public: */ void computeBoundingBox(glm::vec3 &min, glm::vec3 &max); + /*************************************************************/ + /* SERIALISATION */ + /*************************************************************/ + + /** + * @brief serializeMesh can be used to save a mesh + * @return true if the mesh has succesfully been saved + */ + static bool serialize(Mesh* mesh, FILE *file); + + bool serialize(FILE *file) { return serialize(this, file); } + + /** + * @brief deserializeMesh can be used to load a mesh + * @return the loaded mesh of NULL if a reading error has occured + */ + static Mesh* deserialize(FILE *file); + /*************************************************************/ /* ADVANCED CUSTOMISATION */ /*************************************************************/ @@ -224,7 +247,8 @@ protected: enum { // required buffer - POSITION_BUFFER, + POSITION2D_BUFFER, + POSITION3D_BUFFER, // indices buffers INDICES_BUFFER, diff --git a/src/model.cpp b/src/model.cpp new file mode 100644 index 0000000..a1c4c25 --- /dev/null +++ b/src/model.cpp @@ -0,0 +1,116 @@ +#include "model.h" +#include "mesh.h" +#include "image.h" +#include "texture.h" +#include "phongmaterial.h" + +Model::~Model() +{ + destroy(); + destroyGL(); + for(Mesh *m : m_meshes) + delete m; +} + +Image* Model::getImage(const std::string &name) +{ + for(TextureImg &tex : m_textures) + { + if(name.compare(tex.name) == 0) + return tex.img; + } + return NULL; +} + +Texture* Model::getTexture(const std::string &name) +{ + for(TextureImg &tex : m_textures) + { + if(name.compare(tex.name) == 0) + return tex.tex; + } + return NULL; +} + +void Model::destroy() +{ + for(Mesh* m : m_meshes) + m->clearData(); + for(TextureImg &tex : m_textures) + { + delete tex.img; + tex.img = NULL; + } +} + +void Model::initGL() +{ + for(Mesh* m : m_meshes) + m->initGL(); + for(TextureImg &tex : m_textures) + { + if(tex.tex == NULL) + tex.tex = new Texture(tex.img); + } +} + +void Model::destroyGL() +{ + for(Mesh* m : m_meshes) + m->destroyGL(); + for(TextureImg &tex : m_textures) + { + if(tex.tex != NULL) + { + delete tex.tex; + tex.tex = NULL; + } + } +} + +bool Model::save(const std::string &filename, const std::string &texturesPath) +{ + // open file + std::FILE *file = std::fopen(filename.c_str(), "w"); + if(file == NULL) + return false; + + bool ok = false; + int size = m_meshes.size(); + if(std::fwrite(&size, sizeof(int), 1, file)) + { + ok = true; + for(Mesh* m : m_meshes) + { + ok = ok && m->serialize(file); + PhongMaterial* mat = (PhongMaterial*)m->getMaterial(); + ok = ok && mat->serialize(file); + } + for(Model::TextureImg &texImg : m_textures) + { + std::string texFilename = texturesPath + texImg.name + ".png"; + if(texImg.img == NULL && texImg.tex != NULL) + texImg.img = texImg.tex->getData(); + if(texImg.img != NULL) + ok = ok && texImg.img->save(texFilename); + } + std::fclose(file); + } + + // finishing + return ok; +} + +Model* Model::load(const std::string &filename, const std::string &texturesPath) +{ + // open file + std::FILE *file = std::fopen(filename.c_str(), "r"); + if(file == NULL) + return NULL; + + bool ok = false; + + + // finishing + return NULL; +} diff --git a/src/model.h b/src/model.h new file mode 100644 index 0000000..cd2dda0 --- /dev/null +++ b/src/model.h @@ -0,0 +1,90 @@ +#ifndef MODEL_H +#define MODEL_H + +#include +#include + +class Mesh; +class Image; +class Texture; + +class Model +{ +protected: + struct TextureImg + { + std::string name; + Texture* tex; + Image* img; + + TextureImg(const std::string n, Image* i) : + name(n), tex(NULL), img(i) {} + }; + + std::vector m_meshes; + std::vector m_textures; + +public: + ~Model(); + + /** + * @brief addMesh adds a mesh to the mesh list + */ + void addMesh(Mesh *mesh) { m_meshes.push_back(mesh); } + + /** + * @brief addImage adds an image to the texture pack + */ + void addImage(const std::string &name, Image* img) + { + m_textures.push_back(TextureImg(name, img)); + } + + /** + * @brief getMeshes returns the mesh array + */ + const std::vector& getMeshes() { return m_meshes; } + + /** + * @brief getImage returns the image which has the specified name, or NULL if there are no images of this name. + */ + Image* getImage(const std::string &name); + + /** + * @brief getTexture returns the texture which has the specified name, or NULL if there are no images of this name. + */ + Texture* getTexture(const std::string &name); + + /** + * @brief destroy frees Mesh and Image memory in the RAM, but it stays allocated in GPU memory. + */ + void destroy(); + + /** + * @brief initGL uploads image and mesh data to GPU memory + */ + void initGL(); + + /** + * @brief destroyGL frees the meshes and the textures from GPU memory + */ + void destroyGL(); + + /** + * @brief save saves the model on a file + * @param filename is the name of the resulting file + * @param texturesPath is the existing folder where the textures will be saved + * @return true if the save succeeded + */ + bool save(const std::string &filename, const std::string &texturesPath); + + /** + * @brief load loads the model from a file + * @param filename is the name of the target file + * @param texturesPath is the existing folder where the textures will be gathered + * @return the model if the loading succeeded, or NULL if it failed + */ + static Model* load(const std::string &filename, const std::string &texturesPath); +}; + +#endif // MODEL_H diff --git a/src/phongmaterial.cpp b/src/phongmaterial.cpp index c2cbbe6..e34aebc 100644 --- a/src/phongmaterial.cpp +++ b/src/phongmaterial.cpp @@ -64,3 +64,76 @@ unsigned int PhongMaterial::getFlags() return flags; } +// serialisation methods : + +struct MaterialHeader +{ + glm::vec3 emission; + glm::vec3 diffuse; + glm::vec3 specular; + float shininess; + int textureLengths[PhongMaterial::NB_PHONG_SLOTS]; +}; + +bool PhongMaterial::serialize(PhongMaterial* mat, FILE *file) +{ + unsigned int flags = mat->getFlags(); + + if(!fwrite(&flags, sizeof(unsigned int), 1, file)) + return false; + + if(flags & (1 << Mesh::MATERIAL_PHONG)) + { + MaterialHeader header; + header.emission = mat->emission; + header.diffuse = mat->diffuse; + header.specular = mat->specular; + header.shininess = mat->shininess; + + for(int i=0; itextures[i] != NULL) + header.textureLengths[i] = mat->textureNames[i].length()+1; + } + + if(!std::fwrite(&header, sizeof(MaterialHeader), 1, file)) + return false; + for(int i=0; i 0) + { + if(!std::fwrite(mat->textureNames[i].c_str(), header.textureLengths[i], 1, file)) + return false; + } + } + } + + return true; +} + +PhongMaterial* PhongMaterial::deserialize(FILE *file) +{ + int flags; + if(!std::fread(&flags, sizeof(int), 1, file)) + return NULL; + + MaterialHeader header; + if(!std::fread(&header, sizeof(MaterialHeader), 1, file)) + return NULL; + + PhongMaterial *mat = new PhongMaterial(); + + mat->diffuse = header.diffuse; + mat->emission = header.emission; + mat->specular = header.specular; + mat->shininess = header.shininess; + for(int i=0; itextureNames[i] = std::string(str); + } + + return mat; +} diff --git a/src/phongmaterial.h b/src/phongmaterial.h index 28db003..a6b92d2 100644 --- a/src/phongmaterial.h +++ b/src/phongmaterial.h @@ -44,6 +44,21 @@ struct PhongMaterial : public Material virtual void bindAttributes(Shader* myShader); virtual unsigned int getFlags(); + + /** + * @brief serializeMaterial saves a material to a file + * @return true if the save succeeded + */ + static bool serialize(PhongMaterial* mat, FILE *file); + bool serialize(FILE *file) { return serialize(this, file); } + + /** + * @brief deserializeMaterial creates a material from reading a file, + * be careful, this method has no way to load the attached textures, so you must do it manually, + * however, you can use the texture names stored in the textureNames array to find which textures are requested. + * @return the material or NULL if the loading failed + */ + static PhongMaterial* deserialize(FILE *file); }; #endif // PHONGMATERIAL_H diff --git a/src/serializers.cpp b/src/serializers.cpp deleted file mode 100644 index a727123..0000000 --- a/src/serializers.cpp +++ /dev/null @@ -1,291 +0,0 @@ -#include "serializers.h" - -#include - -#include "mesh.h" -#include "light.h" -#include "texture.h" -#include "phongmaterial.h" - -namespace serializers -{ - -// STRUCTS - -struct MeshHeader -{ - unsigned int flags; - int nbPositions; - int depth; - int nbInstances_offsets; - int nbIndices; - int nameLength; -}; - -struct MaterialHeader -{ - glm::vec3 emission; - glm::vec3 diffuse; - glm::vec3 specular; - float shininess; -}; - -struct LightHeader -{ - -}; - -// PRIVATE FUNCTIONS - -bool serializeMesh(Mesh* mesh, const MeshHeader &header, FILE *file); - -bool deserializeMesh(Mesh* mesh, const MeshHeader &header, FILE *file); - -bool serializeMaterial(Material* material, FILE *file); - -Material* deserializeMaterial(FILE *file); - -template -bool writeBuffer(const std::vector &vec, std::FILE *file); - -template -bool readBuffer(std::vector &vec, std::FILE *file); - -// IMPLEMENTATIONS - -template -bool writeBuffer(const std::vector &vec, std::FILE *file) -{ - size_t nbWritten = std::fwrite(vec.data(), sizeof(T), vec.size(), file); - return (nbWritten == vec.size()); -} - -template -bool readBuffer(std::vector &vec, std::FILE *file) -{ - size_t nbRead = std::fread(vec.data(), sizeof(T), vec.size(), file); - return (nbRead == vec.size()); -} - -bool saveMesh(const std::string &filename, Mesh *mesh) -{ - // open file - std::FILE *file = std::fopen(filename.c_str(), "w"); - if(file == NULL) - return false; - - // creating header - MeshHeader header; - header.flags = mesh->getFlags(); - if(header.flags & (1 << Mesh::MESH_3D)) - header.nbPositions = mesh->positions3D.size(); - else - { - mesh->positions2D.size(); - header.depth = mesh->getDepth(); - } - header.nbIndices = mesh->indices.size(); - header.nbInstances_offsets = mesh->instances_offsets.size(); - header.nameLength = mesh->getName().size(); - - // serializing - bool ok = false; - if(header.nbPositions != 0) - ok = serializeMesh(mesh, header, file); - - // finishing - std::fclose(file); - return ok; -} - -bool serializeMesh(Mesh* mesh, const MeshHeader &header, FILE *file) -{ - // writing header - size_t nbWritten; - - nbWritten = std::fwrite(&header, sizeof(MeshHeader), 1, file); - if(nbWritten != 1) - return false; - - if(header.nameLength != 0) - { - nbWritten = std::fwrite(mesh->getName().data(), header.nameLength, 1, file); - if(nbWritten != 1) - return false; - } - - // writing buffers - if(header.flags & (1 << Mesh::MESH_3D)) - { - if(!writeBuffer(mesh->positions3D, file)) - return false; - if(mesh->normals.size() == 0) - mesh->computeNormals(); - if(!writeBuffer(mesh->normals, file)) - return false; - if(header.flags & (1 << Mesh::MESH_TANGENT_SPACE)) - { - if(!writeBuffer(mesh->tangents, file)) - return false; - } - } - else - { - if(!writeBuffer(mesh->positions2D, file)) - return false; - } - - if(header.nbInstances_offsets) - { - if(!writeBuffer(mesh->instances_offsets, file)) - return false; - } - - if(header.nbIndices) - { - if(!writeBuffer(mesh->indices, file)) - return false; - } - - if(header.flags & (1 << Mesh::MESH_TEXTURABLE)) - { - if(!writeBuffer(mesh->texCoords, file)) - return false; - } - - return true; -} - -bool serializeMaterial(Material* material, FILE *file) -{ - unsigned int flags = material->getFlags(); - - if(!fwrite(&flags, sizeof(unsigned int), 1, file)) - return false; - - if(flags & (1 << Mesh::MATERIAL_PHONG)) - { - MaterialHeader header; - PhongMaterial *mat = (PhongMaterial*)material; - header.emission = mat->emission; - header.diffuse = mat->diffuse; - header.specular = mat->specular; - header.shininess = mat->shininess; - } - return true; -} - -bool saveLight(const std::string &filename, Light *light) -{ - std::FILE *file = std::fopen(filename.c_str(), "w"); - if(file == NULL) - return false; - - std::fclose(file); -} - -Mesh* loadMesh(const std::string &filename) -{ - // open file - std::FILE *file = std::fopen(filename.c_str(), "r"); - if(file == NULL) - return NULL; - - // reading header - MeshHeader header; - size_t nbRead = std::fread(&header, sizeof(MeshHeader), 1, file); - - // deserializing mesh - bool ok = false; - Mesh *mesh = NULL; - if(nbRead == 1 && header.nbPositions != 0) - { - mesh = new Mesh(); - ok = deserializeMesh(mesh, header, file); - } - - // finishing - std::fclose(file); - - if(!ok && mesh != NULL) - { - delete mesh; - mesh = NULL; - } - return mesh; -} - -bool deserializeMesh(Mesh* mesh, const MeshHeader &header, FILE *file) -{ - std::string name = "mesh"; - if(header.nameLength != 0) - { - name.resize(header.nameLength); - if(!fread(&(name[0]), header.nameLength, 1, file)) - return false; - } - mesh->setName(name); - - if(header.flags & (1 << Mesh::MESH_3D)) - { - mesh->positions3D.reserve(header.nbPositions); - if(!readBuffer(mesh->positions3D, file)) - return false; - mesh->normals.reserve(header.nbPositions); - if(!readBuffer(mesh->normals, file)) - return false; - if(header.flags & (1 << Mesh::MESH_TANGENT_SPACE)) - { - mesh->tangents.reserve(header.nbPositions); - if(!readBuffer(mesh->tangents, file)) - return false; - } - } - else - { - mesh->positions2D.reserve(header.nbPositions); - if(!readBuffer(mesh->positions2D, file)) - return false; - mesh->setDepth(header.depth); - } - - if(header.nbInstances_offsets) - { - mesh->instances_offsets.reserve(header.nbInstances_offsets); - if(!readBuffer(mesh->instances_offsets, file)) - return false; - } - - if(header.nbIndices) - { - mesh->indices.reserve(header.nbIndices); - if(!readBuffer(mesh->indices, file)) - return false; - } - - if(header.flags & (1 << Mesh::MESH_TEXTURABLE)) - { - mesh->texCoords.reserve(header.nbPositions); - if(!readBuffer(mesh->texCoords, file)) - return false; - } - - mesh->setIsBillboard(header.flags & (1 << Mesh::MESH_BILLBOARD)); - mesh->setIsDoubleSided(header.flags & (1 << Mesh::MESH_DOUBLE_SIDED)); - mesh->setIsShadowCaster(header.flags & (1 << Mesh::MESH_SHADOWED)); - - return true; -} - - -Material* deserializeMaterial(FILE *file) -{ - -} - -Light* loadLight(const std::string &filename) -{ - -} - -} diff --git a/src/serializers.h b/src/serializers.h deleted file mode 100644 index c59d899..0000000 --- a/src/serializers.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef SERIALIZERS_H -#define SERIALIZERS_H - -#include - -class Mesh; -class Light; -class Texture; - -namespace Serializers -{ - bool saveMesh(const std::string &filename, Mesh *mesh); - bool saveLight(const std::string &filename, Light *light); - - Mesh* loadMesh(const std::string &filename); - Light* loadLight(const std::string &filename); -} - -#endif // SERIALIZERS_H