diff --git a/src/rmodels.c b/src/rmodels.c index 68c2d75bc..0fc305ddc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3931,9 +3931,10 @@ static Model LoadOBJ(const char *fileName) if (fileText != NULL) { unsigned int dataSize = (unsigned int)strlen(fileText); + char currentDir[1024] = { 0 }; - strcpy(currentDir, GetWorkingDirectory()); - const char *workingDir = GetDirectoryPath(fileName); + strcpy(currentDir, GetWorkingDirectory()); // Save current working directory + const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness if (CHDIR(workingDir) != 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir); @@ -3945,117 +3946,91 @@ static Model LoadOBJ(const char *fileName) if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName); else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes/%i materials", fileName, meshCount, materialCount); - model.meshCount = materialCount; + // WARNING: We are not splitting meshes by materials (previous implementation) + // Depending on the provided OBJ that was not the best option and it just crashed + // so, implementation was simplified to prioritize parsed meshes + model.meshCount = meshCount; - // Init model materials array - if (materialCount > 0) + // Set number of materials available + // NOTE: There could be more materials available than meshes but it will be resolved at + // model.meshMaterial, just assigning the right material to corresponding mesh + model.materialCount = materialCount; + if (model.materialCount == 0) { - model.materialCount = materialCount; - model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); - TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", materialCount); - } - else - { - model.meshCount = 1; - TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); + model.materialCount = 1; + TRACELOG(LOG_INFO, "MODEL: No materials provided, setting one default material for all meshes"); } + // Init model meshes and materials model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); - model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); - - // Count the faces for each material - int *matFaces = RL_CALLOC(model.meshCount, sizeof(int)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); // Material index assigned to each mesh + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); - // if no materials are present use all faces on one mesh - if (materialCount > 0) + // Process each provided mesh + for (int i = 0; i < model.meshCount; i++) { - for (unsigned int fi = 0; fi < attrib.num_faces; fi++) + // WARNING: We need to calculate the mesh triangles manually using meshes[i].face_offset + // because in case of triangulated quads, meshes[i].length actually report quads, + // despite the triangulation that is efectively considered on attrib.num_faces + unsigned int tris = 0; + if (i == model.meshCount - 1) tris = attrib.num_faces - meshes[i].face_offset; + else tris = meshes[i + 1].face_offset; + + model.meshes[i].vertexCount = tris*3; + model.meshes[i].triangleCount = tris; // Face count (triangulated) + model.meshes[i].vertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshes[i].texcoords = (float *)RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); + model.meshes[i].normals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshMaterial[i] = 0; // By default, assign material 0 to each mesh + + // Process all mesh faces + for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 2, vn += 3) { - //tinyobj_vertex_index_t face = attrib.faces[fi]; - int idx = attrib.material_ids[fi]; - matFaces[idx]++; - } - - } - else - { - matFaces[0] = attrib.num_faces; - } + // Get indices for the face + tinyobj_vertex_index_t idx0 = attrib.faces[f*3 + 0]; + tinyobj_vertex_index_t idx1 = attrib.faces[f*3 + 1]; + tinyobj_vertex_index_t idx2 = attrib.faces[f*3 + 2]; - //-------------------------------------- - // Create the material meshes + // Fill vertices buffer (float) using vertex index of the face + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[v*3 + n] = attrib.vertices[idx0.v_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 1)*3 + n] = attrib.vertices[idx1.v_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 2)*3 + n] = attrib.vertices[idx2.v_idx*3 + n]; } - // Running counts/indexes for each material mesh as we are - // building them at the same time - int *vCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *vtCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *vnCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *faceCount = RL_CALLOC(model.meshCount, sizeof(int)); - - // Allocate space for each of the material meshes - for (int mi = 0; mi < model.meshCount; mi++) - { - model.meshes[mi].vertexCount = matFaces[mi]*3; - model.meshes[mi].triangleCount = matFaces[mi]; - model.meshes[mi].vertices = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); - model.meshes[mi].texcoords = (float *)RL_CALLOC(model.meshes[mi].vertexCount*2, sizeof(float)); - model.meshes[mi].normals = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); - model.meshMaterial[mi] = mi; - } - - // Scan through the combined sub meshes and pick out each material mesh - for (unsigned int af = 0; af < attrib.num_faces; af++) - { - int mm = attrib.material_ids[af]; // mesh material for this face - if (mm == -1) { mm = 0; } // no material object.. - - // Get indices for the face - tinyobj_vertex_index_t idx0 = attrib.faces[3*af + 0]; - tinyobj_vertex_index_t idx1 = attrib.faces[3*af + 1]; - tinyobj_vertex_index_t idx2 = attrib.faces[3*af + 2]; + if (attrib.num_texcoords > 0) + { + // Fill texcoords buffer (float) using vertex index of the face + // NOTE: Y-coordinate must be flipped upside-down + model.meshes[i].texcoords[vt*2 + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; + model.meshes[i].texcoords[vt*2 + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; - // Fill vertices buffer (float) using vertex index of the face - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount[mm] +=3; + model.meshes[i].texcoords[(vt + 1)*2 + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; + model.meshes[i].texcoords[(vt + 1)*2 + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; - if (attrib.num_texcoords > 0) - { - // Fill texcoords buffer (float) using vertex index of the face - // NOTE: Y-coordinate must be flipped upside-down to account for - // raylib's upside down textures... - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount[mm] += 2; - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount[mm] += 2; - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount[mm] += 2; - } + model.meshes[i].texcoords[(vt + 2)*2 + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; + model.meshes[i].texcoords[(vt + 2)*2 + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; + } - if (attrib.num_normals > 0) - { - // Fill normals buffer (float) using vertex index of the face - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount[mm] +=3; + if (attrib.num_normals > 0) + { + // Fill normals buffer (float) using vertex index of the face + for (int n = 0; n < 3; n++) { model.meshes[i].normals[vn*3 + n] = attrib.normals[idx0.vn_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 1)*3 + n] = attrib.normals[idx1.vn_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 2)*3 + n] = attrib.normals[idx2.vn_idx*3 + n]; } + } } } // Init model materials - ProcessMaterialsOBJ(model.materials, materials, materialCount); + if (materialCount > 0) ProcessMaterialsOBJ(model.materials, materials, materialCount); + else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh tinyobj_attrib_free(&attrib); - tinyobj_shapes_free(meshes, meshCount); + tinyobj_shapes_free(meshes, model.meshCount); tinyobj_materials_free(materials, materialCount); UnloadFileText(fileText); - RL_FREE(matFaces); - RL_FREE(vCount); - RL_FREE(vtCount); - RL_FREE(vnCount); - RL_FREE(faceCount); - + // Restore current working directory if (CHDIR(currentDir) != 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir);