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