Last active
February 14, 2019 22:01
-
-
Save dolinenkov/5d0d2d68366987d26fc40bda57b54ae7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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