/** * Jingga * * @copyright Jingga * @license OMS License 2.0 * @version 1.0.0 * @link https://jingga.app */ #ifndef TOS_OBJECT_H #define TOS_OBJECT_H #include "Vertex.h" #include "../stdlib/Types.h" #include "../memory/RingMemory.h" #include "../utils/EndianUtils.h" #include "../compression/LZP.h" #if _WIN32 #include "../platform/win32/UtilsWin32.h" #else #include "../platform/linux/UtilsLinux.h" #endif #include "Mesh.h" #include "../stdlib/simd/SIMD_I32.h" // @todo also handle textures etc. void object_from_file_txt( RingMemory* ring, const char* path, Mesh* mesh ) { FileBody file; file_read(path, &file, ring); char* pos = (char *) file.content; int object_index = 0; int group_index = 0; mesh->vertices = (float *) mesh->data; mesh->vertex_count = 0; mesh->normal_count = 0; mesh->tex_coord_count = 0; mesh->color_count = 0; mesh->face_count = 0; mesh->face_normal_count = 0; mesh->face_tex_coord_count = 0; mesh->face_color_count = 0; uint32 temp_color_count = 0; while (*pos != '\0') { while (*pos == ' ') { ++pos; } if (*pos == '\n') { ++pos; continue; } // Parse type // WARNING: The code below could fail if [1] is outside of range // However that should never happen for well formed files int state = 0; if (*pos == 'v' && pos[1] == ' ') { state = 1; } else if (*pos == 'v' && pos[1] == 'n') { state = 2; } else if (*pos == 'v' && pos[1] == 't') { state = 3; } else if (*pos == 'v' && pos[1] == 'p') { state = 4; } else if (*pos == 'o' && pos[1] == ' ') { state = 5; } else if (*pos == 's' && pos[1] == ' ') { state = 6; } else if (*pos == 'f' && pos[1] == ' ') { state = 7; } else if (*pos == 'g' && pos[1] == ' ') { state = 8; } else if (*pos == 'l' && pos[1] == ' ') { state = 9; } else if (*pos == 'm' && pos[1] == 't') { state = 10; } else if (*pos == 'h' && pos[1] == 'i') { state = 11; } else if (*pos == 'a' && pos[1] == 'n') { state = 12; } else if (*pos == 'u' && pos[3] == 'm') { state = 13; } else if (*pos == 'u' && pos[3] == 'h') { state = 14; } else { // not supported or comment while (*pos != '\n' && *pos != '\0') { ++pos; } } // move past keyword while (*pos != ' ' && *pos != '\n' && *pos != '\0') { ++pos; } // move past whitespaces and newline bool is_next_line = false; while (*pos == ' ' || *pos == '\n') { is_next_line |= *pos == '\n'; ++pos; } if (*pos == '\0' || is_next_line) { continue; } // NOTE: we always load a file in the format: POSITON + NORMAL + TEXTURE + COLOR // EVEN if some of the data is missing. This is necessary to keep the memory kinda in line. // The actual binary file later will have the minimized layout. // handle data types switch (state) { case 0: break; case 1: { // 'v' if (mesh->vertex_count == 0) { mesh->vertex_type |= VERTEX_TYPE_POSITION; } mesh->vertices[mesh->vertex_count * 3] = strtof(pos, &pos); ++pos; mesh->vertices[mesh->vertex_count * 3 + 1] = strtof(pos, &pos); ++pos; mesh->vertices[mesh->vertex_count * 3 + 2] = strtof(pos, &pos); ++pos; // has color information // @todo Move to own case statement // 'co' if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') { if (mesh->vertex_count == 0) { mesh->vertex_type |= VERTEX_TYPE_COLOR; } mesh->vertices[mesh->vertex_count * 12 + 8] = strtof(pos, &pos); ++pos; mesh->vertices[mesh->vertex_count * 12 + 9] = strtof(pos, &pos); ++pos; mesh->vertices[mesh->vertex_count * 12 + 10] = strtof(pos, &pos); ++pos; // handle optional alpha [a] if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') { mesh->vertices[mesh->vertex_count * 12 + 11] = strtof(pos, &pos); ++pos; } else { mesh->vertices[mesh->vertex_count * 12 + 11] = 1.0f; } ++temp_color_count; } ++mesh->vertex_count; } break; case 2: { // 'vn' // @bug This requires normals to be defined before textures if (mesh->normal_count == 0) { mesh->normals = mesh->vertices + mesh->vertex_count * 3; } mesh->normals[mesh->normal_count * 3] = strtof(pos, &pos); ++pos; mesh->normals[mesh->normal_count * 3 + 1] = strtof(pos, &pos); ++pos; mesh->normals[mesh->normal_count * 3 + 2] = strtof(pos, &pos); ++pos; ++mesh->normal_count; } break; case 3: { // 'vt' if (mesh->tex_coord_count == 0) { mesh->tex_coords = mesh->vertices + mesh->vertex_count * 3 + mesh->normal_count * 3; } mesh->tex_coords[mesh->tex_coord_count * 2] = strtof(pos, &pos); ++pos; mesh->tex_coords[mesh->tex_coord_count * 2 + 1] = strtof(pos, &pos); ++pos; ++mesh->tex_coord_count; } break; case 4: { // 'vp' strtof(pos, &pos); ++pos; // handle optional [v] if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') { strtof(pos, &pos); ++pos; // handle optional [w] if (*pos != '\n' && pos[1] != ' ' && pos[1] != '\n') { strtof(pos, &pos); ++pos; } } } break; case 5: { // 'o' char text[100]; int i = 0; while (*pos != '\0' && *pos != ' ') { text[i++] = *pos++; } text[i] = '\0'; ++object_index; } break; case 6: { // 's' strtol(pos, &pos, 10); ++pos; } break; case 7: { // 'f' if (mesh->face_count == 0) { mesh->faces = (uint32 *) (mesh->vertices + mesh->vertex_count * 3 + mesh->normal_count * 3 + mesh->tex_coord_count * 2 + mesh->color_count * 4); } int ftype = 0; char* tmp = pos; while (*tmp != ' ') { if (*tmp++ == '/') { ++ftype; if (*tmp++ == '/') { ftype = 3; break; } } } const int max_blocks = 3; // @todo this could actually be N. Might have to change in the future int block = 0; while (*pos != '\0' && *pos != '\n') { if (ftype == 0) { // v1 v2 v3 ... if (mesh->face_count == 0) { mesh->face_type = FACE_TYPE_VERTICES; } mesh->faces[(mesh->face_count * max_blocks * 1) + block] = strtol(pos, &pos, 10) - 1; ++pos; } else if (ftype == 1) { // v1/vt1 v2/vt2 v3/vt3 ... if (mesh->face_count == 0) { mesh->face_type = FACE_TYPE_VERTICES | FACE_TYPE_TEXTURES; } mesh->faces[(mesh->face_count * max_blocks * 2) + block * 2] = strtol(pos, &pos, 10) - 1; ++pos; mesh->faces[(mesh->face_count * max_blocks * 2) + block * 2 + 1] = strtol(pos, &pos, 10) - 1; ++pos; } else if (ftype == 2) { // v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ... if (mesh->face_count == 0) { mesh->face_type = FACE_TYPE_VERTICES | FACE_TYPE_TEXTURES | FACE_TYPE_NORMALS; } mesh->faces[(mesh->face_count * max_blocks * 3) + block * 3] = strtol(pos, &pos, 10) - 1; ++pos; mesh->faces[(mesh->face_count * max_blocks * 3) + block * 3 + 1] = strtol(pos, &pos, 10) - 1; ++pos; mesh->faces[(mesh->face_count * max_blocks * 3) + block * 3 + 2] = strtol(pos, &pos, 10) - 1; ++pos; } else if (ftype == 3) { // v1//vn1 v2//vn2 v3//vn3 ... if (mesh->face_count == 0) { mesh->face_type = FACE_TYPE_VERTICES | FACE_TYPE_NORMALS; } mesh->faces[(mesh->face_count * max_blocks * 2) + block * 2] = strtol(pos, &pos, 10) - 1; ++pos; ++pos; mesh->faces[(mesh->face_count * max_blocks * 2) + block * 2 + 1] = strtol(pos, &pos, 10) - 1; ++pos; } ++block; } ++mesh->face_count; } break; case 8: { // 'g' char text[100]; int i = 0; while (*pos != '\0' && *pos != ' ') { text[i++] = *pos++; } text[i] = '\0'; ++group_index; } break; case 9: { //l while (*pos != '\0' && *pos != '\n') { strtol(pos, &pos, 10); ++pos; } } break; case 10: { // 'mtllib' char text[100]; int i = 0; while (*pos != '\0' && *pos != ' ') { text[i++] = *pos++; } text[i] = '\0'; } break; case 11: { // 'hitlib' char text[100]; int i = 0; while (*pos != '\0' && *pos != ' ') { text[i++] = *pos++; } text[i] = '\0'; } break; case 12: { // 'anilib' char text[100]; int i = 0; while (*pos != '\0' && *pos != ' ') { text[i++] = *pos++; } text[i] = '\0'; } break; case 13: { // 'usemtl' char text[100]; int i = 0; while (*pos != '\0' && *pos != ' ') { text[i++] = *pos++; } text[i] = '\0'; } break; case 14: { // 'usehit' char text[100]; int i = 0; while (*pos != '\0' && *pos != ' ') { text[i++] = *pos++; } text[i] = '\0'; } break; } } } enum ObjectLoadingRestriction { OBJECT_LOADING_RESTRICTION_POSITION = 1, OBJECT_LOADING_RESTRICTION_NORMAL = 2, OBJECT_LOADING_RESTRICTION_TEXTURE = 4, OBJECT_LOADING_RESTRICTION_COLOR = 8, OBJECT_LOADING_RESTRICTION_FACES = 16, OBJECT_LOADING_RESTRICTION_EVERYTHING = 31 }; // @todo sometimes we don't care about some data, we should have an option which defines which data should be loaded // this can improve performance for algorithms on this. e.g.: // on the server side we only care about the vertex positions for collision (no normals, no color, ...) int32 object_from_file( RingMemory* ring, const char* path, Mesh* mesh, const char* group = NULL, int load_format = OBJECT_LOADING_RESTRICTION_EVERYTHING, int size = 8 ) { FileBody file; file_read(path, &file, ring); byte* pos = file.content; // Read base data mesh->vertex_type = *((int *) pos); pos += sizeof(mesh->vertex_type); mesh->vertex_count = *((int *) pos); pos += sizeof(mesh->vertex_count); mesh->normal_count = *((int *) pos); pos += sizeof(mesh->normal_count); mesh->tex_coord_count = *((int *) pos); pos += sizeof(mesh->tex_coord_count); mesh->color_count = *((int *) pos); pos += sizeof(mesh->color_count); #if !_WIN32 && !__LITTLE_ENDIAN mesh->vertex_type = endian_swap(mesh->vertex_type); mesh->verted_count = endian_swap(mesh->verted_count); mesh->normal_count = endian_swap(mesh->normal_count); mesh->tex_coord_count = endian_swap(mesh->tex_coord_count); mesh->color_count = endian_swap(mesh->color_count); #endif int32 vertex_size = 0; if (mesh->vertex_type & VERTEX_TYPE_POSITION) { vertex_size += 3; } if (mesh->vertex_type & VERTEX_TYPE_NORMAL) { vertex_size += 3; } if (mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD) { vertex_size += 2; } if (mesh->vertex_type & VERTEX_TYPE_COLOR) { vertex_size += 4; } int offset = 0; if (mesh->vertex_count > 0) { memcpy(mesh->data, pos, sizeof(float) * vertex_size * mesh->vertex_count); mesh->vertices = (float *) mesh->data; pos += sizeof(float) * vertex_size * mesh->vertex_count; offset += sizeof(float) * vertex_size * mesh->vertex_count; } if (mesh->normal_count > 0) { memcpy(mesh->data + offset, pos, sizeof(float) * 3 * mesh->normal_count); mesh->normals = (float *) (mesh->data + offset); pos += sizeof(float) * 3 * mesh->normal_count; offset += sizeof(float) * 3 * mesh->normal_count; } if (mesh->tex_coord_count > 0) { memcpy(mesh->data + offset, pos, sizeof(float) * 3 * mesh->tex_coord_count); mesh->tex_coords = (float *) (mesh->data + offset); pos += sizeof(float) * 2 * mesh->tex_coord_count; offset += sizeof(float) * 2 * mesh->tex_coord_count; } if (mesh->color_count > 0) { memcpy(mesh->data + offset, pos, sizeof(float) * 4 * mesh->color_count); mesh->colors = (float *) (mesh->data + offset); pos += sizeof(float) * 4 * mesh->color_count; offset += sizeof(float) * 4 * mesh->color_count; } // Read face data mesh->face_type = *((int *) pos); pos += sizeof(mesh->face_type); mesh->face_count = *((int *) pos); pos += sizeof(mesh->face_count); mesh->face_normal_count = *((int *) pos); pos += sizeof(mesh->face_normal_count); mesh->face_tex_coord_count = *((int *) pos); pos += sizeof(mesh->face_tex_coord_count); mesh->face_color_count = *((int *) pos); pos += sizeof(mesh->face_color_count); #if !_WIN32 && !__LITTLE_ENDIAN mesh->face_type = endian_swap(mesh->face_type); mesh->face_count = endian_swap(mesh->face_count); mesh->face_normal_count = endian_swap(mesh->face_normal_count); mesh->face_tex_coord_count = endian_swap(mesh->face_tex_coord_count); mesh->face_color_count = endian_swap(mesh->face_color_count); #endif int face_size = 0; if (mesh->face_type & FACE_TYPE_VERTICES) { face_size += 3; } if ((mesh->face_type & FACE_TYPE_NORMALS)) { face_size += 3; } if ((mesh->face_type & FACE_TYPE_TEXTURES)) { face_size += 3; } if ((mesh->face_type & FACE_TYPE_COLORS)) { face_size += 3; } // faces can be either in the form // f: v/vt/vn ... // or: // f: v ... // f: vn ... // f: vt ... if (mesh->face_count > 0) { memcpy(mesh->data + offset, pos, sizeof(uint32) * face_size * mesh->face_count); mesh->faces = (uint32 *) (mesh->data + offset); pos += sizeof(uint32) * face_size * mesh->face_count; offset += sizeof(uint32) * face_size * mesh->face_count; } if (mesh->face_normal_count > 0) { memcpy(mesh->data + offset, pos, sizeof(uint32) * 3 * mesh->face_normal_count); mesh->face_normals = (uint32 *) (mesh->data + offset); pos += sizeof(uint32) * 3 * mesh->face_normal_count; offset += sizeof(uint32) * 3 * mesh->face_normal_count; } if (mesh->face_tex_coord_count > 0) { memcpy(mesh->data + offset, pos, sizeof(uint32) * 3 * mesh->face_tex_coord_count); mesh->face_textures = (uint32 *) (mesh->data + offset); pos += sizeof(uint32) * 3 * mesh->face_tex_coord_count; offset += sizeof(uint32) * 3 * mesh->face_tex_coord_count; } if (mesh->face_color_count > 0) { memcpy(mesh->data + offset, pos, sizeof(uint32) * 3 * mesh->face_color_count); mesh->face_colors = (uint32 *) (mesh->data + offset); pos += sizeof(uint32) * 3 * mesh->face_color_count; offset += sizeof(uint32) * 3 * mesh->face_color_count; } SWAP_ENDIAN_LITTLE_SIMD( (int *) mesh->data, (int *) mesh->data, offset / 4, // everything is 4 bytes -> super easy to swap steps ); return offset; } void object_to_file( RingMemory* ring, const char* path, const Mesh* mesh, int vertex_save_format = VERTEX_TYPE_POSITION, int face_save_format = FACE_TYPE_VERTICES, int size = 8 ) { FileBody file; // Temporary file size for buffer file.size = sizeof(mesh) + sizeof(Vertex3D) * mesh->vertex_count + sizeof(float) * 12 * mesh->vertex_count + 4096; file.content = ring_get_memory(ring, file.size, 64); byte* pos = file.content; // vertices memcpy(pos, &vertex_save_format, sizeof(vertex_save_format)); pos += sizeof(vertex_save_format); memcpy(pos, &mesh->vertex_count, sizeof(mesh->vertex_count)); pos += sizeof(mesh->vertex_count); // We are can save the mesh in a different format from the current format -> need to adjust some values uint32 normal_count = mesh->normal_count == 0 && (mesh->vertex_type & VERTEX_TYPE_NORMAL) ? mesh->vertex_count : mesh->normal_count; memcpy(pos, &normal_count, sizeof(mesh->normal_count)); pos += sizeof(mesh->normal_count); uint32 tex_coord_count = mesh->tex_coord_count == 0 && (mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD) ? mesh->vertex_count : mesh->tex_coord_count; memcpy(pos, &tex_coord_count, sizeof(mesh->tex_coord_count)); pos += sizeof(mesh->tex_coord_count); uint32 color_count = mesh->color_count == 0 && (mesh->vertex_type & VERTEX_TYPE_COLOR) ? mesh->vertex_count : mesh->color_count; memcpy(pos, &color_count, sizeof(mesh->color_count)); pos += sizeof(mesh->color_count); // verticies if (mesh->vertex_count > 0) { int vertex_size = 0; if (mesh->vertex_type & VERTEX_TYPE_POSITION) { vertex_size += 3; } if (mesh->vertex_type & VERTEX_TYPE_NORMAL) { vertex_size += 3; } if (mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD) { vertex_size += 2; } if (mesh->vertex_type & VERTEX_TYPE_COLOR) { vertex_size += 4; } int out_vertex_size = 0; if (vertex_save_format & VERTEX_TYPE_POSITION) { out_vertex_size += 3; } if (vertex_save_format & VERTEX_TYPE_NORMAL) { out_vertex_size += 3; } if (vertex_save_format & VERTEX_TYPE_TEXTURE_COORD) { out_vertex_size += 2; } if (vertex_save_format & VERTEX_TYPE_COLOR) { out_vertex_size += 4; } if ((mesh->vertex_type == VERTEX_TYPE_ALL && vertex_save_format == VERTEX_TYPE_ALL) || (mesh->vertex_type == VERTEX_TYPE_POSITION && vertex_save_format == VERTEX_TYPE_POSITION) ) { // data is the same as in the array memcpy(pos, mesh->vertices, vertex_size * sizeof(float) * mesh->vertex_count); pos += vertex_size * sizeof(float) * mesh->vertex_count; } else { float* temp = mesh->vertices; float* end = mesh->vertices + mesh->vertex_count * vertex_size; int offset; byte* vertice_start = pos; // @bug index gets increased every iteration BUT different groups and objects in the source may have different data // This comes again down to how to handle hierarchal data with multiple groups and objects int index = 0; // iterate over all vertices to create new output format while (temp < end) { // @question why do I even need offset? couldn't I just directly manipulate temp? offset = 0; // First we save everything in one large array if that is the setting if (vertex_save_format & VERTEX_TYPE_POSITION) { if (mesh->vertex_type & VERTEX_TYPE_POSITION) { memcpy(pos, temp, sizeof(float) * 3); pos += sizeof(float) * 3; offset += 3; } else { memset(pos, 0, sizeof(float) * 3); pos += sizeof(float) * 3; } } // We want separate arrays for some data if ((mesh->vertex_type & VERTEX_TYPE_NORMAL) && !(vertex_save_format & VERTEX_TYPE_NORMAL)) { // go to end of vertices * shift by previous array sizes + shift by current vertex -> creates one continous array with this data memcpy(vertice_start + sizeof(float) * out_vertex_size * mesh->vertex_count + index * sizeof(float) * 3, temp + offset, sizeof(float) * 3); offset += 3; } else if (vertex_save_format & VERTEX_TYPE_NORMAL) { if (mesh->vertex_type & VERTEX_TYPE_NORMAL) { memcpy(pos, temp + offset, sizeof(float) * 3); pos += sizeof(float) * 3; offset += 3; } else { memset(pos, 0, sizeof(float) * 3); pos += sizeof(float) * 3; } } // We want separate arrays for some data if ((mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD) && !(vertex_save_format & VERTEX_TYPE_TEXTURE_COORD)) { // go to end of vertices * shift by previous array sizes + shift by current vertex -> creates one continous array with this data memcpy(vertice_start + sizeof(float) * out_vertex_size * mesh->vertex_count + sizeof(float) * normal_count * 3 + index * sizeof(float) * 3, temp + offset, sizeof(float) * 3); offset += 2; } else if (vertex_save_format & VERTEX_TYPE_TEXTURE_COORD) { if (mesh->vertex_type & VERTEX_TYPE_TEXTURE_COORD) { memcpy(pos, temp + offset, sizeof(float) * 2); pos += sizeof(float) * 2; offset += 2; } else { memset(pos, 0, sizeof(float) * 2); pos += sizeof(float) * 2; } } // We want separate arrays for some data if ((mesh->vertex_type & VERTEX_TYPE_COLOR) && !(vertex_save_format & VERTEX_TYPE_COLOR)) { // go to end of vertices * shift by previous array sizes + shift by current vertex -> creates one continous array with this data memcpy(vertice_start + sizeof(float) * out_vertex_size * mesh->vertex_count + sizeof(float) * normal_count * 3 + sizeof(float) * tex_coord_count * 2 + index * sizeof(float) * 4, temp + offset, sizeof(float) * 4); offset += 4; } else if (vertex_save_format & VERTEX_TYPE_COLOR) { if (mesh->vertex_type & VERTEX_TYPE_COLOR) { memcpy(pos, temp + offset, sizeof(float) * 4); pos += sizeof(float) * 4; offset += 4; } else { memset(pos, 0, sizeof(float) * 4); pos += sizeof(float) * 4; } } temp += offset; ++index; } } // check if we have clean array data already -> output this array data directly if (mesh->normals && mesh->normal_count > 0) { memcpy(pos, mesh->normals, mesh->normal_count * sizeof(float) * 3); pos += mesh->normal_count * sizeof(float) * 3; } if (mesh->tex_coords && mesh->tex_coord_count > 0) { memcpy(pos, mesh->tex_coords, mesh->tex_coord_count * sizeof(float) * 2); pos += mesh->tex_coord_count * sizeof(float) * 2; } if (mesh->colors && mesh->color_count > 0) { memcpy(pos, mesh->colors, mesh->color_count * sizeof(float) * 4); pos += mesh->color_count * sizeof(float) * 4; } } // faces/indices memcpy(pos, &face_save_format, sizeof(face_save_format)); pos += sizeof(face_save_format); memcpy(pos, &mesh->face_count, sizeof(mesh->face_count)); pos += sizeof(mesh->face_count); // We are can save the mesh in a different format from the current format -> need to adjust some values uint32 face_normal_count = mesh->face_normal_count == 0 && (mesh->face_type & FACE_TYPE_NORMALS) ? mesh->face_count : mesh->face_normal_count; memcpy(pos, &face_normal_count, sizeof(mesh->face_normal_count)); pos += sizeof(mesh->face_normal_count); uint32 face_tex_coord_count = mesh->face_tex_coord_count == 0 && (mesh->face_type & FACE_TYPE_TEXTURES) ? mesh->face_count : mesh->face_tex_coord_count; memcpy(pos, &face_tex_coord_count, sizeof(mesh->face_tex_coord_count)); pos += sizeof(mesh->face_tex_coord_count); uint32 face_color_count = mesh->face_color_count == 0 && (mesh->face_type & FACE_TYPE_COLORS) ? mesh->face_count : mesh->face_color_count; memcpy(pos, &face_color_count, sizeof(mesh->face_color_count)); pos += sizeof(mesh->face_color_count); if (mesh->face_count > 0) { // WARNING: Carefull, we again assume only 3 elements per face int face_size = 0; if (mesh->face_type & FACE_TYPE_VERTICES) { face_size += 3; } if (mesh->face_type & FACE_TYPE_NORMALS) { face_size += 3; } if (mesh->face_type & FACE_TYPE_TEXTURES) { face_size += 3; } if (mesh->face_type & FACE_TYPE_COLORS) { face_size += 3; } int out_face_size = 0; if (face_save_format & FACE_TYPE_VERTICES) { out_face_size += 3; } if (face_save_format & FACE_TYPE_NORMALS) { out_face_size += 3; } if (face_save_format & FACE_TYPE_TEXTURES) { out_face_size += 3; } if (face_save_format & FACE_TYPE_COLORS) { out_face_size += 3; } if ((mesh->face_type == FACE_TYPE_ALL && face_save_format == FACE_TYPE_ALL) || (mesh->face_type == FACE_TYPE_VERTICES && face_save_format == FACE_TYPE_VERTICES) ) { // data is the same as in the array memcpy(pos, mesh->faces, face_size * sizeof(uint32) * mesh->face_count); pos += face_size * sizeof(uint32) * mesh->face_count; } else { uint32* temp = mesh->faces; uint32* end = mesh->faces + mesh->face_count * face_size; int offset; byte* face_start = pos; // @bug index gets increased every iteration BUT different groups and objects in the source may have different data // This comes again down to how to handle hierarchal data with multiple groups and objects int index = 0; // iterate over all faces to create new output format // one iteration represents 1 block (a block could be v/vt/vn or, v, or v//vn, ...) while (temp < end) { // @question why do I even need offset? couldn't I just directly manipulate temp? offset = 0; // First we save everything in one large array if that is the setting if (face_save_format & FACE_TYPE_VERTICES) { if (mesh->face_type & FACE_TYPE_VERTICES) { memcpy(pos, temp, sizeof(uint32)); pos += sizeof(uint32); offset += 1; } else { memset(pos, 0, sizeof(float)); pos += sizeof(float); } } // We want separate arrays for some data if (mesh->face_type & FACE_TYPE_NORMALS && !(face_save_format & FACE_TYPE_NORMALS)) { // go to end of faces * shift by previous array sizes + shift by current face -> creates one continous array with this data memcpy(face_start + sizeof(uint32) * out_face_size * mesh->face_count + index * sizeof(uint32), temp + offset, sizeof(uint32)); offset += 1; } else if (face_save_format & FACE_TYPE_NORMALS) { if (mesh->face_type & FACE_TYPE_NORMALS) { memcpy(pos, temp + offset, sizeof(uint32)); pos += sizeof(uint32); offset += 1; } else { memset(pos, 0, sizeof(uint32)); pos += sizeof(uint32); } } // We want separate arrays for some data if (mesh->face_type & FACE_TYPE_TEXTURES && !(face_save_format & FACE_TYPE_TEXTURES)) { // go to end of faces * shift by previous array sizes + shift by current face -> creates one continous array with this data memcpy(face_start + sizeof(uint32) * out_face_size * mesh->face_count + sizeof(uint32) * 3 * face_normal_count + index * sizeof(uint32), temp + offset, sizeof(uint32)); offset += 1; } else if (face_save_format & FACE_TYPE_TEXTURES) { if (mesh->face_type & FACE_TYPE_TEXTURES) { memcpy(pos, temp + offset, sizeof(uint32)); pos += sizeof(uint32); offset += 1; } else { memset(pos, 0, sizeof(uint32)); pos += sizeof(uint32); } } // We want separate arrays for some data if (mesh->face_type & VERTEX_TYPE_COLOR && !(face_save_format & VERTEX_TYPE_COLOR)) { // go to end of faces * shift by previous array sizes + shift by current face -> creates one continous array with this data memcpy(face_start + sizeof(uint32) * out_face_size * mesh->face_count + sizeof(uint32) * 3 * face_normal_count + sizeof(uint32) * 3 * face_tex_coord_count + index * sizeof(uint32), temp + offset, sizeof(uint32)); offset += 1; } else if (face_save_format & VERTEX_TYPE_COLOR) { if (mesh->face_type & VERTEX_TYPE_COLOR) { memcpy(pos, temp + offset, sizeof(uint32)); pos += sizeof(uint32); offset += 1; } else { memset(pos, 0, sizeof(uint32)); pos += sizeof(uint32); } } temp += offset; ++index; } } // check if we have clean array data already -> output this array data directly if (mesh->face_normals && mesh->face_normal_count > 0) { memcpy(pos, mesh->face_normals, mesh->face_normal_count * sizeof(uint32) * 3); pos += mesh->face_normal_count * sizeof(uint32) * 3; } if (mesh->face_textures && mesh->face_tex_coord_count > 0) { memcpy(pos, mesh->face_textures, mesh->face_tex_coord_count * sizeof(uint32) * 3); pos += mesh->face_tex_coord_count * sizeof(uint32) * 3; } if (mesh->face_colors && mesh->face_color_count > 0) { memcpy(pos, mesh->face_colors, mesh->face_color_count * sizeof(uint32) * 3); pos += mesh->face_color_count * sizeof(uint32) * 3; } } file.size = pos - file.content; SWAP_ENDIAN_LITTLE_SIMD( (int *) file.content, (int *) file.content, (pos - file.content) / 4, // everything in here is 4 bytes -> super easy to swap steps ); /* FileBody file2; file2.content = ring_get_memory(ring, file.size, 64); file2.size = encode_lzp(file.content, file.size, file2.content); file_write(path, &file2); */ file_write(path, &file); } #endif