aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRay <raysan5@gmail.com>2019-03-29 17:15:22 +0100
committerRay <raysan5@gmail.com>2019-03-29 17:15:22 +0100
commit8a73c5d0b403264ebd66cb5a2e1b21f91f6991b0 (patch)
treee699b711bfd9a43632d94b5ac2bb437d41c3525c /src
parenta643dc4ca016ce5791608852ab1357f2d94ebe9b (diff)
downloadraylib-8a73c5d0b403264ebd66cb5a2e1b21f91f6991b0.tar.gz
raylib-8a73c5d0b403264ebd66cb5a2e1b21f91f6991b0.zip
Replace custom OBJ/MTL implementations by tinyobj_loader -WIP-
Diffstat (limited to 'src')
-rw-r--r--src/models.c495
1 files changed, 89 insertions, 406 deletions
diff --git a/src/models.c b/src/models.c
index c9d92315..ec2713ed 100644
--- a/src/models.c
+++ b/src/models.c
@@ -52,9 +52,9 @@
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
-#if defined(SUPPORT_FILEFORMAT_OBJ)
+#if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL)
#define TINYOBJ_LOADER_C_IMPLEMENTATION
- #include "external/tinyobj_loader_c.h" // OBJ file format loading
+ #include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading
#endif
#if defined(SUPPORT_FILEFORMAT_IQM)
@@ -93,9 +93,6 @@
#if defined(SUPPORT_FILEFORMAT_OBJ)
static Model LoadOBJ(const char *fileName); // Load OBJ mesh data
#endif
-#if defined(SUPPORT_FILEFORMAT_MTL)
-static Material LoadMTL(const char *fileName); // Load MTL material data
-#endif
#if defined(SUPPORT_FILEFORMAT_GLTF)
static Model LoadIQM(const char *fileName); // Load IQM mesh data
#endif
@@ -1811,7 +1808,17 @@ Material LoadMaterial(const char *fileName)
Material material = { 0 };
#if defined(SUPPORT_FILEFORMAT_MTL)
- if (IsFileExtension(fileName, ".mtl")) material = LoadMTL(fileName);
+ if (IsFileExtension(fileName, ".mtl"))
+ {
+ tinyobj_material_t *materials;
+ int materialCount = 0;
+
+ int result = tinyobj_parse_mtl_file(&materials, &materialCount, fileName);
+
+ // TODO: Process materials to return
+
+ tinyobj_materials_free(materials, materialCount);
+ }
#else
TraceLog(LOG_WARNING, "[%s] Material fileformat not supported, it can't be loaded", fileName);
#endif
@@ -2359,419 +2366,95 @@ void MeshBinormals(Mesh *mesh)
static Model LoadOBJ(const char *fileName)
{
Model model = { 0 };
-
- // TODO: Use tinyobj_loader_c library
-/*
- char dataType = 0;
- char comments[200];
-
- int vertexCount = 0;
- int normalCount = 0;
- int texcoordCount = 0;
- int triangleCount = 0;
-
- FILE *objFile;
-
- objFile = fopen(fileName, "rt");
-
- if (objFile == NULL)
- {
- TraceLog(LOG_WARNING, "[%s] OBJ file could not be opened", fileName);
- return mesh;
- }
-
- // First reading pass: Get vertexCount, normalCount, texcoordCount, triangleCount
- // NOTE: vertex, texcoords and normals could be optimized (to be used indexed on faces definition)
- // NOTE: faces MUST be defined as TRIANGLES (3 vertex per face)
- while (!feof(objFile))
- {
- dataType = 0;
- fscanf(objFile, "%c", &dataType);
-
- switch (dataType)
- {
- case '#': // Comments
- case 'o': // Object name (One OBJ file can contain multible named meshes)
- case 'g': // Group name
- case 's': // Smoothing level
- case 'm': // mtllib [external .mtl file name]
- case 'u': // usemtl [material name]
- {
- fgets(comments, 200, objFile);
- } break;
- case 'v':
- {
- fscanf(objFile, "%c", &dataType);
-
- if (dataType == 't') // Read texCoord
- {
- texcoordCount++;
- fgets(comments, 200, objFile);
- }
- else if (dataType == 'n') // Read normals
- {
- normalCount++;
- fgets(comments, 200, objFile);
- }
- else // Read vertex
- {
- vertexCount++;
- fgets(comments, 200, objFile);
- }
- } break;
- case 'f':
- {
- triangleCount++;
- fgets(comments, 200, objFile);
- } break;
- default: break;
- }
- }
-
- TraceLog(LOG_DEBUG, "[%s] Model vertices: %i", fileName, vertexCount);
- TraceLog(LOG_DEBUG, "[%s] Model texcoords: %i", fileName, texcoordCount);
- TraceLog(LOG_DEBUG, "[%s] Model normals: %i", fileName, normalCount);
- TraceLog(LOG_DEBUG, "[%s] Model triangles: %i", fileName, triangleCount);
-
- // Once we know the number of vertices to store, we create required arrays
- Vector3 *midVertices = (Vector3 *)malloc(vertexCount*sizeof(Vector3));
- Vector3 *midNormals = NULL;
- if (normalCount > 0) midNormals = (Vector3 *)malloc(normalCount*sizeof(Vector3));
- Vector2 *midTexCoords = NULL;
- if (texcoordCount > 0) midTexCoords = (Vector2 *)malloc(texcoordCount*sizeof(Vector2));
-
- int countVertex = 0;
- int countNormals = 0;
- int countTexCoords = 0;
-
- rewind(objFile); // Return to the beginning of the file, to read again
-
- // Second reading pass: Get vertex data to fill intermediate arrays
- // NOTE: This second pass is required in case of multiple meshes defined in same OBJ
- // TODO: Consider that different meshes can have different vertex data available (position, texcoords, normals)
- while (!feof(objFile))
- {
- fscanf(objFile, "%c", &dataType);
-
- switch (dataType)
- {
- case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'f': fgets(comments, 200, objFile); break;
- case 'v':
- {
- fscanf(objFile, "%c", &dataType);
-
- if (dataType == 't') // Read texCoord
- {
- fscanf(objFile, "%f %f%*[^\n]s\n", &midTexCoords[countTexCoords].x, &midTexCoords[countTexCoords].y);
- countTexCoords++;
-
- fscanf(objFile, "%c", &dataType);
- }
- else if (dataType == 'n') // Read normals
- {
- fscanf(objFile, "%f %f %f", &midNormals[countNormals].x, &midNormals[countNormals].y, &midNormals[countNormals].z);
- countNormals++;
-
- fscanf(objFile, "%c", &dataType);
- }
- else // Read vertex
- {
- fscanf(objFile, "%f %f %f", &midVertices[countVertex].x, &midVertices[countVertex].y, &midVertices[countVertex].z);
- countVertex++;
-
- fscanf(objFile, "%c", &dataType);
- }
- } break;
- default: break;
- }
- }
-
- // At this point all vertex data (v, vt, vn) has been gathered on midVertices, midTexCoords, midNormals
- // Now we can organize that data into our Mesh struct
-
- mesh.vertexCount = triangleCount*3;
-
- // Additional arrays to store vertex data as floats
- mesh.vertices = (float *)malloc(mesh.vertexCount*3*sizeof(float));
- mesh.texcoords = (float *)malloc(mesh.vertexCount*2*sizeof(float));
- mesh.normals = (float *)malloc(mesh.vertexCount*3*sizeof(float));
- mesh.colors = NULL;
-
- int vCounter = 0; // Used to count vertices float by float
- int tcCounter = 0; // Used to count texcoords float by float
- int nCounter = 0; // Used to count normals float by float
-
- int vCount[3], vtCount[3], vnCount[3]; // Used to store triangle indices for v, vt, vn
-
- rewind(objFile); // Return to the beginning of the file, to read again
-
- if (normalCount == 0) TraceLog(LOG_INFO, "[%s] No normals data on OBJ, normals will be generated from faces data", fileName);
+ tinyobj_attrib_t attrib;
+ tinyobj_shape_t *meshes = NULL;
+ int meshCount = 0;
+
+ tinyobj_material_t *materials = NULL;
+ int materialCount = 0;
- // Third reading pass: Get faces (triangles) data and fill VertexArray
- while (!feof(objFile))
+ int dataLength = 0;
+ const char *data = get_file_data(&dataLength, fileName);
+
+ if (data != NULL)
{
- fscanf(objFile, "%c", &dataType);
-
- switch (dataType)
+ unsigned int flags = TINYOBJ_FLAG_TRIANGULATE;
+ int ret = tinyobj_parse_obj(&attrib, &meshes, &meshCount, &materials, &materialCount, data, dataLength, flags);
+
+ if (ret != TINYOBJ_SUCCESS) TraceLog(LOG_WARNING, "[%s] Model data could not be loaded", fileName);
+ else TraceLog(LOG_INFO, "[%s] Model data loaded successfully: %i meshes / %i materials", fileName, meshCount, materialCount);
+
+ for (int i = 0; i < meshCount; i++)
{
- case '#': case 'o': case 'g': case 's': case 'm': case 'u': case 'v': fgets(comments, 200, objFile); break;
- case 'f':
- {
- // NOTE: It could be that OBJ does not have normals or texcoords defined!
-
- if ((normalCount == 0) && (texcoordCount == 0)) fscanf(objFile, "%i %i %i", &vCount[0], &vCount[1], &vCount[2]);
- else if (normalCount == 0) fscanf(objFile, "%i/%i %i/%i %i/%i", &vCount[0], &vtCount[0], &vCount[1], &vtCount[1], &vCount[2], &vtCount[2]);
- else if (texcoordCount == 0) fscanf(objFile, "%i//%i %i//%i %i//%i", &vCount[0], &vnCount[0], &vCount[1], &vnCount[1], &vCount[2], &vnCount[2]);
- else fscanf(objFile, "%i/%i/%i %i/%i/%i %i/%i/%i", &vCount[0], &vtCount[0], &vnCount[0], &vCount[1], &vtCount[1], &vnCount[1], &vCount[2], &vtCount[2], &vnCount[2]);
-
- mesh.vertices[vCounter] = midVertices[vCount[0]-1].x;
- mesh.vertices[vCounter + 1] = midVertices[vCount[0]-1].y;
- mesh.vertices[vCounter + 2] = midVertices[vCount[0]-1].z;
- vCounter += 3;
- mesh.vertices[vCounter] = midVertices[vCount[1]-1].x;
- mesh.vertices[vCounter + 1] = midVertices[vCount[1]-1].y;
- mesh.vertices[vCounter + 2] = midVertices[vCount[1]-1].z;
- vCounter += 3;
- mesh.vertices[vCounter] = midVertices[vCount[2]-1].x;
- mesh.vertices[vCounter + 1] = midVertices[vCount[2]-1].y;
- mesh.vertices[vCounter + 2] = midVertices[vCount[2]-1].z;
- vCounter += 3;
-
- if (normalCount > 0)
- {
- mesh.normals[nCounter] = midNormals[vnCount[0]-1].x;
- mesh.normals[nCounter + 1] = midNormals[vnCount[0]-1].y;
- mesh.normals[nCounter + 2] = midNormals[vnCount[0]-1].z;
- nCounter += 3;
- mesh.normals[nCounter] = midNormals[vnCount[1]-1].x;
- mesh.normals[nCounter + 1] = midNormals[vnCount[1]-1].y;
- mesh.normals[nCounter + 2] = midNormals[vnCount[1]-1].z;
- nCounter += 3;
- mesh.normals[nCounter] = midNormals[vnCount[2]-1].x;
- mesh.normals[nCounter + 1] = midNormals[vnCount[2]-1].y;
- mesh.normals[nCounter + 2] = midNormals[vnCount[2]-1].z;
- nCounter += 3;
- }
- else
- {
- // If normals not defined, they are calculated from the 3 vertices [N = (V2 - V1) x (V3 - V1)]
- Vector3 norm = Vector3CrossProduct(Vector3Subtract(midVertices[vCount[1]-1], midVertices[vCount[0]-1]), Vector3Subtract(midVertices[vCount[2]-1], midVertices[vCount[0]-1]));
- norm = Vector3Normalize(norm);
-
- mesh.normals[nCounter] = norm.x;
- mesh.normals[nCounter + 1] = norm.y;
- mesh.normals[nCounter + 2] = norm.z;
- nCounter += 3;
- mesh.normals[nCounter] = norm.x;
- mesh.normals[nCounter + 1] = norm.y;
- mesh.normals[nCounter + 2] = norm.z;
- nCounter += 3;
- mesh.normals[nCounter] = norm.x;
- mesh.normals[nCounter + 1] = norm.y;
- mesh.normals[nCounter + 2] = norm.z;
- nCounter += 3;
- }
-
- if (texcoordCount > 0)
- {
- // NOTE: If using negative texture coordinates with a texture filter of GL_CLAMP_TO_EDGE doesn't work!
- // NOTE: Texture coordinates are Y flipped upside-down
- mesh.texcoords[tcCounter] = midTexCoords[vtCount[0]-1].x;
- mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtCount[0]-1].y;
- tcCounter += 2;
- mesh.texcoords[tcCounter] = midTexCoords[vtCount[1]-1].x;
- mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtCount[1]-1].y;
- tcCounter += 2;
- mesh.texcoords[tcCounter] = midTexCoords[vtCount[2]-1].x;
- mesh.texcoords[tcCounter + 1] = 1.0f - midTexCoords[vtCount[2]-1].y;
- tcCounter += 2;
- }
- } break;
- default: break;
+ printf("shape[%d] name = %s\n", i, meshes[i].name);
}
+
+ /*
+ // Data reference to process
+ typedef struct {
+ char *name;
+
+ float ambient[3];
+ float diffuse[3];
+ float specular[3];
+ float transmittance[3];
+ float emission[3];
+ float shininess;
+ float ior; // index of refraction
+ float dissolve; // 1 == opaque; 0 == fully transparent
+ // illumination model (see http://www.fileformat.info/format/material/)
+ int illum;
+
+ int pad0;
+
+ char *ambient_texname; // map_Ka
+ char *diffuse_texname; // map_Kd
+ char *specular_texname; // map_Ks
+ char *specular_highlight_texname; // map_Ns
+ char *bump_texname; // map_bump, bump
+ char *displacement_texname; // disp
+ char *alpha_texname; // map_d
+ } tinyobj_material_t;
+
+ typedef struct {
+ char *name; // group name or object name
+ unsigned int face_offset;
+ unsigned int length;
+ } tinyobj_shape_t;
+
+ typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t;
+
+ typedef struct {
+ unsigned int num_vertices;
+ unsigned int num_normals;
+ unsigned int num_texcoords;
+ unsigned int num_faces;
+ unsigned int num_face_num_verts;
+
+ int pad0;
+
+ float *vertices;
+ float *normals;
+ float *texcoords;
+ tinyobj_vertex_index_t *faces;
+ int *face_num_verts;
+ int *material_ids;
+ } tinyobj_attrib_t;
+ */
+
+ tinyobj_attrib_free(&attrib);
+ tinyobj_shapes_free(meshes, meshCount);
+ tinyobj_materials_free(materials, materialCount);
}
- fclose(objFile);
-
- // Now we can free temp mid* arrays
- free(midVertices);
- free(midNormals);
- free(midTexCoords);
-*/
-
- // NOTE: At this point we have all vertex, texcoord, normal data for the model in mesh struct
+ // NOTE: At this point we have all model data loaded
TraceLog(LOG_INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName);
return model;
}
#endif
-#if defined(SUPPORT_FILEFORMAT_MTL)
-// Load MTL material data (specs: http://paulbourke.net/dataformats/mtl/)
-// NOTE: Texture map parameters are not supported
-static Material LoadMTL(const char *fileName)
-{
- #define MAX_BUFFER_SIZE 128
-
- Material material = { 0 };
-
- char buffer[MAX_BUFFER_SIZE];
- Vector3 color = { 1.0f, 1.0f, 1.0f };
- char mapFileName[128];
- int result = 0;
-
- FILE *mtlFile;
-
- mtlFile = fopen(fileName, "rt");
-
- if (mtlFile == NULL)
- {
- TraceLog(LOG_WARNING, "[%s] MTL file could not be opened", fileName);
- return material;
- }
-
- while (!feof(mtlFile))
- {
- fgets(buffer, MAX_BUFFER_SIZE, mtlFile);
-
- switch (buffer[0])
- {
- case 'n': // newmtl string Material name. Begins a new material description.
- {
- // TODO: Support multiple materials in a single .mtl
- sscanf(buffer, "newmtl %127s", mapFileName);
- }
- case 'i': // illum int Illumination model
- {
- // illum = 1 if specular disabled
- // illum = 2 if specular enabled (lambertian model)
- // ...
- }
- case 'K': // Ka, Kd, Ks, Ke
- {
- switch (buffer[1])
- {
- case 'a': // Ka float float float Ambient color (RGB)
- {
- sscanf(buffer, "Ka %f %f %f", &color.x, &color.y, &color.z);
- // TODO: Support ambient color
- //material.colAmbient.r = (unsigned char)(color.x*255);
- //material.colAmbient.g = (unsigned char)(color.y*255);
- //material.colAmbient.b = (unsigned char)(color.z*255);
- } break;
- case 'd': // Kd float float float Diffuse color (RGB)
- {
- sscanf(buffer, "Kd %f %f %f", &color.x, &color.y, &color.z);
- material.maps[MAP_DIFFUSE].color.r = (unsigned char)(color.x*255);
- material.maps[MAP_DIFFUSE].color.g = (unsigned char)(color.y*255);
- material.maps[MAP_DIFFUSE].color.b = (unsigned char)(color.z*255);
- } break;
- case 's': // Ks float float float Specular color (RGB)
- {
- sscanf(buffer, "Ks %f %f %f", &color.x, &color.y, &color.z);
- material.maps[MAP_SPECULAR].color.r = (unsigned char)(color.x*255);
- material.maps[MAP_SPECULAR].color.g = (unsigned char)(color.y*255);
- material.maps[MAP_SPECULAR].color.b = (unsigned char)(color.z*255);
- } break;
- case 'e': // Ke float float float Emmisive color (RGB)
- {
- // TODO: Support Ke?
- } break;
- default: break;
- }
- } break;
- case 'N': // Ns, Ni
- {
- if (buffer[1] == 's') // Ns int Shininess (specular exponent). Ranges from 0 to 1000.
- {
- int shininess = 0;
- sscanf(buffer, "Ns %i", &shininess);
-
- //material.params[PARAM_GLOSSINES] = (float)shininess;
- }
- else if (buffer[1] == 'i') // Ni int Refraction index.
- {
- // Not supported...
- }
- } break;
- case 'm': // map_Kd, map_Ks, map_Ka, map_Bump, map_d
- {
- switch (buffer[4])
- {
- case 'K': // Color texture maps
- {
- if (buffer[5] == 'd') // map_Kd string Diffuse color texture map.
- {
- result = sscanf(buffer, "map_Kd %127s", mapFileName);
- if (result != EOF) material.maps[MAP_DIFFUSE].texture = LoadTexture(mapFileName);
- }
- else if (buffer[5] == 's') // map_Ks string Specular color texture map.
- {
- result = sscanf(buffer, "map_Ks %127s", mapFileName);
- if (result != EOF) material.maps[MAP_SPECULAR].texture = LoadTexture(mapFileName);
- }
- else if (buffer[5] == 'a') // map_Ka string Ambient color texture map.
- {
- // Not supported...
- }
- } break;
- case 'B': // map_Bump string Bump texture map.
- {
- result = sscanf(buffer, "map_Bump %127s", mapFileName);
- if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName);
- } break;
- case 'b': // map_bump string Bump texture map.
- {
- result = sscanf(buffer, "map_bump %127s", mapFileName);
- if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName);
- } break;
- case 'd': // map_d string Opacity texture map.
- {
- // Not supported...
- } break;
- default: break;
- }
- } break;
- case 'd': // d, disp
- {
- if (buffer[1] == ' ') // d float Dissolve factor. d is inverse of Tr
- {
- float alpha = 1.0f;
- sscanf(buffer, "d %f", &alpha);
- material.maps[MAP_DIFFUSE].color.a = (unsigned char)(alpha*255);
- }
- else if (buffer[1] == 'i') // disp string Displacement map
- {
- // Not supported...
- }
- } break;
- case 'b': // bump string Bump texture map
- {
- result = sscanf(buffer, "bump %127s", mapFileName);
- if (result != EOF) material.maps[MAP_NORMAL].texture = LoadTexture(mapFileName);
- } break;
- case 'T': // Tr float Transparency Tr (alpha). Tr is inverse of d
- {
- float ialpha = 0.0f;
- sscanf(buffer, "Tr %f", &ialpha);
- material.maps[MAP_DIFFUSE].color.a = (unsigned char)((1.0f - ialpha)*255);
-
- } break;
- case 'r': // refl string Reflection texture map
- default: break;
- }
- }
-
- fclose(mtlFile);
-
- // NOTE: At this point we have all material data
- TraceLog(LOG_INFO, "[%s] Material loaded successfully", fileName);
-
- return material;
-}
-#endif
-
#if defined(SUPPORT_FILEFORMAT_GLTF)
// Load IQM mesh data
static Model LoadIQM(const char *fileName)