]> git.lizzy.rs Git - shadowclad.git/blob - src/engine/asset.c
Some debug prints for assimp structures
[shadowclad.git] / src / engine / asset.c
1 #include "asset.h"
2
3 #include <stdlib.h>
4 #include <assimp/cimport.h>
5 //#include <assimp/metadata.h>
6 #include <assimp/postprocess.h>
7 #include <assimp/scene.h>
8
9 #include "logger.h"
10 #include "tga.h"
11
12 #define IMPORT_DEBUG_ 1
13
14 static const struct aiScene* importScene(const char* path);
15 static Vector3D convertAiVector3D(struct aiVector3D vect);
16 static const char* replaceFileExtension(const struct aiString path, const char* ext);
17
18
19
20 #if IMPORT_DEBUG_
21 static void printMetadata(const struct aiMetadata* meta) {
22         if (meta) {
23                 for (size_t i = 0; i < meta->mNumProperties; ++i) {
24                         char* key = memcpy(malloc((meta->mKeys[i].length + 1) * sizeof(char)),
25                                            meta->mKeys[i].data,
26                                            meta->mKeys[i].length * sizeof(char));
27                         key[meta->mKeys[i].length] = '\0';
28                         const struct aiMetadataEntry value = meta->mValues[i];
29                         switch (value.mType) {
30                                 case AI_BOOL:
31                                         logDebug("\"%s\": (bool) %d", key, *((int*) value.mData));
32                                         break;
33                                 case AI_INT32:
34                                         logDebug("\"%s\": (int32) %d", key, *((int32_t*) value.mData));
35                                         break;
36                                 case AI_UINT64:
37                                         logDebug("\"%s\": (uint64) %llu", key, *((uint64_t*) value.mData));
38                                         break;
39                                 case AI_FLOAT:
40                                         logDebug("\"%s\": (float) %f", key, *((float*) value.mData));
41                                         break;
42                                 case AI_DOUBLE:
43                                         logDebug("\"%s\": (double) %f", key, *((double*) value.mData));
44                                         break;
45                                 case AI_AISTRING: {
46                                         struct aiString aistr = *((struct aiString*) value.mData);
47                                         char* str = memcpy(malloc((aistr.length + 1) * sizeof(char)),
48                                                            aistr.data,
49                                                            aistr.length * sizeof(char));
50                                         str[aistr.length] = '\0';
51                                         logDebug("\"%s\": (string) %s", key, str);
52                                         free(str);
53                                         break; }
54                                 case AI_AIVECTOR3D: {
55                                         struct aiVector3D vec = *((struct aiVector3D*) value.mData);
56                                         logDebug("\"%s\": (vector3d) { %f, %f, %f }", key, vec.x, vec.y, vec.z);
57                                         break; }
58                                 case AI_META_MAX:
59                                 default:
60                                         logDebug("\"%s\": (unrecognized type)", key);
61                                         break;
62                         }
63                         free(key);
64                 }
65         }
66 }
67
68 void printAiNodeMetadata(const struct aiNode* node) {
69         if (!node) {
70                 return;
71         }
72
73         struct aiString aistr = node->mName;
74         char* name = memcpy(malloc((aistr.length + 1) * sizeof(char)),
75                             aistr.data,
76                             aistr.length * sizeof(char));
77         name[aistr.length] = '\0';
78         logDebug("Metadata from node \"%s\": %p", name, node->mMetaData);
79         printMetadata(node->mMetaData);
80
81         for (size_t i = 0; i < node->mNumChildren; ++i) {
82                 printAiNodeMetadata(node->mChildren[i]);
83         }
84 }
85 #endif // IMPORT_DEBUG_
86
87 const Solid* importSolid(const char* path) {
88         const struct aiScene* scene = importScene(path);
89         if (scene == NULL) {
90                 logError("Failed to import solid from %s", path);
91                 return NULL;
92         }
93         
94 #if IMPORT_DEBUG_
95         const struct aiMetadata* meta = scene->mMetaData;
96         logDebug("Metadata from %s: %p", path, meta);
97         printMetadata(meta);
98         printAiNodeMetadata(scene->mRootNode);
99 #endif // IMPORT_DEBUG_
100         
101         const unsigned int numMeshes = scene->mNumMeshes;
102         const unsigned int numMaterials = scene->mNumMaterials;
103
104         // TODO Consider assets with some arrays empty, and prevent zero mallocs
105         
106         Solid* solid = malloc(sizeof(Solid));
107         solid->numMeshes = numMeshes;
108         solid->meshes = malloc(numMeshes * sizeof(Mesh));
109         solid->numMaterials = numMaterials;
110         solid->materials = malloc(numMaterials * sizeof(Material));
111         
112         for (unsigned int meshIndex = 0; meshIndex < numMeshes; ++meshIndex) {
113                 const struct aiMesh* aiMesh = scene->mMeshes[meshIndex];
114                 const unsigned int numVertices = aiMesh->mNumVertices;
115                 const unsigned int numFaces = aiMesh->mNumFaces;
116                 
117                 Mesh mesh = { .numVertices = numVertices,
118                               .vertices = malloc(numVertices * sizeof(Vector3D)),
119                               .normals = NULL,
120                               .textureCoords = NULL,
121                               .numFaces = numFaces,
122                               .faces = malloc(numFaces * sizeof(Face)),
123                               .materialIndex = aiMesh->mMaterialIndex };
124                 
125                 for (unsigned int vertIndex = 0; vertIndex < numVertices; ++vertIndex) {
126                         mesh.vertices[vertIndex] = convertAiVector3D(
127                                         aiMesh->mVertices[vertIndex]);
128                 }
129                 
130                 if (aiMesh->mNormals != NULL) {
131                         mesh.normals = malloc(numVertices * sizeof(Vector3D));
132                         for (unsigned int normIndex = 0; normIndex < numVertices; ++normIndex) {
133                                 mesh.normals[normIndex] = convertAiVector3D(
134                                                 aiMesh->mNormals[normIndex]);
135                         }
136                 }
137                 
138                 mesh.textureCoords = malloc(numVertices * sizeof(Vector3D));
139                 for (unsigned int texcIndex = 0; texcIndex < numVertices; ++texcIndex) {
140                         mesh.textureCoords[texcIndex] = convertAiVector3D(
141                                         aiMesh->mTextureCoords[0][texcIndex]);
142                 }
143                 
144                 for (unsigned int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
145                         const struct aiFace aiFace = aiMesh->mFaces[faceIndex];
146                         const unsigned int numIndices = aiFace.mNumIndices;
147                         
148                         Face face = { .numIndices = numIndices,
149                                       .indices = malloc(numIndices * sizeof(size_t)) };
150                         
151                         for (unsigned int i = 0; i < numIndices; ++i) {
152                                 face.indices[i] = aiFace.mIndices[i];
153                         }
154                         
155                         mesh.faces[faceIndex] = face;
156                 }
157                 
158                 solid->meshes[meshIndex] = mesh;
159         }
160         
161         GLuint* textureIds = malloc(numMaterials * sizeof(GLuint));
162         glGenTextures(numMaterials, textureIds);
163         
164         for (unsigned int matIndex = 0; matIndex < numMaterials; ++matIndex) {
165                 Material material = { .textureId = textureIds[matIndex] };
166
167 #if IMPORT_DEBUG_
168                 const struct aiMaterialProperty* prop;
169                 aiGetMaterialProperty(scene->mMaterials[matIndex],
170                                       AI_MATKEY_SHADING_MODEL,
171                                       &prop);
172                 // print mKey
173                 struct aiString aistr = prop->mKey;
174                 char* key = memcpy(malloc((aistr.length + 1) * sizeof(char)),
175                                    aistr.data,
176                                    aistr.length * sizeof(char));
177                 key[aistr.length] = '\0';
178
179                 logDebug("Material property \"%s\": Shading model: (length %u) %d",
180                          key,
181                          prop->mDataLength,
182                          *((int32_t*) prop->mData));
183 #endif // IMPORT_DEBUG_
184                 
185                 glBindTexture(GL_TEXTURE_2D, material.textureId);
186                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
187                 
188                 struct aiString originalTexturePath;
189                 if (aiGetMaterialTexture(scene->mMaterials[matIndex],
190                                          aiTextureType_DIFFUSE,
191                                          0,
192                                          &originalTexturePath,
193                                          NULL, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
194                         const char* textureFilename = replaceFileExtension(originalTexturePath, ".tga");
195                         const size_t textureFilenameLength = strlen(textureFilename);
196                         char* texturePath = malloc(strlen("assets/") + textureFilenameLength + 1);
197                         strcpy(texturePath, "assets/");
198                         strncat(texturePath, textureFilename, textureFilenameLength);
199                         
200                         TgaImage* textureImage = readTga(texturePath);
201                         if (textureImage == NULL) {
202                                 logError("Importing solid from %s: Cannot read texture file %s", path, texturePath);
203                         }
204                         else {
205                                 glTexImage2D(GL_TEXTURE_2D,
206                                              0,
207                                              textureImage->imageComponents,
208                                              textureImage->header.imageWidth,
209                                              textureImage->header.imageHeight,
210                                              0,
211                                              textureImage->imageFormat,
212                                              GL_UNSIGNED_BYTE,
213                                              textureImage->bytes);
214                                 free(textureImage->bytes);
215                                 free(textureImage);
216                         }
217                 }
218                 
219                 solid->materials[matIndex] = material;
220         }
221         glBindTexture(GL_TEXTURE_2D, 0);
222         
223         aiReleaseImport(scene);
224         return solid;
225 }
226
227 static const struct aiScene* importScene(const char* path) {
228         const struct aiScene* scene = aiImportFile(path, aiProcess_PreTransformVertices);
229         if (scene != NULL && scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) {
230                 logError("Incomplete scene imported from %s", path);
231                 aiReleaseImport(scene);
232                 scene = NULL;
233         }
234         return scene;
235 }
236
237 static Vector3D convertAiVector3D(struct aiVector3D vect) {
238         return (Vector3D) { .x = vect.x,
239                             .y = vect.y,
240                             .z = vect.z };
241 }
242
243 /**
244  * BUGS
245  * The following function will not work properly with texture
246  * file names (excluding directory part) beginning with '.'
247  */
248 static const char* replaceFileExtension(const struct aiString path, const char* ext) {
249                 size_t lengthToCopy = path.length;
250                 
251                 char* lastDotSubstr = strrchr(path.data, '.');
252                 if (lastDotSubstr != NULL) {
253                         if (strpbrk(lastDotSubstr, "\\/") == NULL) {
254                                 lengthToCopy = lastDotSubstr - path.data;
255                         }
256                 }
257                 
258                 size_t extLength = strlen(ext) + 1;
259                 char* newPath = malloc(lengthToCopy + extLength);
260                 strncpy(newPath, path.data, lengthToCopy);
261                 strncpy(newPath + lengthToCopy, ext, extLength);
262                 
263                 return newPath;
264 }