aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
blob: 87260cbc3bb5d19bfb09cd8d0ec0df1522579a1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <assert.h>
#include <stdio.h>

#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/LogStream.hpp>

static void usage();
static void file_write_error_check(FILE *file, const void *data, size_t size);
static void file_write_uint_error_check(FILE *file, unsigned int value);
static void process_node(const aiScene *scene, const aiNode *node, FILE *output_file);

int main(int argc, char **argv) {
    if(argc != 3)
        usage();

    const char *source_filepath = argv[1];
    const char *destination_filepath = argv[2];

    Assimp::Logger::LogSeverity severity = Assimp::Logger::VERBOSE;
	Assimp::DefaultLogger::create("", severity, aiDefaultLogStream_STDERR);

    Assimp::Importer importer;
    const aiScene *scene = importer.ReadFile(source_filepath, aiProcessPreset_TargetRealtime_Quality | aiProcess_Triangulate | aiProcess_FlipUVs);
    if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
        fprintf(stderr, "Failed to load file: %s, reason: %s\n", source_filepath, importer.GetErrorString());
        exit(2);
    }

    FILE *destination_file = fopen(destination_filepath, "wb");
    if(!destination_file) {
        perror(destination_filepath);
        exit(3);
    }
    file_write_uint_error_check(destination_file, 0x036144AF);  // magic number
    file_write_uint_error_check(destination_file, 0x00000001);  // version

    process_node(scene, scene->mRootNode, destination_file);
    fclose(destination_file);
    Assimp::DefaultLogger::kill();
    return 0;
}

void usage() {
    fprintf(stderr, "usage: amalgine-model-converter <source> <destination>\n");
    exit(1);
}

void file_write_error_check(FILE *file, const void *data, size_t size) {
    if(fwrite(data, 1, size, file) != size) {
        fprintf(stderr, "Error: failed to write %ld bytes to output file, error: %s\n", size, strerror(errno));
        exit(4);
    }
}

void file_write_uint_error_check(FILE *file, unsigned int value) {
    const size_t size = sizeof(value);
    if(fwrite(&value, 1, size, file) != size) {
        fprintf(stderr, "Error: failed to write %ld bytes to output file, error: %s\n", size, strerror(errno));
        exit(4);
    }
}

static void process_mesh(const aiScene *scene, const aiMesh *mesh, FILE *output_file) {
    // TODO: It seems like sometimes the vertice count doesn't match triangles.. excluding the last vertices in the list
    unsigned int vertice_overflow = mesh->mNumVertices % 3;
    unsigned int num_vertices = mesh->mNumVertices - vertice_overflow;
    assert(num_vertices % 3 == 0);
    file_write_uint_error_check(output_file, num_vertices);
    file_write_error_check(output_file, mesh->mVertices, num_vertices * sizeof(aiVector3D));
    file_write_error_check(output_file, mesh->mNormals, num_vertices * sizeof(aiVector3D));

    file_write_uint_error_check(output_file, mesh->mTextureCoords[0] ? 1 : 0);
    if(mesh->mTextureCoords[0]) {
        for(unsigned int i = 0; i < num_vertices; ++i) {
            file_write_error_check(output_file, &mesh->mTextureCoords[0][i].x, sizeof(float));
            file_write_error_check(output_file, &mesh->mTextureCoords[0][i].y, sizeof(float));
        }
    }

    file_write_uint_error_check(output_file, mesh->mNumFaces);
    for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
        // Should always be 3 since the file was loaded with aiProcess_Triangulate
        assert(mesh->mFaces[i].mNumIndices == 3);
        file_write_error_check(output_file, mesh->mFaces[i].mIndices, 3 * sizeof(unsigned int));
    }

    file_write_uint_error_check(output_file, mesh->mMaterialIndex < scene->mNumMaterials);
    if(mesh->mMaterialIndex < scene->mNumMaterials) {
        const unsigned int num_texture_types = 3;
        const aiTextureType texture_types[num_texture_types] = {
            aiTextureType_DIFFUSE,
            aiTextureType_SPECULAR,
            aiTextureType_AMBIENT
        };

        const aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
        for(unsigned int i = 0; i < num_texture_types; ++i) {
            unsigned int num_textures_for_type = material->GetTextureCount(texture_types[i]);
            file_write_uint_error_check(output_file, num_textures_for_type);
            for(unsigned int j = 0; j < num_textures_for_type; ++j) {
                aiString str;
                if(material->GetTexture(texture_types[i], j, &str) == aiReturn_SUCCESS)
                    file_write_error_check(output_file, str.data, str.length + 1); // +1 to include null-terminate character
                else
                    file_write_error_check(output_file, "\0", 1);
            }
        }
    }
}

void process_node(const aiScene *scene, const aiNode *node, FILE *output_file) {
    for(unsigned int i = 0; i < node->mNumMeshes; ++i) {
        const aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
        process_mesh(scene, mesh, output_file);
    }

    for(unsigned int i = 0; i < node->mNumChildren; ++i) {
        process_node(scene, node->mChildren[i], output_file);
    }
}