]> git.lizzy.rs Git - shadowclad.git/blob - src/engine/asset.c
6be7e2712c284f27158ae3e608a7c4164345f4b7
[shadowclad.git] / src / engine / asset.c
1 #include <stdlib.h>
2 #include <assimp/cimport.h>
3 #include <assimp/postprocess.h>
4
5 #include "asset.h"
6 #include "assimp_types.h"
7 #include "logger.h"
8 #include "tga.h"
9
10 static const AiScene* importScene(const char* path);
11 static Vector3D convertAiVector3D(AiVector3D vect);
12 static const char* replaceFileExtension(const AiString path, const char* ext);
13
14
15
16 const Solid* importSolid(const char* path) {
17         const AiScene* scene = importScene(path);
18         if (scene == NULL) {
19                 logError("Failed to import solid from %s", path);
20                 return NULL;
21         }
22         
23         const unsigned int numMeshes = scene->mNumMeshes;
24         const unsigned int numMaterials = scene->mNumMaterials;
25
26         // TODO Consider assets with some arrays empty, and prevent zero mallocs
27         
28         Solid* solid = malloc(sizeof(Solid));
29         solid->numMeshes = numMeshes;
30         solid->meshes = malloc(numMeshes * sizeof(Mesh));
31         solid->numMaterials = numMaterials;
32         solid->materials = malloc(numMaterials * sizeof(Material));
33         
34         for (unsigned int meshIndex = 0; meshIndex < numMeshes; ++meshIndex) {
35                 const AiMesh* aiMesh = scene->mMeshes[meshIndex];
36                 const unsigned int numVertices = aiMesh->mNumVertices;
37                 const unsigned int numFaces = aiMesh->mNumFaces;
38                 
39                 Mesh mesh = { .numVertices = numVertices,
40                               .vertices = malloc(numVertices * sizeof(Vector3D)),
41                               .normals = NULL,
42                               .textureCoords = NULL,
43                               .numFaces = numFaces,
44                               .faces = malloc(numFaces * sizeof(Face)),
45                               .materialIndex = aiMesh->mMaterialIndex };
46                 
47                 for (unsigned int vertIndex = 0; vertIndex < numVertices; ++vertIndex) {
48                         mesh.vertices[vertIndex] = convertAiVector3D(
49                                         aiMesh->mVertices[vertIndex]);
50                 }
51                 
52                 if (aiMesh->mNormals != NULL) {
53                         mesh.normals = malloc(numVertices * sizeof(Vector3D));
54                         for (unsigned int normIndex = 0; normIndex < numVertices; ++normIndex) {
55                                 mesh.normals[normIndex] = convertAiVector3D(
56                                                 aiMesh->mNormals[normIndex]);
57                         }
58                 }
59                 
60                 mesh.textureCoords = malloc(numVertices * sizeof(Vector3D));
61                 for (unsigned int texcIndex = 0; texcIndex < numVertices; ++texcIndex) {
62                         mesh.textureCoords[texcIndex] = convertAiVector3D(
63                                         aiMesh->mTextureCoords[0][texcIndex]);
64                 }
65                 
66                 for (unsigned int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
67                         const AiFace aiFace = aiMesh->mFaces[faceIndex];
68                         const unsigned int numIndices = aiFace.mNumIndices;
69                         
70                         Face face = { .numIndices = numIndices,
71                                       .indices = malloc(numIndices * sizeof(size_t)) };
72                         
73                         for (unsigned int i = 0; i < numIndices; ++i) {
74                                 face.indices[i] = aiFace.mIndices[i];
75                         }
76                         
77                         mesh.faces[faceIndex] = face;
78                 }
79                 
80                 solid->meshes[meshIndex] = mesh;
81         }
82         
83         GLuint* textureIds = malloc(numMaterials * sizeof(GLuint));
84         glGenTextures(numMaterials, textureIds);
85         
86         for (unsigned int matIndex = 0; matIndex < numMaterials; ++matIndex) {
87                 Material material = { .textureId = textureIds[matIndex] };
88                 
89                 glBindTexture(GL_TEXTURE_2D, material.textureId);
90                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
91                 
92                 AiString originalTexturePath;
93                 if (aiGetMaterialTexture(scene->mMaterials[matIndex],
94                                          aiTextureType_DIFFUSE,
95                                          0,
96                                          &originalTexturePath,
97                                          NULL, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
98                         const char* textureFilename = replaceFileExtension(originalTexturePath, ".tga");
99                         const size_t textureFilenameLength = strlen(textureFilename);
100                         char* texturePath = malloc(strlen("assets/") + textureFilenameLength + 1);
101                         strcpy(texturePath, "assets/");
102                         strncat(texturePath, textureFilename, textureFilenameLength);
103                         
104                         TgaImage* textureImage = readTga(texturePath);
105                         if (textureImage == NULL) {
106                                 logError("Importing solid from %s: Cannot read texture file %s", path, texturePath);
107                         }
108                         else {
109                                 glTexImage2D(GL_TEXTURE_2D,
110                                              0,
111                                              textureImage->imageComponents,
112                                              textureImage->header.imageWidth,
113                                              textureImage->header.imageHeight,
114                                              0,
115                                              textureImage->imageFormat,
116                                              GL_UNSIGNED_BYTE,
117                                              textureImage->bytes);
118                                 free(textureImage->bytes);
119                                 free(textureImage);
120                         }
121                 }
122                 
123                 solid->materials[matIndex] = material;
124         }
125         glBindTexture(GL_TEXTURE_2D, 0);
126         
127         aiReleaseImport(scene);
128         return solid;
129 }
130
131 static const AiScene* importScene(const char* path) {
132         const AiScene* scene = aiImportFile(path, aiProcess_PreTransformVertices);
133         if (scene != NULL && scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) {
134                 logError("Incomplete scene imported from %s", path);
135                 aiReleaseImport(scene);
136                 scene = NULL;
137         }
138         return scene;
139 }
140
141 static Vector3D convertAiVector3D(AiVector3D vect) {
142         return (Vector3D) { .x = vect.x,
143                             .y = vect.y,
144                             .z = vect.z };
145 }
146
147 /**
148  * BUGS
149  * The following function will not work properly with texture
150  * file names (excluding directory part) beginning with '.'
151  */
152 static const char* replaceFileExtension(const AiString path, const char* ext) {
153                 size_t lengthToCopy = path.length;
154                 
155                 char* lastDotSubstr = strrchr(path.data, '.');
156                 if (lastDotSubstr != NULL) {
157                         if (strpbrk(lastDotSubstr, "\\/") == NULL) {
158                                 lengthToCopy = lastDotSubstr - path.data;
159                         }
160                 }
161                 
162                 size_t extLength = strlen(ext) + 1;
163                 char* newPath = malloc(lengthToCopy + extLength);
164                 strncpy(newPath, path.data, lengthToCopy);
165                 strncpy(newPath + lengthToCopy, ext, extLength);
166                 
167                 return newPath;
168 }