2 * Copyright 2019-2020 Iwo 'Outfrost' Bujkiewicz
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
13 #include <assimp/cimport.h>
14 //#include <assimp/metadata.h>
15 #include <assimp/postprocess.h>
16 #include <assimp/scene.h>
22 #define IMPORT_DEBUG_ 0
24 static const float smoothingThresholdAngle = TAU / 14.0f;
26 static const struct aiScene* importScene(const char* path);
27 static Vector triangleNormal(Vector v1, Vector v2, Vector v3);
28 static Vector convertAiVector3D(struct aiVector3D vect);
29 static const char* replaceFileExtension(const struct aiString path, const char* ext);
34 static void printMetadata(const struct aiMetadata* meta) {
36 for (size_t i = 0; i < meta->mNumProperties; ++i) {
37 String key = stringFromAiString(meta->mKeys[i]);
38 const struct aiMetadataEntry value = meta->mValues[i];
39 switch (value.mType) {
41 logDebug("\"%s\": (bool) %d", key.cstr, *((int*) value.mData));
44 logDebug("\"%s\": (int32) %d", key.cstr, *((int32_t*) value.mData));
47 logDebug("\"%s\": (uint64) %llu", key.cstr, *((uint64_t*) value.mData));
50 logDebug("\"%s\": (float) %f", key.cstr, *((float*) value.mData));
53 logDebug("\"%s\": (double) %f", key.cstr, *((double*) value.mData));
56 String str = stringFromAiString(*((struct aiString*) value.mData));
57 logDebug("\"%s\": (string) %s", key.cstr, str.cstr);
61 struct aiVector3D vec = *((struct aiVector3D*) value.mData);
62 logDebug("\"%s\": (vector3d) { %f, %f, %f }", key.cstr, vec.x, vec.y, vec.z);
66 logDebug("\"%s\": (unrecognized type)", key.cstr);
74 void printAiNodeMetadata(const struct aiNode* node) {
79 String name = stringFromAiString(node->mName);
80 logDebug("Metadata from node \"%s\": %p", name.cstr, node->mMetaData);
81 printMetadata(node->mMetaData);
83 for (size_t i = 0; i < node->mNumChildren; ++i) {
84 printAiNodeMetadata(node->mChildren[i]);
89 #endif // IMPORT_DEBUG_
91 const Solid* importSolid(const char* path) {
92 const struct aiScene* scene = importScene(path);
94 logError("Failed to import solid from %s", path);
99 const struct aiMetadata* meta = scene->mMetaData;
100 logDebug("Metadata from %s: %p", path, meta);
102 printAiNodeMetadata(scene->mRootNode);
103 #endif // IMPORT_DEBUG_
105 const unsigned int numMeshes = scene->mNumMeshes;
106 const unsigned int numMaterials = scene->mNumMaterials;
108 // TODO Consider assets with some arrays empty, and prevent zero mallocs
110 Solid* solid = malloc(sizeof(Solid));
111 solid->numMeshes = numMeshes;
112 solid->meshes = malloc(numMeshes * sizeof(Mesh));
113 solid->numMaterials = numMaterials;
114 solid->materials = malloc(numMaterials * sizeof(Material));
116 for (unsigned int meshIndex = 0; meshIndex < numMeshes; ++meshIndex) {
117 const struct aiMesh* aiMesh = scene->mMeshes[meshIndex];
118 const unsigned int numVertices = aiMesh->mNumVertices;
119 const unsigned int numFaces = aiMesh->mNumFaces;
121 Mesh mesh = { .numVertices = numVertices,
122 .vertices = malloc(numVertices * sizeof(Vector)),
124 .textureCoords = NULL,
125 .numFaces = numFaces,
126 .faces = malloc(numFaces * sizeof(Face)),
127 .materialIndex = aiMesh->mMaterialIndex };
129 for (unsigned int vertIndex = 0; vertIndex < numVertices; ++vertIndex) {
130 mesh.vertices[vertIndex] = convertAiVector3D(
131 aiMesh->mVertices[vertIndex]);
134 if (aiMesh->mNormals != NULL) {
135 mesh.normals = malloc(numVertices * sizeof(Vector));
136 for (unsigned int normIndex = 0; normIndex < numVertices; ++normIndex) {
137 mesh.normals[normIndex] = convertAiVector3D(
138 aiMesh->mNormals[normIndex]);
142 mesh.textureCoords = malloc(numVertices * sizeof(Vector));
143 for (unsigned int texcIndex = 0; texcIndex < numVertices; ++texcIndex) {
144 mesh.textureCoords[texcIndex] = convertAiVector3D(
145 aiMesh->mTextureCoords[0][texcIndex]);
148 for (unsigned int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
149 const struct aiFace aiFace = aiMesh->mFaces[faceIndex];
150 const unsigned int numIndices = aiFace.mNumIndices;
152 Face face = { .numIndices = numIndices,
153 .indices = malloc(numIndices * sizeof(size_t)),
154 .normals = malloc(numIndices * sizeof(Vector)) };
156 for (unsigned int i = 0; i < numIndices; ++i) {
157 face.indices[i] = aiFace.mIndices[i];
160 if (numIndices == 3) {
161 Vector normal = triangleNormal(mesh.vertices[face.indices[0]],
162 mesh.vertices[face.indices[1]],
163 mesh.vertices[face.indices[2]]);
164 for (size_t i = 0; i < numIndices; ++i) {
165 face.normals[i] = normal;
170 for (size_t i = 0; i < numIndices; ++i) {
171 face.normals[i] = mesh.normals[face.indices[i]];
180 mesh.faces[faceIndex] = face;
183 float smoothingThreshold = cosf(smoothingThresholdAngle);
184 Face* smoothedFaces = malloc(mesh.numFaces * sizeof(Face));
185 for (size_t faceIndex = 0; faceIndex < mesh.numFaces; ++faceIndex) {
186 Face face = mesh.faces[faceIndex];
189 face.normals = memcpy(malloc(face.numIndices * sizeof(Vector)),
191 face.numIndices * sizeof(Vector));
193 for (size_t indexIndex = 0; indexIndex < face.numIndices; ++indexIndex) {
194 Vector smoothedNormal = face.normals[indexIndex];
196 for (size_t i = 0; i < mesh.numFaces; ++i) {
197 if (i == faceIndex || !mesh.faces[i].normals) {
201 for (size_t k = 0; k < mesh.faces[i].numIndices; ++k) {
202 if (mesh.faces[i].indices[k] == face.indices[indexIndex]
203 && dotProduct(face.normals[indexIndex],
204 mesh.faces[i].normals[k]) >= smoothingThreshold) {
205 smoothedNormal = addVectors(smoothedNormal, mesh.faces[i].normals[k]);
210 face.normals[indexIndex] = normalized(smoothedNormal);
213 smoothedFaces[faceIndex] = face;
215 // TODO Actually clean up the stuff inside
217 mesh.faces = smoothedFaces;
219 solid->meshes[meshIndex] = mesh;
222 GLuint* textureIds = malloc(numMaterials * sizeof(GLuint));
223 glGenTextures(numMaterials, textureIds);
225 for (unsigned int matIndex = 0; matIndex < numMaterials; ++matIndex) {
226 Material material = { .textureId = textureIds[matIndex] };
229 const struct aiMaterialProperty* prop;
230 aiGetMaterialProperty(scene->mMaterials[matIndex],
231 AI_MATKEY_SHADING_MODEL,
234 String key = stringFromAiString(prop->mKey);
236 logDebug("Material property \"%s\": Shading model: (length %u) %d",
239 *((int32_t*) prop->mData));
242 #endif // IMPORT_DEBUG_
244 glBindTexture(GL_TEXTURE_2D, material.textureId);
245 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
247 struct aiString originalTexturePath;
248 if (aiGetMaterialTexture(scene->mMaterials[matIndex],
249 aiTextureType_DIFFUSE,
251 &originalTexturePath,
252 NULL, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
253 const char* textureFilename = replaceFileExtension(originalTexturePath, ".tga");
254 const size_t textureFilenameLength = strlen(textureFilename);
255 char* texturePath = malloc(strlen("assets/") + textureFilenameLength + 1);
256 strcpy(texturePath, "assets/");
257 strncat(texturePath, textureFilename, textureFilenameLength);
259 TgaImage* textureImage = readTga(texturePath);
260 if (textureImage == NULL) {
261 logError("Importing solid from %s: Cannot read texture file %s", path, texturePath);
264 glTexImage2D(GL_TEXTURE_2D,
266 textureImage->imageComponents,
267 textureImage->header.imageWidth,
268 textureImage->header.imageHeight,
270 textureImage->imageFormat,
272 textureImage->bytes);
273 free(textureImage->bytes);
278 solid->materials[matIndex] = material;
280 glBindTexture(GL_TEXTURE_2D, 0);
282 aiReleaseImport(scene);
286 static const struct aiScene* importScene(const char* path) {
287 const struct aiScene* scene = aiImportFile(path, aiProcess_JoinIdenticalVertices
288 | aiProcess_PreTransformVertices
289 | aiProcess_ValidateDataStructure);
290 if (scene != NULL && scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) {
291 logError("Incomplete scene imported from %s", path);
292 aiReleaseImport(scene);
298 static Vector triangleNormal(Vector v1, Vector v2, Vector v3) {
299 return normalized(crossProduct(subtractVectors(v2, v1), subtractVectors(v3, v1)));
302 static Vector convertAiVector3D(struct aiVector3D vect) {
303 return (Vector) { .x = vect.x,
310 * The following function will not work properly with texture
311 * file names (excluding directory part) beginning with '.'
313 static const char* replaceFileExtension(const struct aiString path, const char* ext) {
314 size_t lengthToCopy = path.length;
316 char* lastDotSubstr = strrchr(path.data, '.');
317 if (lastDotSubstr != NULL) {
318 if (strpbrk(lastDotSubstr, "\\/") == NULL) {
319 lengthToCopy = lastDotSubstr - path.data;
323 size_t extLength = strlen(ext) + 1;
324 char* newPath = malloc(lengthToCopy + extLength);
325 strncpy(newPath, path.data, lengthToCopy);
326 strncpy(newPath + lengthToCopy, ext, extLength);