diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | LICENSE | 13 | ||||
-rw-r--r-- | README.md | 33 | ||||
-rw-r--r-- | project.conf | 8 | ||||
-rw-r--r-- | src/main.cpp | 114 |
5 files changed, 173 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..636c6b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Compiled sibs files +sibs-build/ +compile_commands.json +tests/sibs-build/ +tests/compile_commands.json @@ -0,0 +1,13 @@ +Copyright 2020 dec05eba + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a53393e --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# Amalgine 3D model file format (.a3d) +All types are stored in little endian format. Any type with a * is optional. +|Type |Description | +|-------|-------------------------------------------------------------------------------------| +|uint |The magic number, the value will always be 0x036144AF. | +|uint |Version of the file format. This is backwards compatible but not forwards compatible.| +|mesh[] |The mesh data. | + +mesh: +|Type |Description | +|-----------|-------------------------------------------------------------------------------------| +|uint |The number of vertices, normals and optionally texture coordinates. | +|vec3f[] |A list of vertices. | +|vec3f[] |A list of normals. | +|uint |The number of faces. Each face has 3 indices of type uint. | +|uint[] |The face indices. | +|uint |1 if the model has texture coordinates, otherwise 0. | +|vec3f[]* |A list of texture coordinates. | +|uint |1 if the model has materials, otherwise 0. | +|material[3]|The material data. This is a list of [diffuse, specular, ambient] materials. | + +vec3f: +|Type |Description | +|-----|----------------| +|float|The x coordiate.| +|float|The y coordiate.| +|float|The z coordiate.| + +material: +|Type |Description | +|------|-------------------------------------------------------------------------| +|uint |The number of textures. | +|char[]|The filepath to the texture (relative). This is a null-terminated string.| diff --git a/project.conf b/project.conf new file mode 100644 index 0000000..7f989af --- /dev/null +++ b/project.conf @@ -0,0 +1,8 @@ +[package] +name = "amalgine-model-converter" +type = "executable" +version = "0.1.0" +platforms = ["any"] + +[dependencies] +assimp = "5" diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..a679828 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,114 @@ +#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) { + file_write_uint_error_check(output_file, mesh->mNumVertices); + file_write_error_check(output_file, &mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D)); + file_write_error_check(output_file, &mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D)); + + 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 + file_write_error_check(output_file, mesh->mFaces[i].mIndices, 3 * sizeof(unsigned int)); + } + + file_write_uint_error_check(output_file, mesh->mTextureCoords[0] ? 1 : 0); + if(mesh->mTextureCoords[0]) + file_write_error_check(output_file, &mesh->mTextureCoords[0], mesh->mNumVertices * sizeof(aiVector3D)); + + 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); + } +}
\ No newline at end of file |