diff options
| -rw-r--r-- | examples/models/resources/models/cube.obj | 44 | ||||
| -rw-r--r-- | projects/VS2017/examples/core_basic_window.vcxproj | 2 | ||||
| -rw-r--r-- | projects/VS2017/examples/core_basic_window_cpp.vcxproj | 2 | ||||
| -rw-r--r-- | projects/VS2017/raylib/raylib.vcxproj | 2 | ||||
| -rw-r--r-- | src/config.h | 6 | ||||
| -rw-r--r-- | src/external/tinyobj_loader_c.h | 9 | ||||
| -rw-r--r-- | src/models.c | 586 | ||||
| -rw-r--r-- | src/raylib.h | 45 | ||||
| -rw-r--r-- | src/rlgl.h | 39 |
9 files changed, 564 insertions, 171 deletions
diff --git a/examples/models/resources/models/cube.obj b/examples/models/resources/models/cube.obj index 0e9d6597..bf7e3bee 100644 --- a/examples/models/resources/models/cube.obj +++ b/examples/models/resources/models/cube.obj @@ -1,7 +1,7 @@ # reference material -mtllib myCube.mtl +#mtllib cube.mtl -# object Pau_Box +# object box # vertex (XZY) v 5.5 0 1.5 @@ -14,85 +14,55 @@ v 5.5 3 -1.5 v 8.5 3 -1.5 # normals (XYZ) -# red vn 0 -1 0 -#blue vn 0 1 0 -#top vn 0 0 1 -#yellow vn 1 0 0 -#bottom vn 0 0 -1 -#green vn -1 0 0 - # UVs (XY) -# yellow (1234) vt 0.5 0 0 vt 1 0 0 vt 1 0.5 0 vt 0.5 0.5 0 -# red (5678) vt 0.5 0.5 0 vt 1 0.5 0 vt 0.5 1 0 vt 1 1 0 -#bottom (9101112) vt 0 0.5 0 vt 1 0.5 0 vt 1 0 0 vt 0 0 0 -#top (13141516) vt 0 0.5 0 vt 1 0.5 0 vt 1 1 0 vt 0 1 0 -#green (17181920) vt 0.5 0 0 vt 0 0 0 vt 0 0.5 0 vt 0.5 0.5 0 -#blue (21222324) vt 0 0.5 0 vt 0.5 0.5 0 vt 0.5 1 0 vt 0 1 0 # merger -g Pau_Box +g box # reference material -usemtl Material_1 - -# bottom -f 1/9/1 3/10/1 4/11/1 -f 4/11/1 2/12/1 1/9/1 +#usemtl mat01 -# top +# faces +f 1/9/1 3/10/1 4/11/1 +f 4/11/1 2/12/1 1/9/1 f 5/13/2 6/14/2 8/15/2 f 8/15/2 7/16/2 5/13/2 - -# front-yellow f 1/17/6 2/18/6 6/19/6 f 6/19/6 5/20/6 1/17/6 - -# right-blue f 2/6/1 4/5/1 8/7/1 f 8/7/1 6/8/1 2/6/1 - -# back-green f 4/2/3 3/1/3 7/4/3 f 7/4/3 8/3/3 4/2/3 - -# left-red f 3/22/5 1/21/5 5/24/5 f 5/24/5 7/23/5 3/22/5 - - - - - - - diff --git a/projects/VS2017/examples/core_basic_window.vcxproj b/projects/VS2017/examples/core_basic_window.vcxproj index 48e06e44..87bef8d8 100644 --- a/projects/VS2017/examples/core_basic_window.vcxproj +++ b/projects/VS2017/examples/core_basic_window.vcxproj @@ -22,7 +22,7 @@ <ProjectGuid>{0981CA98-E4A5-4DF1-987F-A41D09131EFC}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>core_basic_window</RootNamespace> - <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion> <ProjectName>core_basic_window</ProjectName> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> diff --git a/projects/VS2017/examples/core_basic_window_cpp.vcxproj b/projects/VS2017/examples/core_basic_window_cpp.vcxproj index 39a2aee7..cfe08524 100644 --- a/projects/VS2017/examples/core_basic_window_cpp.vcxproj +++ b/projects/VS2017/examples/core_basic_window_cpp.vcxproj @@ -22,7 +22,7 @@ <ProjectGuid>{B655E850-3322-42F7-941D-6AC18FD66CA1}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>raylib_example_cpp</RootNamespace> - <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion> <ProjectName>core_basic_window_cpp</ProjectName> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> diff --git a/projects/VS2017/raylib/raylib.vcxproj b/projects/VS2017/raylib/raylib.vcxproj index 4c620053..3ff77104 100644 --- a/projects/VS2017/raylib/raylib.vcxproj +++ b/projects/VS2017/raylib/raylib.vcxproj @@ -22,7 +22,7 @@ <ProjectGuid>{E89D61AC-55DE-4482-AFD4-DF7242EBC859}</ProjectGuid> <Keyword>Win32Proj</Keyword> <RootNamespace>raylib</RootNamespace> - <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> diff --git a/src/config.h b/src/config.h index d45ff707..4174ed71 100644 --- a/src/config.h +++ b/src/config.h @@ -66,10 +66,10 @@ //------------------------------------------------------------------------------------ // Draw rectangle shapes using font texture white character instead of default white texture // Allows drawing rectangles and text with a single draw call, very useful for GUI systems! -#define SUPPORT_FONT_TEXTURE +#define SUPPORT_FONT_TEXTURE 1 // Use QUADS instead of TRIANGLES for drawing when possible // Some lines-based shapes could still use lines -#define SUPPORT_QUADS_DRAW_MODE +#define SUPPORT_QUADS_DRAW_MODE 1 //------------------------------------------------------------------------------------ // Module: textures - Configuration Flags @@ -114,6 +114,8 @@ // Selected desired model fileformats to be supported for loading #define SUPPORT_FILEFORMAT_OBJ 1 #define SUPPORT_FILEFORMAT_MTL 1 +#define SUPPORT_FILEFORMAT_IQM 1 +#define SUPPORT_FILEFORMAT_GLTF 1 // Support procedural mesh generation functions, uses external par_shapes.h library // NOTE: Some generated meshes DO NOT include generated texture coordinates #define SUPPORT_MESH_GENERATION 1 diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h index e9d015ff..ae975829 100644 --- a/src/external/tinyobj_loader_c.h +++ b/src/external/tinyobj_loader_c.h @@ -1342,12 +1342,13 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, attrib->num_normals = (unsigned int)num_vn; attrib->texcoords = (float *)TINYOBJ_MALLOC(sizeof(float) * num_vt * 2); attrib->num_texcoords = (unsigned int)num_vt; - attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC( - sizeof(tinyobj_vertex_index_t) * num_f); - attrib->num_faces = (unsigned int)num_f; + attrib->faces = (tinyobj_vertex_index_t *)TINYOBJ_MALLOC(sizeof(tinyobj_vertex_index_t) * num_f); attrib->face_num_verts = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); + + attrib->num_faces = (unsigned int)num_faces; + attrib->num_face_num_verts = (unsigned int)num_f; + attrib->material_ids = (int *)TINYOBJ_MALLOC(sizeof(int) * num_faces); - attrib->num_face_num_verts = (unsigned int)num_faces; for (i = 0; i < num_lines; i++) { if (commands[i].type == COMMAND_EMPTY) { diff --git a/src/models.c b/src/models.c index 2893fd2f..632aca2b 100644 --- a/src/models.c +++ b/src/models.c @@ -57,11 +57,6 @@ #include "external/tinyobj_loader_c.h" // OBJ/MTL file formats loading #endif -#if defined(SUPPORT_FILEFORMAT_IQM) - #define RIQM_IMPLEMENTATION - #include "external/riqm.h" // IQM file format loading -#endif - #if defined(SUPPORT_FILEFORMAT_GLTF) #define CGLTF_IMPLEMENTATION #include "external/cgltf.h" // glTF file format loading @@ -629,22 +624,30 @@ Model LoadModel(const char *fileName) #if defined(SUPPORT_FILEFORMAT_IQM) if (IsFileExtension(fileName, ".iqm")) model = LoadIQM(fileName); #endif + + // Make sure model transform is set to identity matrix! + model.transform = MatrixIdentity(); if (model.meshCount == 0) { TraceLog(LOG_WARNING, "[%s] No meshes can be loaded, default to cube mesh", fileName); model.meshCount = 1; - model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + model.meshes = (Mesh *)calloc(model.meshCount, sizeof(Mesh)); model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); } - + else + { + // Upload vertex data to GPU (static mesh) + for (int i = 0; i < model.meshCount; i++) rlLoadMesh(&model.meshes[i], false); + } + if (model.materialCount == 0) { TraceLog(LOG_WARNING, "[%s] No materials can be loaded, default to white material", fileName); model.materialCount = 1; - model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + model.materials = (Material *)calloc(model.materialCount, sizeof(Material)); model.materials[0] = LoadMaterialDefault(); model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); @@ -682,31 +685,16 @@ void UnloadModel(Model model) { for (int i = 0; i < model.meshCount; i++) UnloadMesh(&model.meshes[i]); for (int i = 0; i < model.materialCount; i++) UnloadMaterial(model.materials[i]); + + free(model.meshes); + free(model.materials); free(model.meshMaterial); - - TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); -} - -// Load mesh from file -// NOTE: Mesh data loaded in CPU and GPU -Mesh LoadMesh(const char *fileName) -{ - Mesh mesh = { 0 }; - - // TODO: Review this function, should still exist? -#if defined(SUPPORT_MESH_GENERATION) - if (mesh.vertexCount == 0) - { - TraceLog(LOG_WARNING, "Mesh could not be loaded! Let's load a cube to replace it!"); - mesh = GenMeshCube(1.0f, 1.0f, 1.0f); - } - else rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) -#else - rlLoadMesh(&mesh, false); // Upload vertex data to GPU (static mesh) -#endif + // Unload animation data + free(model.bones); + free(model.bindPose); - return mesh; + TraceLog(LOG_INFO, "Unloaded model data from RAM and VRAM"); } // Unload mesh from memory (RAM and/or VRAM) @@ -1879,7 +1867,7 @@ void UnloadMaterial(Material material) void DrawModel(Model model, Vector3 position, float scale, Color tint) { Vector3 vScale = { scale, scale, scale }; - Vector3 rotationAxis = { 0.0f, 0.0f, 0.0f }; + Vector3 rotationAxis = { 0.0f, 1.0f, 0.0f }; DrawModelEx(model, position, rotationAxis, 0.0f, vScale, tint); } @@ -1896,8 +1884,6 @@ void DrawModelEx(Model model, Vector3 position, Vector3 rotationAxis, float rota Matrix matTransform = MatrixMultiply(MatrixMultiply(matScale, matRotation), matTranslation); // Combine model transformation matrix (model.transform) with matrix generated by function parameters (matTransform) - //Matrix matModel = MatrixMultiply(model.transform, matTransform); // Transform to world-space coordinates - model.transform = MatrixMultiply(model.transform, matTransform); for (int i = 0; i < model.meshCount; i++) @@ -2126,25 +2112,25 @@ RayHitInfo GetCollisionRayModel(Ray ray, Model *model) { RayHitInfo result = { 0 }; - for (int i = 0; i < model->meshCount; i++) + for (int m = 0; m < model->meshCount; m++) { // Check if meshhas vertex data on CPU for testing - if (model->meshes[i].vertices != NULL) + if (model->meshes[m].vertices != NULL) { // model->mesh.triangleCount may not be set, vertexCount is more reliable - int triangleCount = model->meshes[i].vertexCount/3; + int triangleCount = model->meshes[m].vertexCount/3; // Test against all triangles in mesh for (int i = 0; i < triangleCount; i++) { Vector3 a, b, c; - Vector3 *vertdata = (Vector3 *)model->meshes[i].vertices; + Vector3 *vertdata = (Vector3 *)model->meshes[m].vertices; - if (model->meshes[i].indices) + if (model->meshes[m].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]]; + a = vertdata[model->meshes[m].indices[i*3 + 0]]; + b = vertdata[model->meshes[m].indices[i*3 + 1]]; + c = vertdata[model->meshes[m].indices[i*3 + 2]]; } else { @@ -2260,6 +2246,8 @@ BoundingBox MeshBoundingBox(Mesh mesh) // Get min and max vertex to construct bounds (AABB) Vector3 minVertex = { 0 }; Vector3 maxVertex = { 0 }; + + printf("Mesh vertex count: %i\n", mesh.vertexCount); if (mesh.vertices != NULL) { @@ -2274,7 +2262,7 @@ BoundingBox MeshBoundingBox(Mesh mesh) } // Create the bounding box - BoundingBox box; + BoundingBox box = { 0 }; box.min = minVertex; box.max = maxVertex; @@ -2383,97 +2371,160 @@ void MeshBinormals(Mesh *mesh) static Model LoadOBJ(const char *fileName) { Model model = { 0 }; - + tinyobj_attrib_t attrib; tinyobj_shape_t *meshes = NULL; unsigned int meshCount = 0; - + tinyobj_material_t *materials = NULL; unsigned int materialCount = 0; int dataLength = 0; char *data = NULL; - + // Load model data FILE *objFile = fopen(fileName, "rb"); if (objFile != NULL) { fseek(objFile, 0, SEEK_END); - long dataLength = ftell(objFile); // Get file size - fseek(objFile, 0, SEEK_SET); // Reset file pointer + long length = ftell(objFile); // Get file size + fseek(objFile, 0, SEEK_SET); // Reset file pointer - data = (char *)malloc(dataLength); + data = (char *)malloc(length); - fread(data, dataLength, 1, objFile); + fread(data, length, 1, objFile); + dataLength = length; fclose(objFile); } - if (data != NULL) + if (data != NULL) { 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++) - { - printf("shape[%d] name = %s\n", i, meshes[i].name); - } + + // Init model meshes array + model.meshCount = meshCount; + model.meshes = (Mesh *)malloc(model.meshCount*sizeof(Mesh)); + + // Init model materials array + model.materialCount = materialCount; + model.materials = (Material *)malloc(model.materialCount*sizeof(Material)); + model.meshMaterial = (int *)calloc(model.meshCount, sizeof(int)); /* - // 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; - + // Multiple meshes data reference + // NOTE: They are provided as a faces offset typedef struct { char *name; // group name or object name unsigned int face_offset; unsigned int length; } tinyobj_shape_t; + */ + + // Init model meshes + for (int m = 0; m < 1; m++) + { + Mesh mesh = { 0 }; + memset(&mesh, 0, sizeof(Mesh)); + mesh.vertexCount = attrib.num_faces*3; + mesh.triangleCount = attrib.num_faces; + 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)); + + int vCount = 0; + int vtCount = 0; + int vnCount = 0; + + for (int f = 0; f < attrib.num_faces; f++) + { + // Get indices for the face + tinyobj_vertex_index_t idx0 = attrib.faces[3*f + 0]; + tinyobj_vertex_index_t idx1 = attrib.faces[3*f + 1]; + tinyobj_vertex_index_t idx2 = attrib.faces[3*f + 2]; + + // TraceLog(LOG_DEBUG, "Face %i index: v %i/%i/%i . vt %i/%i/%i . vn %i/%i/%i\n", f, idx0.v_idx, idx1.v_idx, idx2.v_idx, idx0.vt_idx, idx1.vt_idx, idx2.vt_idx, idx0.vn_idx, idx1.vn_idx, idx2.vn_idx); + + // Fill vertices buffer (float) using vertex index of the face + for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount +=3; + for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount +=3; + for (int v = 0; v < 3; v++) { mesh.vertices[vCount + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount +=3; + + // Fill texcoords buffer (float) using vertex index of the face + // NOTE: Y-coordinate must be flipped upside-down + mesh.texcoords[vtCount + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; + mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount += 2; + mesh.texcoords[vtCount + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; + mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount += 2; + mesh.texcoords[vtCount + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; + mesh.texcoords[vtCount + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount += 2; + + // Fill normals buffer (float) using vertex index of the face + for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount +=3; + for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount +=3; + for (int v = 0; v < 3; v++) { mesh.normals[vnCount + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount +=3; + } - typedef struct { int v_idx, vt_idx, vn_idx; } tinyobj_vertex_index_t; + model.meshes[m] = mesh; // Assign mesh data to model - 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; - */ + // Assign mesh material for current mesh + model.meshMaterial[m] = attrib.material_ids[m]; + } + + // Init model materials + for (int m = 0; m < materialCount; m++) + { + // Init material to default + // NOTE: Uses default shader, only MAP_DIFFUSE supported + model.materials[m] = LoadMaterialDefault(); + + /* + 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; + */ + + model.materials[m].maps[MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd + model.materials[m].maps[MAP_DIFFUSE].color = (Color){ (float)(materials[m].diffuse[0]*255.0f), (float)(materials[m].diffuse[1]*255.0f), (float)(materials[m].diffuse[2]*255.0f), 255 }; //float diffuse[3]; + model.materials[m].maps[MAP_DIFFUSE].value = 0.0f; + + model.materials[m].maps[MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks + model.materials[m].maps[MAP_SPECULAR].color = (Color){ (float)(materials[m].specular[0]*255.0f), (float)(materials[m].specular[1]*255.0f), (float)(materials[m].specular[2]*255.0f), 255 }; //float specular[3]; + model.materials[m].maps[MAP_SPECULAR].value = 0.0f; + + model.materials[m].maps[MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump + model.materials[m].maps[MAP_NORMAL].color = WHITE; + model.materials[m].maps[MAP_NORMAL].value = materials[m].shininess; + + model.materials[m].maps[MAP_EMISSION].color = (Color){ (float)(materials[m].emission[0]*255.0f), (float)(materials[m].emission[1]*255.0f), (float)(materials[m].emission[2]*255.0f), 255 }; //float emission[3]; + + model.materials[m].maps[MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp + } tinyobj_attrib_free(&attrib); tinyobj_shapes_free(meshes, meshCount); @@ -2487,13 +2538,336 @@ static Model LoadOBJ(const char *fileName) } #endif -#if defined(SUPPORT_FILEFORMAT_GLTF) +#if defined(SUPPORT_FILEFORMAT_IQM) // Load IQM mesh data static Model LoadIQM(const char *fileName) { + #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number + #define IQM_VERSION 2 // only IQM version 2 supported + + #define BONE_NAME_LENGTH 32 // BoneInfo name string length + #define MESH_NAME_LENGTH 32 // Mesh name string length + + // IQM file structs + //----------------------------------------------------------------------------------- + typedef struct IQMHeader { + char magic[16]; + unsigned int version; + unsigned int filesize; + unsigned int flags; + unsigned int num_text, ofs_text; + unsigned int num_meshes, ofs_meshes; + unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; + unsigned int num_triangles, ofs_triangles, ofs_adjacency; + unsigned int num_joints, ofs_joints; + unsigned int num_poses, ofs_poses; + unsigned int num_anims, ofs_anims; + unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; + unsigned int num_comment, ofs_comment; + unsigned int num_extensions, ofs_extensions; + } IQMHeader; + + typedef struct IQMMesh { + unsigned int name; + unsigned int material; + unsigned int first_vertex, num_vertexes; + unsigned int first_triangle, num_triangles; + } IQMMesh; + + typedef struct IQMTriangle { + unsigned int vertex[3]; + } IQMTriangle; + + // NOTE: Adjacency unused by default + typedef struct IQMAdjacency { + unsigned int triangle[3]; + } IQMAdjacency; + + typedef struct IQMJoint { + unsigned int name; + int parent; + float translate[3], rotate[4], scale[3]; + } IQMJoint; + + typedef struct IQMPose { + int parent; + unsigned int mask; + float channeloffset[10]; + float channelscale[10]; + } IQMPose; + + typedef struct IQMAnim { + unsigned int name; + unsigned int first_frame, num_frames; + float framerate; + unsigned int flags; + } IQMAnim; + + typedef struct IQMVertexArray { + unsigned int type; + unsigned int flags; + unsigned int format; + unsigned int size; + unsigned int offset; + } IQMVertexArray; + + // NOTE: Bounds unused by default + typedef struct IQMBounds { + float bbmin[3], bbmax[3]; + float xyradius, radius; + } IQMBounds; + //----------------------------------------------------------------------------------- + + // IQM vertex data types + typedef enum { + IQM_POSITION = 0, + IQM_TEXCOORD = 1, + IQM_NORMAL = 2, + IQM_TANGENT = 3, // NOTE: Tangents unused by default + IQM_BLENDINDEXES = 4, + IQM_BLENDWEIGHTS = 5, + IQM_COLOR = 6, // NOTE: Vertex colors unused by default + IQM_CUSTOM = 0x10 // NOTE: Custom vertex values unused by default + } IQMVertexType; + Model model = { 0 }; - // TODO: Load IQM file + FILE *iqmFile; + IQMHeader iqm; + + IQMMesh *imesh; + IQMTriangle *tri; + IQMVertexArray *va; + IQMJoint *ijoint; + + float *vertex = NULL; + float *normal = NULL; + float *text = NULL; + char *blendi = NULL; + unsigned char *blendw = NULL; + + iqmFile = fopen(fileName, "rb"); + + if (iqmFile == NULL) + { + TraceLog(LOG_WARNING, "[%s] IQM file could not be opened", fileName); + return model; + } + + fread(&iqm,sizeof(IQMHeader), 1, iqmFile); // Read IQM header + + if (strncmp(iqm.magic, IQM_MAGIC, sizeof(IQM_MAGIC))) + { + TraceLog(LOG_WARNING, "[%s] IQM file does not seem to be valid", fileName); + fclose(iqmFile); + return model; + } + + if (iqm.version != IQM_VERSION) + { + TraceLog(LOG_WARNING, "[%s] IQM file version is not supported (%i).", fileName, iqm.version); + fclose(iqmFile); + return model; + } + + // Meshes data processing + imesh = malloc(sizeof(IQMMesh)*iqm.num_meshes); + fseek(iqmFile, iqm.ofs_meshes, SEEK_SET); + fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile); + + model.meshCount = iqm.num_meshes; + model.meshes = malloc(sizeof(Mesh)*iqm.num_meshes); + + char name[MESH_NAME_LENGTH]; + + for (int i = 0; i < iqm.num_meshes; i++) + { + fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET); + fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used... + model.meshes[i].vertexCount = imesh[i].num_vertexes; + + model.meshes[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default vertex positions + model.meshes[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default vertex normals + model.meshes[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2); // Default vertex texcoords + + model.meshes[i].boneIds = malloc(sizeof(int)*imesh[i].num_vertexes*4); // Up-to 4 bones supported! + model.meshes[i].boneWeights = malloc(sizeof(float)*imesh[i].num_vertexes*4); // Up-to 4 bones supported! + + model.meshes[i].triangleCount = imesh[i].num_triangles; + model.meshes[i].indices = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3); + + // Animated verted data, what we actually process for rendering + // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) + model.meshes[i].animVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); + model.meshes[i].animNormals = malloc(sizeof(float)*imesh[i].num_vertexes*3); + } + + // Triangles data processing + tri = malloc(sizeof(IQMTriangle)*iqm.num_triangles); + fseek(iqmFile, iqm.ofs_triangles, SEEK_SET); + fread(tri, sizeof(IQMTriangle)*iqm.num_triangles, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int tcounter = 0; + + for (int i = imesh[m].first_triangle; i < imesh[m].first_triangle+imesh[m].num_triangles; i++) + { + // IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around + model.meshes[m].indices[tcounter+2] = tri[i].vertex[0] - imesh[m].first_vertex; + model.meshes[m].indices[tcounter+1] = tri[i].vertex[1] - imesh[m].first_vertex; + model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex; + tcounter += 3; + } + } + + // Vertex arrays data processing + va = malloc(sizeof(IQMVertexArray)*iqm.num_vertexarrays); + fseek(iqmFile, iqm.ofs_vertexarrays, SEEK_SET); + fread(va, sizeof(IQMVertexArray)*iqm.num_vertexarrays, 1, iqmFile); + + for (int i = 0; i < iqm.num_vertexarrays; i++) + { + switch (va[i].type) + { + case IQM_POSITION: + { + vertex = malloc(sizeof(float)*iqm.num_vertexes*3); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(vertex, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int vCounter = 0; + for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) + { + model.meshes[m].vertices[vCounter] = vertex[i]; + model.meshes[m].animVertices[vCounter] = vertex[i]; + vCounter++; + } + } + } break; + case IQM_NORMAL: + { + normal = malloc(sizeof(float)*iqm.num_vertexes*3); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(normal, sizeof(float)*iqm.num_vertexes*3, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int vCounter = 0; + for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++) + { + model.meshes[m].normals[vCounter] = normal[i]; + model.meshes[m].animNormals[vCounter] = normal[i]; + vCounter++; + } + } + } break; + case IQM_TEXCOORD: + { + text = malloc(sizeof(float)*iqm.num_vertexes*2); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(text, sizeof(float)*iqm.num_vertexes*2, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int vCounter = 0; + for (int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++) + { + model.meshes[m].texcoords[vCounter] = text[i]; + vCounter++; + } + } + } break; + case IQM_BLENDINDEXES: + { + blendi = malloc(sizeof(char)*iqm.num_vertexes*4); + fseek(iqmFile, va[i].offset, SEEK_SET); + fread(blendi, sizeof(char)*iqm.num_vertexes*4, 1, iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int boneCounter = 0; + for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) + { + model.meshes[m].boneIds[boneCounter] = blendi[i]; + boneCounter++; + } + } + } break; + case IQM_BLENDWEIGHTS: + { + blendw = malloc(sizeof(unsigned char)*iqm.num_vertexes*4); + fseek(iqmFile,va[i].offset,SEEK_SET); + fread(blendw,sizeof(unsigned char)*iqm.num_vertexes*4,1,iqmFile); + + for (int m = 0; m < iqm.num_meshes; m++) + { + int boneCounter = 0; + for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++) + { + model.meshes[m].boneWeights[boneCounter] = blendw[i]/255.0f; + boneCounter++; + } + } + } break; + } + } + + // Bones (joints) data processing + ijoint = malloc(sizeof(IQMJoint)*iqm.num_joints); + fseek(iqmFile, iqm.ofs_joints, SEEK_SET); + fread(ijoint, sizeof(IQMJoint)*iqm.num_joints, 1, iqmFile); + + model.boneCount = iqm.num_joints; + model.bones = malloc(sizeof(BoneInfo)*iqm.num_joints); + model.bindPose = malloc(sizeof(Transform)*iqm.num_joints); + + for (int i = 0; i < iqm.num_joints; i++) + { + // Bones + model.bones[i].parent = ijoint[i].parent; + fseek(iqmFile, iqm.ofs_text + ijoint[i].name, SEEK_SET); + fread(model.bones[i].name,sizeof(char)*BONE_NAME_LENGTH, 1, iqmFile); + + // Bind pose (base pose) + model.bindPose[i].translation.x = ijoint[i].translate[0]; + model.bindPose[i].translation.y = ijoint[i].translate[1]; + model.bindPose[i].translation.z = ijoint[i].translate[2]; + + model.bindPose[i].rotation.x = ijoint[i].rotate[0]; + model.bindPose[i].rotation.y = ijoint[i].rotate[1]; + model.bindPose[i].rotation.z = ijoint[i].rotate[2]; + model.bindPose[i].rotation.w = ijoint[i].rotate[3]; + + model.bindPose[i].scale.x = ijoint[i].scale[0]; + model.bindPose[i].scale.y = ijoint[i].scale[1]; + model.bindPose[i].scale.z = ijoint[i].scale[2]; + } + + // Build bind pose from parent joints + for (int i = 0; i < model.boneCount; i++) + { + if (model.bones[i].parent >= 0) + { + model.bindPose[i].rotation = QuaternionMultiply(model.bindPose[model.bones[i].parent].rotation, model.bindPose[i].rotation); + model.bindPose[i].translation = Vector3RotateByQuaternion(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].rotation); + model.bindPose[i].translation = Vector3Add(model.bindPose[i].translation, model.bindPose[model.bones[i].parent].translation); + model.bindPose[i].scale = Vector3MultiplyV(model.bindPose[i].scale, model.bindPose[model.bones[i].parent].scale); + } + } + + fclose(iqmFile); + free(imesh); + free(tri); + free(va); + free(vertex); + free(normal); + free(text); + free(blendi); + free(blendw); + free(ijoint); return model; } @@ -2525,23 +2899,23 @@ static Model LoadGLTF(const char *fileName) // glTF data loading cgltf_options options = {0}; - cgltf_data data; + cgltf_data *data; cgltf_result result = cgltf_parse(&options, buffer, size, &data); free(buffer); if (result == cgltf_result_success) { - printf("Type: %u\n", data.file_type); - printf("Version: %d\n", data.version); - printf("Meshes: %lu\n", data.meshes_count); + // printf("Type: %u\n", data.file_type); + // printf("Version: %d\n", data.version); + // printf("Meshes: %lu\n", data.meshes_count); // TODO: Process glTF data and map to model // NOTE: data.buffers[] should be loaded to model.meshes and data.images[] should be loaded to model.materials // 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); diff --git a/src/raylib.h b/src/raylib.h index 1ad719bd..93965038 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -307,10 +307,10 @@ typedef struct Mesh { unsigned short *indices;// Vertex indices (in case vertex data comes indexed) // Animation vertex data - float *baseVertices; // Vertex base position (required to apply bones transformations) - float *baseNormals; // Vertex base normals (required to apply bones transformations) - float *weightBias; // Vertex weight bias - int *weightId; // Vertex weight id + float *animVertices; // Animated vertex positions (after bones transformations) + float *animNormals; // Animated normals (after bones transformations) + int *boneIds; // Vertex bone ids, up to 4 bones influence by vertex (skinning) + float *boneWeights; // Vertex bone weight, up to 4 bones influence by vertex (skinning) // OpenGL identifiers unsigned int vaoId; // OpenGL Vertex Array Object id @@ -337,6 +337,19 @@ typedef struct Material { float *params; // Material generic parameters (if required) } Material; +// Transformation properties +typedef struct Transform { + Vector3 translation; // Translation + Quaternion rotation; // Rotation + Vector3 scale; // Scale +} Transform; + +// Bone information +typedef struct BoneInfo { + char name[32]; // Bone name + int parent; // Bone parent +} BoneInfo; + // Model type typedef struct Model { Matrix transform; // Local transform matrix @@ -346,10 +359,23 @@ typedef struct Model { int materialCount; // Number of materials Material *materials; // Materials array - int *meshMaterial; // Mesh material number + + // Animation data + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + Transform *bindPose; // Bones base transformation (pose) } Model; +// Model animation +typedef struct ModelAnimation { + int boneCount; // Number of bones + BoneInfo *bones; // Bones information (skeleton) + + int frameCount; // Number of animation frames + Transform **framePoses; // Poses array by frame +} ModelAnimation; + // Ray type (useful for raycast) typedef struct Ray { Vector3 position; // Ray position (origin) @@ -1230,17 +1256,16 @@ RLAPI void DrawGizmo(Vector3 position); // Model loading/unloading functions RLAPI Model LoadModel(const char *fileName); // Load model from files (meshes and materials) RLAPI Model LoadModelFromMesh(Mesh mesh); // Load model from generated mesh +//RLAPI void LoadModelAnimations(const char fileName, ModelAnimation *anims, int *animsCount); // Load model animations from file +//RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose RLAPI void UnloadModel(Model model); // Unload model from memory (RAM and/or VRAM) -// Mesh loading/unloading functions -RLAPI Mesh LoadMesh(const char *fileName); // Load mesh from file -RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) -RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file - // Mesh manipulation functions RLAPI BoundingBox MeshBoundingBox(Mesh mesh); // Compute mesh bounding box limits RLAPI void MeshTangents(Mesh *mesh); // Compute mesh tangents RLAPI void MeshBinormals(Mesh *mesh); // Compute mesh binormals +RLAPI void UnloadMesh(Mesh *mesh); // Unload mesh from memory (RAM and/or VRAM) +RLAPI void ExportMesh(Mesh mesh, const char *fileName); // Export mesh data to file // Mesh generation functions RLAPI Mesh GenMeshPoly(int sides, float radius); // Generate polygonal mesh @@ -205,8 +205,8 @@ typedef unsigned char byte; unsigned short *indices;// vertex indices (in case vertex data comes indexed) // Animation vertex data - float *baseVertices; // Vertex base position (required to apply bones transformations) - float *baseNormals; // Vertex base normals (required to apply bones transformations) + float *animVertices; // Animated vertex positions (after bones transformations) + float *animNormals; // Animated normals (after bones transformations) float *weightBias; // Vertex weight bias int *weightId; // Vertex weight id @@ -455,6 +455,7 @@ void rlDeleteVertexArrays(unsigned int id); // Unload vertex data (V void rlDeleteBuffers(unsigned int id); // Unload vertex data (VBO) from GPU memory void rlClearColor(byte r, byte g, byte b, byte a); // Clear color buffer with color void rlClearScreenBuffers(void); // Clear used screen buffers (color and depth) +void rlUpdateBuffer(int bufferId, void *data, int dataSize); // Update GPU buffer with new data //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality @@ -1501,6 +1502,13 @@ void rlClearScreenBuffers(void) //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Stencil buffer not used... } +// Update GPU buffer with new data +void rlUpdateBuffer(int bufferId, void *data, int dataSize) +{ + glBindBuffer(GL_ARRAY_BUFFER, bufferId); + glBufferSubData(GL_ARRAY_BUFFER, 0, dataSize, data); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - rlgl Functions //---------------------------------------------------------------------------------- @@ -2036,6 +2044,8 @@ unsigned int rlLoadTexture(void *data, int width, int height, int format, int mi unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderBuffer) { unsigned int id = 0; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) unsigned int glInternalFormat = GL_DEPTH_COMPONENT16; if ((bits != 16) && (bits != 24) && (bits != 32)) bits = 16; @@ -2081,6 +2091,7 @@ unsigned int rlLoadTextureDepth(int width, int height, int bits, bool useRenderB glBindRenderbuffer(GL_RENDERBUFFER, 0); } +#endif return id; } @@ -2092,7 +2103,8 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format) { unsigned int cubemapId = 0; unsigned int dataSize = GetPixelDataSize(size, size, format); - + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glGenTextures(1, &cubemapId); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapId); @@ -2137,6 +2149,7 @@ unsigned int rlLoadTextureCubemap(void *data, int size, int format) #endif glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +#endif return cubemapId; } @@ -2221,9 +2234,9 @@ RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depth { RenderTexture2D target = { 0 }; +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) if (useDepthTexture && texDepthSupported) target.depthTexture = true; -#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Create the framebuffer object glGenFramebuffers(1, &target.id); glBindFramebuffer(GL_FRAMEBUFFER, target.id); @@ -2274,6 +2287,7 @@ RenderTexture2D rlLoadRenderTexture(int width, int height, int format, int depth // NOTE: Attach type: 0-Color, 1-Depth renderbuffer, 2-Depth texture void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachType) { +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindFramebuffer(GL_FRAMEBUFFER, target.id); if (attachType == 0) glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0); @@ -2284,11 +2298,15 @@ void rlRenderTextureAttach(RenderTexture2D target, unsigned int id, int attachTy } glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif } // Verify render texture is complete bool rlRenderTextureComplete(RenderTexture target) { + bool result = false; + +#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) glBindFramebuffer(GL_FRAMEBUFFER, target.id); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -2309,7 +2327,10 @@ bool rlRenderTextureComplete(RenderTexture target) glBindFramebuffer(GL_FRAMEBUFFER, 0); - return (status == GL_FRAMEBUFFER_COMPLETE); + result = (status == GL_FRAMEBUFFER_COMPLETE); +#endif + + return result; } // Generate mipmap data for selected texture @@ -2775,10 +2796,10 @@ void rlUnloadMesh(Mesh *mesh) free(mesh->texcoords2); free(mesh->indices); - free(mesh->baseVertices); - free(mesh->baseNormals); - free(mesh->weightBias); - free(mesh->weightId); + free(mesh->animVertices); + free(mesh->animNormals); + free(mesh->boneWeights); + free(mesh->boneIds); rlDeleteBuffers(mesh->vboId[0]); // vertex rlDeleteBuffers(mesh->vboId[1]); // texcoords |
