aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-02-29 03:05:36 +0100
committerdec05eba <dec05eba@protonmail.com>2021-11-18 15:22:10 +0100
commit12c36c61c3f8d19c44cb2e5ffdf3ed812a0390d2 (patch)
tree5c45ed12702335ba5a30706ba413daab95995af9 /src
parentc3453fedbd270afe8d9dfc7f288ea7205f029b86 (diff)
Implement .a3d model loader (amalgine specific format)
Diffstat (limited to 'src')
-rw-r--r--src/ModelLoader.cpp230
-rw-r--r--src/main.cpp9
-rw-r--r--src/model_loader/ObjModelLoader.cpp5
3 files changed, 239 insertions, 5 deletions
diff --git a/src/ModelLoader.cpp b/src/ModelLoader.cpp
new file mode 100644
index 0000000..60626f1
--- /dev/null
+++ b/src/ModelLoader.cpp
@@ -0,0 +1,230 @@
+#include "../include/ModelLoader.hpp"
+#include "../include/File.hpp"
+#include "../include/Image.hpp"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <libgen.h>
+#include <string>
+
+namespace amalgine {
+ class ModelLoaderInternal {
+ public:
+ ModelLoaderInternal(const char *data, size_t size) : data(data), size(size), offset(0) {}
+
+ template <typename T>
+ bool extract(T *result) {
+ if(size - offset >= sizeof(T)) {
+ // This is fine, because all data in .a3d files are aligned correctly.
+ // However this could be incorrect if the file is not an .a3d file, in which case this would crash on ARM processors for example..
+ // TODO: Use memcpy on ARM (and other platforms that dont support unaligned memory access)
+ *result = *(T*)(data + offset);
+ offset += sizeof(T);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ template <typename T>
+ bool extract_list(T *result, size_t num_items) {
+ const size_t bytes_to_extract = sizeof(T) * num_items;
+ if(size - offset >= bytes_to_extract) {
+ memcpy(result, data + offset, bytes_to_extract);
+ offset += bytes_to_extract;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ std::string extract_null_terminated_string() {
+ std::string result;
+ const char *p = data + offset;
+ while(true) {
+ char c = *p++;
+ if(c == '\0')
+ break;
+ else
+ result += c;
+ }
+ offset += p - (data + offset);
+ return result;
+ }
+
+ bool skip(size_t num_bytes) {
+ if(size - offset >= num_bytes) {
+ offset += num_bytes;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ bool empty() const { return offset == size; }
+ private:
+ const char *data;
+ const size_t size;
+ size_t offset;
+ };
+
+ struct Face {
+ unsigned int index[3];
+ };
+
+ // TODO: Check bounds of indices and size of data before allocating
+ static bool model_loader_version_1(ModelLoaderInternal &model_loader, std::vector<Triangle3D> &triangles, std::vector<vec2f> &texture_coords, Image **image, const std::string &dir_path) {
+ // TODO: Remove these temporary variables when amalgine has been modified to use indices instead of
+ // data directly in shaders
+ std::vector<Triangle3D> temp_vertices;
+ std::vector<vec2f> temp_texcoords;
+ std::vector<Face> indices;
+ while(!model_loader.empty()) {
+ // TODO: Verify num_vertices isn't too large?
+ unsigned int num_vertices;
+ if(!model_loader.extract<unsigned int>(&num_vertices) || num_vertices == 0) {
+ fprintf(stderr, "Error: failed to extract num_vertices\n");
+ return false;
+ }
+
+ unsigned int num_triangles = num_vertices / 3;
+ temp_vertices.resize(num_triangles);
+ if(!model_loader.extract_list(&temp_vertices[0], num_triangles)) {
+ fprintf(stderr, "Error: failed to extract vertices\n");
+ return false;
+ }
+
+ if(!model_loader.skip(num_vertices * sizeof(vec3f))) {
+ fprintf(stderr, "Error: failed to extract normals\n");
+ return false;
+ }
+
+ unsigned int has_texcoords;
+ if(!model_loader.extract<unsigned int>(&has_texcoords) || has_texcoords > 1) {
+ fprintf(stderr, "Error: failed to extract has_texcoords\n");
+ return false;
+ }
+
+ if(has_texcoords) {
+ temp_texcoords.resize(num_vertices);
+ if(!model_loader.extract_list(&temp_texcoords[0], num_vertices)) {
+ fprintf(stderr, "Error: failed to extract texture coordinates\n");
+ return false;
+ }
+ }
+
+ unsigned int num_faces;
+ if(!model_loader.extract<unsigned int>(&num_faces)) {
+ fprintf(stderr, "Error: failed to extract num_faces\n");
+ return false;
+ }
+
+ indices.resize(num_faces);
+ if(!model_loader.extract_list(&indices[0], num_faces)) {
+ fprintf(stderr, "Error: failed to extract face indices\n");
+ return false;
+ }
+
+ unsigned int triangles_offset = triangles.size();
+ triangles.resize(triangles.size() + indices.size());
+ const Vertex3D *temp_vertices_begin = &temp_vertices[0].p1;
+ unsigned int max_vertex_index = (unsigned int)(temp_vertices.size() - 1) * 3;
+ for(unsigned int i = 0; i < indices.size(); ++i) {
+ triangles[triangles_offset + i].p1 = temp_vertices_begin[std::min(indices[i].index[0], max_vertex_index)];
+ triangles[triangles_offset + i].p2 = temp_vertices_begin[std::min(indices[i].index[1], max_vertex_index)];
+ triangles[triangles_offset + i].p3 = temp_vertices_begin[std::min(indices[i].index[2], max_vertex_index)];
+ }
+
+ unsigned int texcoords_offset = texture_coords.size();
+ texture_coords.resize(texture_coords.size() + indices.size() * 3);
+ if(has_texcoords) {
+ unsigned int max_texcoord_index = (unsigned int)temp_texcoords.size() - 1;
+ for(unsigned int i = 0; i < indices.size(); ++i) {
+ texture_coords[texcoords_offset + 0] = temp_texcoords[std::min(indices[i].index[0], max_texcoord_index)];
+ texture_coords[texcoords_offset + 1] = temp_texcoords[std::min(indices[i].index[1], max_texcoord_index)];
+ texture_coords[texcoords_offset + 2] = temp_texcoords[std::min(indices[i].index[2], max_texcoord_index)];
+ texcoords_offset += 3;
+ }
+ } else {
+ for(unsigned int i = 0; i < indices.size(); ++i) {
+ // TODO: is this needed even with the resize above? or use memset?
+ texture_coords[texcoords_offset + i + 0] = { 0.0f, 0.0f };
+ texture_coords[texcoords_offset + i + 1] = { 0.0f, 0.0f };
+ texture_coords[texcoords_offset + i + 2] = { 0.0f, 0.0f };
+ }
+ }
+
+ unsigned int has_materials;
+ if(!model_loader.extract<unsigned int>(&has_materials) || has_materials > 1) {
+ fprintf(stderr, "Error: failed to extract has_materials\n");
+ return false;
+ }
+
+ if(has_materials) {
+ for(unsigned int i = 0; i < 3; ++i) {
+ unsigned int num_textures;
+ if(!model_loader.extract<unsigned int>(&num_textures)) {
+ fprintf(stderr, "Error: failed to extract num_textures\n");
+ return false;
+ }
+
+ for(unsigned int j = 0; j < num_textures; ++j) {
+ std::string texture_name = model_loader.extract_null_terminated_string();
+ fprintf(stderr, "texture name: %s\n", texture_name.c_str());
+ std::string texture_path = dir_path + "/" + texture_name;
+ assert(!*image); // TODO: Support multiple images
+ Result<Image*> image_result = Image::loadFromFile(texture_path.c_str());
+ if(!image_result) {
+ fprintf(stderr, "%s\n", image_result.getErrorMsg().c_str());
+ return false;
+ }
+ *image = image_result.unwrap();
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ // TODO: Instead of reading all file data to a buffer and then copying it, maybe read from file
+ // directly into data structures?
+ bool ModelLoader::load_from_file(const char *filepath, std::vector<Triangle3D> &triangles, std::vector<vec2f> &texture_coords, Image **image) {
+ triangles.clear();
+ texture_coords.clear();
+ *image = nullptr;
+ bool result = true;
+
+ size_t file_size;
+ char *file_data = file_get_content(filepath, &file_size);
+ if(!file_data)
+ return false;
+
+ std::string dir_path = filepath;
+ dirname(&dir_path[0]);
+ const int dir_path_size = strlen(dir_path.c_str());
+ dir_path.resize(dir_path_size);
+
+ ModelLoaderInternal model_loader(file_data, file_size);
+
+ unsigned int magic_number;
+ unsigned int version = 0;
+
+ if(!model_loader.extract<unsigned int>(&magic_number) || magic_number != 0x036144AF) {
+ fprintf(stderr, "Error: file %s is not an .a3d file (magic number not found)\n", filepath);
+ result = false;
+ goto cleanup;
+ }
+
+ if(model_loader.extract<unsigned int>(&version) && version == 1) {
+ result = model_loader_version_1(model_loader, triangles, texture_coords, image, dir_path);
+ } else {
+ fprintf(stderr, "Error: version not supported: %u\n", version);
+ result = false;
+ }
+
+ cleanup:
+ free(file_data);
+ return result;
+ }
+} \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index f2b2c3f..7cefb17 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -8,6 +8,7 @@
#include "../include/ThirdPersonCamera.hpp"
#include "../include/model_loader/ObjModelLoader.hpp"
+#include "../include/ModelLoader.hpp"
#include <cstdio>
#include <chrono>
@@ -96,7 +97,7 @@ int main() {
std::unique_ptr<Shader> vertex_shader = load_shader_from_file("shaders/vertex.vert", Shader::Type::VERTEX);
std::unique_ptr<Shader> pixel_shader = load_shader_from_file("shaders/fragment.frag", Shader::Type::PIXEL);
std::unique_ptr<ShaderProgram> shader_program = build_shader_program_from_shaders({ vertex_shader.get(), pixel_shader.get() });
- Model buddha_model = load_model("/home/dec05eba/Downloads/ELI4OS.obj", shader_program.get());
+ Model buddha_model = load_model("/home/dec05eba/Downloads/ELI4OS.a3d", shader_program.get());
buddha_model.frame.get_uniform_by_name("proj")->set(proj);
Result<Uniform> view_uniform = buddha_model.frame.get_uniform_by_name("view");
Result<Uniform> model_uniform = buddha_model.frame.get_uniform_by_name("model");
@@ -104,7 +105,7 @@ int main() {
std::unique_ptr<Shader> vertex_no_texture_shader = load_shader_from_file("shaders/vertex_no_texture.vert", Shader::Type::VERTEX);
std::unique_ptr<Shader> pixel_no_texture_shader = load_shader_from_file("shaders/fragment_no_texture.frag", Shader::Type::PIXEL);
std::unique_ptr<ShaderProgram> no_texture_shader_program = build_shader_program_from_shaders({ vertex_no_texture_shader.get(), pixel_no_texture_shader.get() });
- Model character_model = load_model("/home/dec05eba/Downloads/FinalBaseMesh.obj", no_texture_shader_program.get());
+ Model character_model = load_model("/home/dec05eba/Downloads/FinalBaseMesh.a3d", no_texture_shader_program.get());
character_model.frame.get_uniform_by_name("proj")->set(proj);
Result<Uniform> char_view_uniform = character_model.frame.get_uniform_by_name("view");
Result<Uniform> char_model_uniform = character_model.frame.get_uniform_by_name("model");
@@ -260,7 +261,9 @@ Model load_model(const char *filepath, ShaderProgram *shader_program) {
std::vector<Triangle3D> triangles;
std::vector<vec2f> texture_coords;
Image *image;
- ObjModelLoader::load_from_file(filepath, triangles, texture_coords, &image);
+ if(!ModelLoader::load_from_file(filepath, triangles, texture_coords, &image)) {
+ fprintf(stderr, "Error: failed to load model from file: %s\n", filepath);
+ }
// TODO: This needs to be done to prevent crash in glDrawArrays, but this should not be used when shader doesn't handle any texture
//if(texture_coords.size() < triangles.size())
// texture_coords.resize(triangles.size());
diff --git a/src/model_loader/ObjModelLoader.cpp b/src/model_loader/ObjModelLoader.cpp
index 7976bc9..7c4ee5c 100644
--- a/src/model_loader/ObjModelLoader.cpp
+++ b/src/model_loader/ObjModelLoader.cpp
@@ -101,7 +101,7 @@ namespace amalgine {
static Result<Material> load_material_from_file(const char *filepath);
- void ObjModelLoader::load_from_file(const char *filepath, std::vector<Triangle3D> &triangles, std::vector<vec2f> &texture_coords, Image **image) {
+ bool ObjModelLoader::load_from_file(const char *filepath, std::vector<Triangle3D> &triangles, std::vector<vec2f> &texture_coords, Image **image) {
triangles.clear();
texture_coords.clear();
*image = nullptr;
@@ -109,7 +109,7 @@ namespace amalgine {
size_t file_size;
char *file_data = file_get_content(filepath, &file_size);
if(!file_data)
- return;
+ return false;
std::string dir_path = filepath;
dirname(&dir_path[0]);
@@ -238,6 +238,7 @@ namespace amalgine {
cleanup:
free(file_data);
+ return true;
}
Result<Material> load_material_from_file(const char *filepath) {