Skip to content

Instantly share code, notes, and snippets.

@dolinenkov
Last active February 14, 2019 22:01
Show Gist options
  • Save dolinenkov/5d0d2d68366987d26fc40bda57b54ae7 to your computer and use it in GitHub Desktop.
Save dolinenkov/5d0d2d68366987d26fc40bda57b54ae7 to your computer and use it in GitHub Desktop.
#ifndef __MODEL_FORMAT_HH__
#define __MODEL_FORMAT_HH__
#if !defined(__cplusplus)
#error "C++11 conformant compiler is required"
#endif
#include <cstdint>
#include <cstdio>
#include <memory>
#include <ostream>
#include <fstream>
#include <iostream>
#include <type_traits>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
namespace rw
{
struct Model
{
static constexpr size_t POSITION_COMPONENTS = 3;
static constexpr size_t NORMAL_COMPONENTS = 3;
static constexpr size_t TEXTURE_COMPONENTS = 2;
using Indice = uint32_t;
struct Vertice;
struct Mesh;
struct Material;
/*
SerializationStream:
implements 2 template methods:
- apply<T>(T * ptr, size_t size)
reads or writes `size` entries of plain old data of type T from/to memory addressed by `ptr`
- prepare<T>(std::unique_ptr<T[]> * ptr, size_t size)
prepares storage for data of type T at address `ptr` so it will have size `size`
*/
template<class SerializationStream>
bool serialization(SerializationStream &);
std::unique_ptr<Material[]> materials;
std::uint32_t materialCount = 0;
std::unique_ptr<Mesh[]> meshes;
std::uint32_t meshCount = 0;
};
struct Model::Vertice
{
float position[POSITION_COMPONENTS];
float normal[NORMAL_COMPONENTS];
float texture[TEXTURE_COMPONENTS];
};
struct Model::Mesh
{
std::unique_ptr<Indice[]> index;
std::uint32_t indexCount = 0;
std::unique_ptr<Vertice[]> vertex;
std::uint32_t vertexCount = 0;
std::uint32_t materialIndex = 0;
void * impl = nullptr;
};
struct Model::Material
{
std::unique_ptr<char[]> textureName;
void * impl = nullptr;
};
class StandardBinaryReader
{
public:
StandardBinaryReader(FILE *);
template<class T>
bool apply(T *, size_t);
template<class T>
void prepare(std::unique_ptr<T[]> *, size_t);
private:
FILE * file;
};
class StandardBinaryWriter
{
public:
StandardBinaryWriter(FILE *);
template<class T>
bool apply(T *, size_t);
template<class T>
void prepare(std::unique_ptr<T[]> *, size_t);
private:
FILE * file;
};
class StandardStreamWriter
{
public:
StandardStreamWriter(std::ostream &);
template<class T>
bool apply(T *, size_t);
template<class T>
void prepare(std::unique_ptr<T[]> *, size_t);
private:
std::ostream & stream;
};
int converter_main(int argc, const char * const * argv);
//
// template methods
//
template<class SerializationStream>
bool Model::serialization(SerializationStream & stream)
{
if (!stream.apply(&materialCount, 1) || !stream.apply(&meshCount, 1))
return false;
stream.prepare(&materials, materialCount);
for (std::uint32_t i = 0; i < materialCount; ++i)
{
std::uint32_t textureNameLength = strlen(materials[i].textureName.get()) + 1;
if (!stream.apply(&textureNameLength, 1))
return false;
stream.prepare(&materials[i].textureName, textureNameLength);
if (!stream.apply(materials[i].textureName.get(), textureNameLength))
return false;
}
stream.prepare(&meshes, meshCount);
for (std::uint32_t i = 0; i < meshCount; ++i)
{
if (!stream.apply(&meshes[i].indexCount, 1) || !stream.apply(&meshes[i].vertexCount, 1))
return false;
stream.prepare(&meshes[i].index, meshes[i].indexCount);
if (!stream.apply(meshes[i].index.get(), meshes[i].indexCount))
return false;
stream.prepare(&meshes[i].vertex, meshes[i].vertexCount);
for (std::uint32_t j = 0; j < meshes[i].vertexCount; ++j)
{
if (!stream.apply(meshes[i].vertex[j].position, sizeof(Vertice::position) / sizeof(Vertice::position[0])) ||
!stream.apply(meshes[i].vertex[j].normal, sizeof(Vertice::normal) / sizeof(Vertice::normal[0])) ||
!stream.apply(meshes[i].vertex[j].texture, sizeof(Vertice::texture) / sizeof(Vertice::texture[0])))
{
return false;
}
}
}
return true;
}
template<class T>
bool StandardBinaryReader::apply(T * t, size_t s)
{
static_assert(std::is_pod<T>::value, "fread requires POD type");
return std::fread(t, sizeof(T) * s, 1, file) != 1;
}
template<class T>
void StandardBinaryReader::prepare(std::unique_ptr<T[]> * t, size_t s)
{
*t = std::unique_ptr<T[]>(new T[s]);
}
template<class T>
bool StandardBinaryWriter::apply(T * t, size_t s)
{
static_assert(std::is_pod<T>::value, "fwrite requires POD type");
const auto b = std::fwrite(t, sizeof(T) * s, 1, file);
if (b != 1)
return false;
return true;
}
template<class T>
void StandardBinaryWriter::prepare(std::unique_ptr<T[]> * t, size_t s)
{
}
template<class T>
bool StandardStreamWriter::apply(T * t, size_t s)
{
static_assert(std::is_pod<T>::value, "fwrite requires POD type");
size_t i = 0;
while (true)
{
if (i >= s)
break;
stream << t[i++];
if (i >= s)
break;
if (i < s)
stream << ", ";
}
stream << std::endl;
return true;
}
template<class T>
void StandardStreamWriter::prepare(std::unique_ptr<T[]> *, size_t)
{
}
//
//
//
StandardBinaryReader::StandardBinaryReader(FILE * file)
: file(file)
{
}
StandardBinaryWriter::StandardBinaryWriter(FILE * file)
: file(file)
{
}
StandardStreamWriter::StandardStreamWriter(std::ostream & stream)
: stream(stream)
{
}
int converter_main(int argc, const char * const * argv)
{
Assimp::Importer importer;
for (int i = 1; i < argc; ++i)
{
const std::string infile = argv[i];
const std::string outfile = infile.substr(0, infile.rfind('.')) + ".mff";
const std::string dbgfile = infile.substr(0, infile.rfind('.')) + ".txt";
std::cout << infile << std::endl;
auto scene = importer.ReadFile(infile, aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_FlipUVs);
if (!scene)
{
std::cerr << "cannot open file" << std::endl;
continue;
}
Model model;
model.materialCount = scene->mNumMaterials;
model.materials = std::unique_ptr<Model::Material[]>(new Model::Material[model.materialCount]);
for (std::uint32_t j = 0; j < scene->mNumMaterials; ++j)
{
const auto inMaterial = scene->mMaterials[j];
auto & outMaterial = model.materials[j];
if (inMaterial->GetTextureCount(aiTextureType_DIFFUSE) > 0)
{
aiString path;
inMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &path);
outMaterial.textureName = std::unique_ptr<char[]>(new char[path.length + 1]);
std::memcpy(outMaterial.textureName.get(), path.data, path.length);
outMaterial.textureName[path.length] = '\0';
}
else
{
std::cerr << "no diffuse texture found" << std::endl;
}
}
model.meshCount = scene->mNumMeshes;
model.meshes = std::unique_ptr<Model::Mesh[]>(new Model::Mesh[model.meshCount]);
for (std::uint32_t j = 0; j < scene->mNumMeshes; ++j)
{
const auto inMesh = scene->mMeshes[j];
auto & outMesh = model.meshes[j];
outMesh.materialIndex = inMesh->mMaterialIndex;
outMesh.vertexCount = inMesh->mNumVertices;
outMesh.vertex = std::unique_ptr<Model::Vertice[]>(new Model::Vertice[outMesh.vertexCount]);
for (std::uint32_t k = 0; k < inMesh->mNumVertices; ++k)
{
const auto & inPositions = inMesh->mVertices[k];
auto & outPositions = outMesh.vertex[k].position;
outPositions[0] = inPositions.x;
outPositions[1] = inPositions.y;
outPositions[2] = inPositions.z;
const auto & inNormals = inMesh->mNormals[k];
auto & outNormals = outMesh.vertex[k].normal;
outNormals[0] = inNormals.x;
outNormals[1] = inNormals.y;
outNormals[2] = inNormals.z;
const auto & inTextures = inMesh->mTextureCoords[0][k];
auto & outTextures = outMesh.vertex[k].texture;
outTextures[0] = inTextures.x;
outTextures[1] = inTextures.y;
}
const std::uint32_t faceCount = inMesh->mNumFaces;
std::uint32_t indexCount = 0;
for (std::uint32_t k = 0; k < faceCount; ++k)
indexCount += inMesh->mFaces[k].mNumIndices;
outMesh.indexCount = indexCount;
outMesh.index = std::unique_ptr<Model::Indice[]>(new Model::Indice[indexCount]);
std::uint32_t * outIndex = outMesh.index.get();
for (std::uint32_t k = 0; k < faceCount; ++k)
for (std::uint32_t l = 0; l < inMesh->mFaces[k].mNumIndices; ++l)
*outIndex++ = inMesh->mFaces[k].mIndices[l];
}
auto file = std::fopen(outfile.c_str(), "wb");
if (file)
{
StandardBinaryWriter writer(file);
const bool success = model.serialization(writer);
std::fclose(file);
if (!success)
{
std::cerr << "error writing file: " << outfile << std::endl;
//continue;
}
}
std::ofstream debugFile(dbgfile.c_str(), std::ios_base::binary | std::ios_base::trunc);
if (debugFile.is_open())
{
StandardStreamWriter writer(debugFile);
if (!model.serialization(writer))
{
std::cerr << "error writing debug file" << dbgfile << std::endl;
}
}
}
return 0;
}
} // namespace rw
int main(int argc, char ** argv)
{
return rw::converter_main(argc, argv);
}
#endif // __MODEL_FORMAT_HH__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment