WARNING: Redesigned model struct for multi-meshes

This is quite a big change, Model struct has been redesigned to support multiple meshes and multiple materials, most 3d fileformats contain multiple meshes and reference multiple materials.

Consequently, multiple functions have been reviewed.

LoadOBJ(), LoadIQM(), LoadGLFT() now return a Model.

Current LoadOBJ() is not valid anymore, actually, tinyobj_loader_c library is considered for replacement.
This commit is contained in:
Ray 2019-03-29 16:48:23 +01:00
parent 876c64b1e5
commit a643dc4ca0
3 changed files with 1684 additions and 73 deletions

1583
src/external/tinyobj_loader_c.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -52,6 +52,11 @@
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2
#if defined(SUPPORT_FILEFORMAT_OBJ)
#define TINYOBJ_LOADER_C_IMPLEMENTATION
#include "external/tinyobj_loader_c.h" // OBJ file format loading
#endif
#if defined(SUPPORT_FILEFORMAT_IQM) #if defined(SUPPORT_FILEFORMAT_IQM)
#define RIQM_IMPLEMENTATION #define RIQM_IMPLEMENTATION
#include "external/riqm.h" // IQM file format loading #include "external/riqm.h" // IQM file format loading
@ -86,16 +91,16 @@
// Module specific Functions Declaration // Module specific Functions Declaration
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
#if defined(SUPPORT_FILEFORMAT_OBJ) #if defined(SUPPORT_FILEFORMAT_OBJ)
static Mesh LoadOBJ(const char *fileName); // Load OBJ mesh data static Model LoadOBJ(const char *fileName); // Load OBJ mesh data
#endif #endif
#if defined(SUPPORT_FILEFORMAT_MTL) #if defined(SUPPORT_FILEFORMAT_MTL)
static Material LoadMTL(const char *fileName); // Load MTL material data static Material LoadMTL(const char *fileName); // Load MTL material data
#endif #endif
#if defined(SUPPORT_FILEFORMAT_GLTF) #if defined(SUPPORT_FILEFORMAT_GLTF)
static Mesh LoadIQM(const char *fileName); // Load IQM mesh data static Model LoadIQM(const char *fileName); // Load IQM mesh data
#endif #endif
#if defined(SUPPORT_FILEFORMAT_GLTF) #if defined(SUPPORT_FILEFORMAT_GLTF)
static Mesh LoadGLTF(const char *fileName); // Load GLTF mesh data static Model LoadGLTF(const char *fileName); // Load GLTF mesh data
#endif #endif
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
@ -618,9 +623,18 @@ Model LoadModel(const char *fileName)
{ {
Model model = { 0 }; Model model = { 0 };
model.mesh = LoadMesh(fileName); #if defined(SUPPORT_FILEFORMAT_OBJ)
model.transform = MatrixIdentity(); if (IsFileExtension(fileName, ".obj")) model = LoadOBJ(fileName);
model.material = LoadMaterialDefault(); #endif
#if defined(SUPPORT_FILEFORMAT_GLTF)
if (IsFileExtension(fileName, ".gltf")) model = LoadGLTF(fileName);
#endif
#if defined(SUPPORT_FILEFORMAT_IQM)
if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName);
#endif
if (model.meshCount == 0) TraceLog(LOG_WARNING, "[%s] No meshes can be loaded", fileName);
if (model.materialCount == 0) TraceLog(LOG_WARNING, "[%s] No materials can be loaded", fileName);
return model; return model;
} }
@ -633,9 +647,18 @@ Model LoadModelFromMesh(Mesh mesh)
{ {
Model model = { 0 }; Model model = { 0 };
model.mesh = mesh;
model.transform = MatrixIdentity(); model.transform = MatrixIdentity();
model.material = LoadMaterialDefault();
model.meshCount = 1;
model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh));
model.meshes[0] = mesh;
model.materialCount = 1;
model.materials = (Material *)malloc(model.materialCount*sizeof(Material));
model.materials[0] = LoadMaterialDefault();
model.meshMaterial = (int *)malloc(model.meshCount*sizeof(int));
model.meshMaterial[0] = 0; // First material index
return model; return model;
} }
@ -643,10 +666,11 @@ Model LoadModelFromMesh(Mesh mesh)
// Unload model from memory (RAM and/or VRAM) // Unload model from memory (RAM and/or VRAM)
void UnloadModel(Model model) void UnloadModel(Model model)
{ {
UnloadMesh(&model.mesh); for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]);
UnloadMaterial(model.material); for (int i = 0; i < model.materialCount; i++) UnloadMaterial(model.materials[i]);
free(model.meshMaterial);
TraceLog(LOG_INFO, "Unloaded model data (mesh and material) from RAM and VRAM"); TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM");
} }
// Load mesh from file // Load mesh from file
@ -655,11 +679,7 @@ Mesh LoadMesh(const char *fileName)
{ {
Mesh mesh = { 0 }; Mesh mesh = { 0 };
#if defined(SUPPORT_FILEFORMAT_OBJ) // TODO: Review this function, should still exist?
if (IsFileExtension(fileName, ".obj")) mesh = LoadOBJ(fileName);
#else
TraceLog(LOG_WARNING, "[%s] Mesh fileformat not supported, it can't be loaded", fileName);
#endif
#if defined(SUPPORT_MESH_GENERATION) #if defined(SUPPORT_MESH_GENERATION)
if (mesh.vertexCount == 0) if (mesh.vertexCount == 0)
@ -1855,9 +1875,12 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota
//Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates //Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates
model.transform = MatrixMultiply(model.transform, matTransform); model.transform = MatrixMultiply(model.transform, matTransform);
model.material.maps[MAP_DIFFUSE].color = tint; // TODO: Multiply tint color by diffuse color?
rlDrawMesh(model.mesh, model.material, model.transform); for (int i = 0; i < model.meshCount; i++)
{
model.materials[model.meshMaterial[i]].maps[MAP_DIFFUSE].color = tint;
rlDrawMesh(model.meshes[i], model.materials[model.meshMaterial[i]], model.transform);
}
} }
// Draw a model wires (with texture if set) // Draw a model wires (with texture if set)
@ -2079,41 +2102,45 @@ RayHitInfo GetCollisionRayModel(Ray ray, Model *model)
{ {
RayHitInfo result = { 0 }; RayHitInfo result = { 0 };
// If mesh doesn't have vertex data on CPU, can't test it. for (int i = 0; i < model->meshCount; i++)
if (!model->mesh.vertices) return result;
// model->mesh.triangleCount may not be set, vertexCount is more reliable
int triangleCount = model->mesh.vertexCount/3;
// Test against all triangles in mesh
for (int i = 0; i < triangleCount; i++)
{ {
Vector3 a, b, c; // Check if meshhas vertex data on CPU for testing
Vector3 *vertdata = (Vector3 *)model->mesh.vertices; if (model->meshes[i].vertices != NULL)
if (model->mesh.indices)
{ {
a = vertdata[model->mesh.indices[i*3 + 0]]; // model->mesh.triangleCount may not be set, vertexCount is more reliable
b = vertdata[model->mesh.indices[i*3 + 1]]; int triangleCount = model->meshes[i].vertexCount/3;
c = vertdata[model->mesh.indices[i*3 + 2]];
}
else
{
a = vertdata[i*3 + 0];
b = vertdata[i*3 + 1];
c = vertdata[i*3 + 2];
}
a = Vector3Transform(a, model->transform); // Test against all triangles in mesh
b = Vector3Transform(b, model->transform); for (int i = 0; i < triangleCount; i++)
c = Vector3Transform(c, model->transform); {
Vector3 a, b, c;
Vector3 *vertdata = (Vector3 *)model->meshes[i].vertices;
RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c); if (model->meshes[i].indices)
{
a = vertdata[model->meshes[i].indices[i*3 + 0]];
b = vertdata[model->meshes[i].indices[i*3 + 1]];
c = vertdata[model->meshes[i].indices[i*3 + 2]];
}
else
{
a = vertdata[i*3 + 0];
b = vertdata[i*3 + 1];
c = vertdata[i*3 + 2];
}
if (triHitInfo.hit) a = Vector3Transform(a, model->transform);
{ b = Vector3Transform(b, model->transform);
// Save the closest hit triangle c = Vector3Transform(c, model->transform);
if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo;
RayHitInfo triHitInfo = GetCollisionRayTriangle(ray, a, b, c);
if (triHitInfo.hit)
{
// Save the closest hit triangle
if ((!result.hit) || (result.distance > triHitInfo.distance)) result = triHitInfo;
}
}
} }
} }
@ -2329,10 +2356,13 @@ void MeshBinormals(Mesh *mesh)
#if defined(SUPPORT_FILEFORMAT_OBJ) #if defined(SUPPORT_FILEFORMAT_OBJ)
// Load OBJ mesh data // Load OBJ mesh data
static Mesh LoadOBJ(const char *fileName) static Model LoadOBJ(const char *fileName)
{ {
Mesh mesh = { 0 }; Model model = { 0 };
// TODO: Use tinyobj_loader_c library
/*
char dataType = 0; char dataType = 0;
char comments[200]; char comments[200];
@ -2568,11 +2598,12 @@ static Mesh LoadOBJ(const char *fileName)
free(midVertices); free(midVertices);
free(midNormals); free(midNormals);
free(midTexCoords); 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 vertex, texcoord, normal data for the model in mesh struct
TraceLog(LOG_INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName); TraceLog(LOG_INFO, "[%s] Model loaded successfully in RAM (CPU)", fileName);
return mesh; return model;
} }
#endif #endif
@ -2743,21 +2774,21 @@ static Material LoadMTL(const char *fileName)
#if defined(SUPPORT_FILEFORMAT_GLTF) #if defined(SUPPORT_FILEFORMAT_GLTF)
// Load IQM mesh data // Load IQM mesh data
static Mesh LoadIQM(const char *fileName) static Model LoadIQM(const char *fileName)
{ {
Mesh mesh = { 0 }; Model model = { 0 };
// TODO: Load IQM file // TODO: Load IQM file
return mesh; return model;
} }
#endif #endif
#if defined(SUPPORT_FILEFORMAT_GLTF) #if defined(SUPPORT_FILEFORMAT_GLTF)
// Load glTF mesh data // Load glTF mesh data
static Mesh LoadGLTF(const char *fileName) static Model LoadGLTF(const char *fileName)
{ {
Mesh mesh = { 0 }; Model model = { 0 };
// glTF file loading // glTF file loading
FILE *gltfFile = fopen(fileName, "rb"); FILE *gltfFile = fopen(fileName, "rb");
@ -2765,7 +2796,7 @@ static Mesh LoadGLTF(const char *fileName)
if (gltfFile == NULL) if (gltfFile == NULL)
{ {
TraceLog(LOG_WARNING, "[%s] glTF file could not be opened", fileName); TraceLog(LOG_WARNING, "[%s] glTF file could not be opened", fileName);
return mesh; return model;
} }
fseek(gltfFile, 0, SEEK_END); fseek(gltfFile, 0, SEEK_END);
@ -2790,15 +2821,15 @@ static Mesh LoadGLTF(const char *fileName)
printf("Version: %d\n", data.version); printf("Version: %d\n", data.version);
printf("Meshes: %lu\n", data.meshes_count); printf("Meshes: %lu\n", data.meshes_count);
// TODO: Process glTF data and map to mesh // TODO: Process glTF data and map to model
// NOTE: data.buffers[] and data.images[] should be loaded // NOTE: data.buffers[] should be loaded to model.meshes and data.images[] should be loaded to model.materials
// using buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName); // Use buffers[n].uri and images[n].uri... or use cgltf_load_buffers(&options, data, fileName);
cgltf_free(&data); cgltf_free(&data);
} }
else TraceLog(LOG_WARNING, "[%s] glTF data could not be loaded", fileName); else TraceLog(LOG_WARNING, "[%s] glTF data could not be loaded", fileName);
return mesh; return model;
} }
#endif #endif

View file

@ -339,18 +339,15 @@ typedef struct Material {
// Model type // Model type
typedef struct Model { typedef struct Model {
Mesh mesh; // Vertex data buffers (RAM and VRAM)
Matrix transform; // Local transform matrix Matrix transform; // Local transform matrix
Material material; // Shader and textures data
/*
Mesh *meshes; // Vertex data buffers (RAM and VRAM)
int meshCount;
Material *materials; // Shader and textures data int meshCount; // Number of meshes
int materialCount; Mesh *meshes; // Meshes array
int *meshMaterial; // Material assigned to every mesh int materialCount; // Number of materials
*/ Material *materials; // Materials array
int *meshMaterial; // Mesh material number
} Model; } Model;
// Ray type (useful for raycast) // Ray type (useful for raycast)
@ -1226,7 +1223,7 @@ RLAPI void DrawGizmo(Vector3 position);
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
// Model loading/unloading functions // Model loading/unloading functions
RLAPI Model LoadModel(const char *fileName); // Load model from files (mesh and material) RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials)
RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh
RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM)