Browse Source

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
pull/3497/head
Ray 1 year ago
parent
commit
43f36b9b05
1 changed files with 65 additions and 90 deletions
  1. +65
    -90
      src/rmodels.c

+ 65
- 90
src/rmodels.c View File

@ -3931,9 +3931,10 @@ static Model LoadOBJ(const char *fileName)
if (fileText != NULL) if (fileText != NULL)
{ {
unsigned int dataSize = (unsigned int)strlen(fileText); unsigned int dataSize = (unsigned int)strlen(fileText);
char currentDir[1024] = { 0 }; char currentDir[1024] = { 0 };
strcpy(currentDir, GetWorkingDirectory()); strcpy(currentDir, GetWorkingDirectory()); // Save current working directory
const char *workingDir = GetDirectoryPath(fileName); const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness
if (CHDIR(workingDir) != 0) if (CHDIR(workingDir) != 0)
{ {
TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir); 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); 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); 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 // Set number of materials available
if (materialCount > 0) // 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.materialCount = 1;
model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); TRACELOG(LOG_INFO, "MODEL: No materials provided, setting one default material for all meshes");
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");
} }
// Init model meshes and materials
model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh));
model.meshMaterial = (int *)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));
// Count the faces for each material
int *matFaces = RL_CALLOC(model.meshCount, sizeof(int));
// k">if no materials are present use all faces on one mesh // Process each provided mesh
if (materialCount > 0) 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]; // Get indices for the face
int idx = attrib.material_ids[fi]; tinyobj_vertex_index_t idx0 = attrib.faces[f*3 + 0];
matFaces[idx]++; tinyobj_vertex_index_t idx1 = attrib.faces[f*3 + 1];
} tinyobj_vertex_index_t idx2 = attrib.faces[f*3 + 2];
}
else
{
matFaces[0] = attrib.num_faces;
}
//-------------------------------------- // Fill vertices buffer (float) using vertex index of the face
// Create the material meshes 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 if (attrib.num_texcoords > 0)
// building them at the same time {
int *vCount = RL_CALLOC(model.meshCount, sizeof(int)); // Fill texcoords buffer (float) using vertex index of the face
int *vtCount = RL_CALLOC(model.meshCount, sizeof(int)); // NOTE: Y-coordinate must be flipped upside-down
int *vnCount = RL_CALLOC(model.meshCount, sizeof(int)); model.meshes[i].texcoords[vt*2 + 0] = attrib.texcoords[idx0.vt_idx*2 + 0];
int *faceCount = RL_CALLOC(model.meshCount, sizeof(int)); model.meshes[i].texcoords[vt*2 + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1];
// 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];
// Fill vertices buffer (float) using vertex index of the face model.meshes[i].texcoords[(vt + 1)*2 + 0] = attrib.texcoords[idx1.vt_idx*2 + 0];
for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount[mm] +=3; model.meshes[i].texcoords[(vt + 1)*2 + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1];
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;
if (attrib.num_texcoords > 0) 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];
// 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;
}
if (attrib.num_normals > 0) if (attrib.num_normals > 0)
{ {
// Fill normals buffer (float) using vertex index of the face // 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 n = 0; n < 3; n++) { model.meshes[i].normals[vn*3 + n] = attrib.normals[idx0.vn_idx*3 + n]; }
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 n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 1)*3 + n] = attrib.normals[idx1.vn_idx*3 + n]; }
for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount[mm] +=3; 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 // 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_attrib_free(&attrib);
tinyobj_shapes_free(meshes, meshCount); tinyobj_shapes_free(meshes, model.meshCount);
tinyobj_materials_free(materials, materialCount); tinyobj_materials_free(materials, materialCount);
UnloadFileText(fileText); UnloadFileText(fileText);
RL_FREE(matFaces); // Restore current working directory
RL_FREE(vCount);
RL_FREE(vtCount);
RL_FREE(vnCount);
RL_FREE(faceCount);
if (CHDIR(currentDir) != 0) if (CHDIR(currentDir) != 0)
{ {
TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir); TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir);

||||||
x
 
000:0
Loading…
Cancel
Save