finished material and mesh serialisation, added model class

This commit is contained in:
Anselme 2016-06-24 17:11:05 +02:00
parent 116a5e3d1a
commit 66784724d8
8 changed files with 510 additions and 318 deletions

View File

@ -78,7 +78,7 @@ void Mesh::initGL()
{ {
auto posBuffer = new TBuffer<glm::vec3>(positions3D, Buffer::VBO); auto posBuffer = new TBuffer<glm::vec3>(positions3D, Buffer::VBO);
posBuffer->setVertexAttrib(0, 3); posBuffer->setVertexAttrib(0, 3);
addBuffer(posBuffer, POSITION_BUFFER); addBuffer(posBuffer, POSITION3D_BUFFER);
// init normals vbo // init normals vbo
if(!normals.empty()) if(!normals.empty())
@ -100,7 +100,7 @@ void Mesh::initGL()
{ {
auto posBuffer = new TBuffer<glm::vec2>(positions2D, Buffer::VBO); auto posBuffer = new TBuffer<glm::vec2>(positions2D, Buffer::VBO);
posBuffer->setVertexAttrib(0, 2); posBuffer->setVertexAttrib(0, 2);
addBuffer(posBuffer, POSITION_BUFFER); addBuffer(posBuffer, POSITION2D_BUFFER);
} }
else else
{ {
@ -184,22 +184,33 @@ void Mesh::destroyGL()
clearBuffers(); 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 Mesh::getFlags()
{ {
unsigned int flags = material->getFlags(); unsigned int flags = material->getFlags();
if(!indices.empty()) if(!(indices.empty() || buffersId[INDICES_BUFFER] == -1))
flags |= 1 << MESH_INDEXED; flags |= 1 << MESH_INDEXED;
if(!texCoords.empty()) if(!(texCoords.empty() || buffersId[TEXCOORD_BUFFER] == -1))
flags |= 1 << MESH_TEXTURABLE; flags |= 1 << MESH_TEXTURABLE;
if(!instances_offsets.empty()) if(!(instances_offsets.empty() || buffersId[INSTANCE_BUFFER] == -1))
flags |= 1 << MESH_INSTANCED; flags |= 1 << MESH_INSTANCED;
if(!positions3D.empty()) if(!(positions3D.empty() || buffersId[POSITION3D_BUFFER] == -1))
{ {
flags |= 1 << MESH_3D; flags |= 1 << MESH_3D;
if(!tangents.empty()) if((!tangents.empty() || buffersId[TANGENT_BUFFER] == -1))
flags |= 1 << MESH_TANGENT_SPACE; flags |= 1 << MESH_TANGENT_SPACE;
if(isDoubleSided) if(isDoubleSided)
flags |= 1 << MESH_DOUBLE_SIDED; flags |= 1 << MESH_DOUBLE_SIDED;
@ -399,3 +410,176 @@ void Mesh::computeBoundingBox(glm::vec3 &min, glm::vec3 &max)
max.z = pos.z; max.z = pos.z;
} }
} }
// serialisation methods
struct MeshHeader
{
unsigned int flags;
int nbPositions;
int depth;
int nbInstances_offsets;
int nbIndices;
int nameLength;
};
template<typename T>
bool writeBuffer(const std::vector<T> &vec, std::FILE *file)
{
size_t nbWritten = std::fwrite(vec.data(), sizeof(T), vec.size(), file);
return (nbWritten == vec.size());
}
template<typename T>
bool readBuffer(std::vector<T> &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;
}

View File

@ -102,6 +102,11 @@ public:
void initGL(); void initGL();
void draw(Shader* shader, bool drawNormals = true, bool drawTexCoord = true, bool drawTangents = true); void draw(Shader* shader, bool drawNormals = true, bool drawTexCoord = true, bool drawTangents = true);
void destroyGL(); 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 * @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); 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 */ /* ADVANCED CUSTOMISATION */
/*************************************************************/ /*************************************************************/
@ -224,7 +247,8 @@ protected:
enum { enum {
// required buffer // required buffer
POSITION_BUFFER, POSITION2D_BUFFER,
POSITION3D_BUFFER,
// indices buffers // indices buffers
INDICES_BUFFER, INDICES_BUFFER,

116
src/model.cpp Normal file
View File

@ -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;
}

90
src/model.h Normal file
View File

@ -0,0 +1,90 @@
#ifndef MODEL_H
#define MODEL_H
#include <vector>
#include <string>
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<Mesh*> m_meshes;
std::vector<TextureImg> 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<Mesh*>& 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

View File

@ -64,3 +64,76 @@ unsigned int PhongMaterial::getFlags()
return flags; 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; i<PhongMaterial::NB_PHONG_SLOTS; ++i)
{
if(mat->textures[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<PhongMaterial::NB_PHONG_SLOTS; ++i)
{
if(header.textureLengths[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; i<PhongMaterial::NB_PHONG_SLOTS; ++i)
{
char str[256];
if(!std::fread(str, header.textureLengths[i], 1, file))
{ delete mat; return NULL; }
mat->textureNames[i] = std::string(str);
}
return mat;
}

View File

@ -44,6 +44,21 @@ struct PhongMaterial : public Material
virtual void bindAttributes(Shader* myShader); virtual void bindAttributes(Shader* myShader);
virtual unsigned int getFlags(); 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 #endif // PHONGMATERIAL_H

View File

@ -1,291 +0,0 @@
#include "serializers.h"
#include <cstdio>
#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<typename T>
bool writeBuffer(const std::vector<T> &vec, std::FILE *file);
template<typename T>
bool readBuffer(std::vector<T> &vec, std::FILE *file);
// IMPLEMENTATIONS
template<typename T>
bool writeBuffer(const std::vector<T> &vec, std::FILE *file)
{
size_t nbWritten = std::fwrite(vec.data(), sizeof(T), vec.size(), file);
return (nbWritten == vec.size());
}
template<typename T>
bool readBuffer(std::vector<T> &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)
{
}
}

View File

@ -1,19 +0,0 @@
#ifndef SERIALIZERS_H
#define SERIALIZERS_H
#include <string>
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