diff options
Diffstat (limited to 'src/model_loader')
-rw-r--r-- | src/model_loader/ObjModelLoader.cpp | 176 |
1 files changed, 147 insertions, 29 deletions
diff --git a/src/model_loader/ObjModelLoader.cpp b/src/model_loader/ObjModelLoader.cpp index 1bae458..51d8a74 100644 --- a/src/model_loader/ObjModelLoader.cpp +++ b/src/model_loader/ObjModelLoader.cpp @@ -1,25 +1,20 @@ #include "../../include/model_loader/ObjModelLoader.hpp" #include "../../include/File.hpp" #include "../../include/DataView.hpp" +#include "../../include/Image.hpp" #include <stdlib.h> #include <string.h> #include <stdio.h> #include <assert.h> +#include <libgen.h> +#include <string> namespace amalgine { static StringView get_line(char *str, size_t size) { - #if 0 char *found_ptr = (char*)memchr(str, '\n', size); if(!found_ptr) found_ptr = str + size; return { str, static_cast<size_t>(found_ptr - str) }; - #endif - for(size_t i = 0; i < size; ++i) { - char c = str[i]; - if(c == '\n') - return { str, i }; - } - return { str, size }; } static bool is_whitespace(char c) { @@ -44,13 +39,13 @@ namespace amalgine { } // Return the number of columns, limited by @output_size - static int split_columns(const StringView &str, StringView *output, int output_size) { + static int split_columns(const StringView &str, char split_char, StringView *output, int output_size) { char *data = str.data; ssize_t size = str.size; int column = 0; while(size > 0) { - char *found_ptr = (char*)memchr(data, ' ', size); + char *found_ptr = (char*)memchr(data, split_char, size); if(!found_ptr) found_ptr = data + size; @@ -75,17 +70,32 @@ namespace amalgine { return static_cast<size_t>(found_ptr - str.data); } - void ObjModelLoader::load_from_file(const char *filepath, std::vector<Triangle3D> &triangles) { + struct Material { + Image *image; + }; + + 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) { + triangles.clear(); + texture_coords.clear(); + *image = nullptr; + size_t file_size; char *file_data = file_get_content(filepath, &file_size); - triangles.clear(); if(!file_data) return; - const int max_columns = 12; + 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); + + const int max_columns = 8; StringView columns[max_columns]; std::vector<Vertex3D> vertices; + std::vector<vec2f> temp_texture_coords; char *data = file_data; ssize_t size = file_size; @@ -94,9 +104,9 @@ namespace amalgine { if(line_data.size > 2 && memcmp(line_data.data, "v ", 2) == 0) { //printf("line: |%.*s|\n", line_data.size, line_data.data); - StringView column_data = { line_data.data + 1, line_data.size - 1 }; + StringView column_data = { line_data.data + 2, line_data.size - 2 }; - int num_columns = split_columns(column_data, columns, max_columns); + int num_columns = split_columns(column_data, ' ', columns, max_columns); if(num_columns >= 3) { columns[0].data[columns[0].size] = '\0'; columns[1].data[columns[1].size] = '\0'; @@ -105,33 +115,140 @@ namespace amalgine { } } else if(line_data.size > 2 && memcmp(line_data.data, "f ", 2) == 0) { //printf("line: |%.*s|\n", line_data.size, line_data.data); - StringView column_data = { line_data.data + 1, line_data.size - 1 }; + StringView column_data = { line_data.data + 2, line_data.size - 2 }; - int num_columns = split_columns(column_data, columns, max_columns); + int num_columns = split_columns(column_data, ' ', columns, max_columns); if(num_columns == 3 || num_columns == 4) { bool valid = true; - int indices[4]; + int vertex_indices[4]; + int texture_coord_indices[4]; + for(int i = 0; i < num_columns; ++i) { - columns[i].size = find_or_end(columns[i], '/'); - columns[i].data[columns[i].size] = '\0'; - indices[i] = atoi(columns[i].data); - //printf("column[%d] = %.*s = %d\n", i, columns[i].size, columns[i].data, vertices.size()); - if(indices[i] < 1 || indices[i] > vertices.size()) { + const int max_attributes = 3; + StringView attributes[max_attributes]; + int num_attributes = split_columns(columns[i], '/', attributes, max_attributes); + assert(num_attributes == 3); + + attributes[0].data[attributes[0].size] = '\0'; + attributes[1].data[attributes[1].size] = '\0'; + attributes[2].data[attributes[2].size] = '\0'; + + vertex_indices[i] = atoi(attributes[0].data); + texture_coord_indices[i] = atoi(attributes[1].data); + + if(vertex_indices[i] < 1 || vertex_indices[i] > vertices.size()) { valid = false; + abort(); break; } - --indices[i]; + + if(texture_coord_indices[i] < 1 || texture_coord_indices[i] > temp_texture_coords.size()) { + valid = false; + abort(); + break; + } + + --vertex_indices[i]; + --texture_coord_indices[i]; } if(valid) { - if(num_columns == 3) { - triangles.push_back({ vertices[indices[0]], vertices[indices[1]], vertices[indices[2]] }); - } else if(num_columns == 4) { - triangles.push_back({ vertices[indices[0]], vertices[indices[1]], vertices[indices[3]] }); - triangles.push_back({ vertices[indices[3]], vertices[indices[2]], vertices[indices[1]] }); + if(num_columns == 3) { // Triangle + triangles.push_back({ vertices[vertex_indices[0]], vertices[vertex_indices[1]], vertices[vertex_indices[2]] }); + + texture_coords.push_back(temp_texture_coords[texture_coord_indices[0]]); + texture_coords.push_back(temp_texture_coords[texture_coord_indices[1]]); + texture_coords.push_back(temp_texture_coords[texture_coord_indices[2]]); + } else if(num_columns == 4) { // Quad, convert to triangle. TODO: Support rendering quads instead of converting to triangles + triangles.push_back({ vertices[vertex_indices[0]], vertices[vertex_indices[1]], vertices[vertex_indices[3]] }); + triangles.push_back({ vertices[vertex_indices[3]], vertices[vertex_indices[2]], vertices[vertex_indices[1]] }); + + texture_coords.push_back(temp_texture_coords[texture_coord_indices[0]]); + texture_coords.push_back(temp_texture_coords[texture_coord_indices[1]]); + texture_coords.push_back(temp_texture_coords[texture_coord_indices[3]]); + + texture_coords.push_back(temp_texture_coords[texture_coord_indices[3]]); + texture_coords.push_back(temp_texture_coords[texture_coord_indices[2]]); + texture_coords.push_back(temp_texture_coords[texture_coord_indices[1]]); } } } + } else if(line_data.size > 3 && memcmp(line_data.data, "vt ", 3) == 0) { + StringView column_data = { line_data.data + 3, line_data.size - 3 }; + int num_columns = split_columns(column_data, ' ', columns, max_columns); + if(num_columns == 2) { + columns[0].data[columns[0].size] = '\0'; + columns[1].data[columns[1].size] = '\0'; + temp_texture_coords.push_back({ atof(columns[0].data), atof(columns[1].data) }); + } + } else if(line_data.size > 7 && memcmp(line_data.data, "mtllib ", 7) == 0) { + StringView column_data = { line_data.data + 7, line_data.size - 7 }; + int num_columns = split_columns(column_data, ' ', columns, max_columns); + if(num_columns == 1) { + std::string material_path; + material_path.resize(dir_path_size + 1 + columns[0].size); + memcpy(&material_path[0], dir_path.data(), dir_path_size); + material_path[dir_path_size] = '/'; + memcpy(&material_path[dir_path_size + 1], columns[0].data, columns[0].size); + printf("mtl file: %.*s\n", material_path.size(), material_path.data()); + + assert(!*image); + Result<Material> material = load_material_from_file(material_path.c_str()); + if(!material) { + fprintf(stderr, "Error: %s\n", material.getErrorMsg().c_str()); + abort(); + goto cleanup; + } + + *image = material->image; + } + } + + data += (line_data.size + 1); + size -= (line_data.size + 1); + } + + printf("num tex coords: %d\n", texture_coords.size()); + + cleanup: + free(file_data); + } + + Result<Material> load_material_from_file(const char *filepath) { + size_t file_size; + char *file_data = file_get_content(filepath, &file_size); + if(!file_data) + return Result<Material>::Err("Failed to load material: " + std::string(filepath)); + + 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); + + Image *image = nullptr; + + char *data = file_data; + ssize_t size = file_size; + while(size > 0) { + StringView line_data = get_line(data, size); + + if(line_data.size > 7 && memcmp(line_data.data, "map_Kd ", 7) == 0) { + StringView texture_file = { line_data.data + 7, line_data.size - 7 }; + line_data.data[line_data.size] = '\0'; + + std::string texture_path; + texture_path.resize(dir_path_size + 1 + texture_file.size); + memcpy(&texture_path[0], dir_path.data(), dir_path_size); + texture_path[dir_path_size] = '/'; + memcpy(&texture_path[dir_path_size + 1], texture_file.data, texture_file.size); + printf("texture file: |%.*s|\n", texture_path.size(), texture_path.data()); + + Result<Image*> image_result = Image::loadFromFile(texture_path.c_str()); + if(!image_result) + return Result<Material>::Err("Failed to load texture: " + texture_path); + + image = image_result.unwrap(); + break; } data += (line_data.size + 1); @@ -139,5 +256,6 @@ namespace amalgine { } free(file_data); + return Result<Material>::Ok({ image }); } }
\ No newline at end of file |