summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRay <[email protected]>2023-10-31 20:15:45 +0100
committerRay <[email protected]>2023-10-31 20:15:45 +0100
commit43f36b9b05d99138307318e29ff80443fc1b4249 (patch)
tree23ddbdd47faa1469949e03911f18033ba22cb4e3 /src
parent3645244f9fc1874e72252138ca4ed0ab849a2fd9 (diff)
downloadraylib-43f36b9b05d99138307318e29ff80443fc1b4249.tar.gz
raylib-43f36b9b05d99138307318e29ff80443fc1b4249.zip
REDESIGNED: `LoadOBJ()`, fix #3398
- Now triangulated meshes are properly supported - Simplified code to consider issue situation - Removed mesh split per material, just define separate mesh if multiple materials are required
Diffstat (limited to 'src')
-rw-r--r--src/rmodels.c155
1 files changed, 65 insertions, 90 deletions
diff --git a/src/rmodels.c b/src/rmodels.c
index 68c2d75b..0fc305dd 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);