4 #include <assimp/cimport.h>
5 //#include <assimp/metadata.h>
6 #include <assimp/postprocess.h>
7 #include <assimp/scene.h>
13 #define IMPORT_DEBUG_ 1
15 static const struct aiScene* importScene(const char* path);
16 static Vector3D triangleNormal(Vector3D v1, Vector3D v2, Vector3D v3);
17 static Vector3D convertAiVector3D(struct aiVector3D vect);
18 static const char* replaceFileExtension(const struct aiString path, const char* ext);
23 static void printMetadata(const struct aiMetadata* meta) {
25 for (size_t i = 0; i < meta->mNumProperties; ++i) {
26 String key = stringFromAiString(meta->mKeys[i]);
27 const struct aiMetadataEntry value = meta->mValues[i];
28 switch (value.mType) {
30 logDebug("\"%s\": (bool) %d", key.cstr, *((int*) value.mData));
33 logDebug("\"%s\": (int32) %d", key.cstr, *((int32_t*) value.mData));
36 logDebug("\"%s\": (uint64) %llu", key.cstr, *((uint64_t*) value.mData));
39 logDebug("\"%s\": (float) %f", key.cstr, *((float*) value.mData));
42 logDebug("\"%s\": (double) %f", key.cstr, *((double*) value.mData));
45 String str = stringFromAiString(*((struct aiString*) value.mData));
46 logDebug("\"%s\": (string) %s", key.cstr, str.cstr);
50 struct aiVector3D vec = *((struct aiVector3D*) value.mData);
51 logDebug("\"%s\": (vector3d) { %f, %f, %f }", key.cstr, vec.x, vec.y, vec.z);
55 logDebug("\"%s\": (unrecognized type)", key.cstr);
63 void printAiNodeMetadata(const struct aiNode* node) {
68 String name = stringFromAiString(node->mName);
69 logDebug("Metadata from node \"%s\": %p", name.cstr, node->mMetaData);
70 printMetadata(node->mMetaData);
72 for (size_t i = 0; i < node->mNumChildren; ++i) {
73 printAiNodeMetadata(node->mChildren[i]);
78 #endif // IMPORT_DEBUG_
80 const Solid* importSolid(const char* path) {
81 const struct aiScene* scene = importScene(path);
83 logError("Failed to import solid from %s", path);
88 const struct aiMetadata* meta = scene->mMetaData;
89 logDebug("Metadata from %s: %p", path, meta);
91 printAiNodeMetadata(scene->mRootNode);
92 #endif // IMPORT_DEBUG_
94 const unsigned int numMeshes = scene->mNumMeshes;
95 const unsigned int numMaterials = scene->mNumMaterials;
97 // TODO Consider assets with some arrays empty, and prevent zero mallocs
99 Solid* solid = malloc(sizeof(Solid));
100 solid->numMeshes = numMeshes;
101 solid->meshes = malloc(numMeshes * sizeof(Mesh));
102 solid->numMaterials = numMaterials;
103 solid->materials = malloc(numMaterials * sizeof(Material));
105 for (unsigned int meshIndex = 0; meshIndex < numMeshes; ++meshIndex) {
106 const struct aiMesh* aiMesh = scene->mMeshes[meshIndex];
107 const unsigned int numVertices = aiMesh->mNumVertices;
108 const unsigned int numFaces = aiMesh->mNumFaces;
110 Mesh mesh = { .numVertices = numVertices,
111 .vertices = malloc(numVertices * sizeof(Vector3D)),
113 .textureCoords = NULL,
114 .numFaces = numFaces,
115 .faces = malloc(numFaces * sizeof(Face)),
116 .materialIndex = aiMesh->mMaterialIndex };
118 for (unsigned int vertIndex = 0; vertIndex < numVertices; ++vertIndex) {
119 mesh.vertices[vertIndex] = convertAiVector3D(
120 aiMesh->mVertices[vertIndex]);
123 if (aiMesh->mNormals != NULL) {
124 mesh.normals = malloc(numVertices * sizeof(Vector3D));
125 for (unsigned int normIndex = 0; normIndex < numVertices; ++normIndex) {
126 mesh.normals[normIndex] = convertAiVector3D(
127 aiMesh->mNormals[normIndex]);
131 mesh.textureCoords = malloc(numVertices * sizeof(Vector3D));
132 for (unsigned int texcIndex = 0; texcIndex < numVertices; ++texcIndex) {
133 mesh.textureCoords[texcIndex] = convertAiVector3D(
134 aiMesh->mTextureCoords[0][texcIndex]);
137 for (unsigned int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
138 const struct aiFace aiFace = aiMesh->mFaces[faceIndex];
139 const unsigned int numIndices = aiFace.mNumIndices;
141 Face face = { .numIndices = numIndices,
142 .indices = malloc(numIndices * sizeof(size_t)),
143 .normals = malloc(numIndices * sizeof(Vector3D)) };
145 for (unsigned int i = 0; i < numIndices; ++i) {
146 face.indices[i] = aiFace.mIndices[i];
149 if (numIndices == 3) {
150 Vector3D normal = triangleNormal(mesh.vertices[face.indices[0]],
151 mesh.vertices[face.indices[1]],
152 mesh.vertices[face.indices[2]]);
153 for (size_t i = 0; i < numIndices; ++i) {
154 face.normals[i] = normal;
159 for (size_t i = 0; i < numIndices; ++i) {
160 face.normals[i] = mesh.normals[face.indices[i]];
169 mesh.faces[faceIndex] = face;
172 solid->meshes[meshIndex] = mesh;
175 GLuint* textureIds = malloc(numMaterials * sizeof(GLuint));
176 glGenTextures(numMaterials, textureIds);
178 for (unsigned int matIndex = 0; matIndex < numMaterials; ++matIndex) {
179 Material material = { .textureId = textureIds[matIndex] };
182 const struct aiMaterialProperty* prop;
183 aiGetMaterialProperty(scene->mMaterials[matIndex],
184 AI_MATKEY_SHADING_MODEL,
187 String key = stringFromAiString(prop->mKey);
189 logDebug("Material property \"%s\": Shading model: (length %u) %d",
192 *((int32_t*) prop->mData));
195 #endif // IMPORT_DEBUG_
197 glBindTexture(GL_TEXTURE_2D, material.textureId);
198 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
200 struct aiString originalTexturePath;
201 if (aiGetMaterialTexture(scene->mMaterials[matIndex],
202 aiTextureType_DIFFUSE,
204 &originalTexturePath,
205 NULL, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
206 const char* textureFilename = replaceFileExtension(originalTexturePath, ".tga");
207 const size_t textureFilenameLength = strlen(textureFilename);
208 char* texturePath = malloc(strlen("assets/") + textureFilenameLength + 1);
209 strcpy(texturePath, "assets/");
210 strncat(texturePath, textureFilename, textureFilenameLength);
212 TgaImage* textureImage = readTga(texturePath);
213 if (textureImage == NULL) {
214 logError("Importing solid from %s: Cannot read texture file %s", path, texturePath);
217 glTexImage2D(GL_TEXTURE_2D,
219 textureImage->imageComponents,
220 textureImage->header.imageWidth,
221 textureImage->header.imageHeight,
223 textureImage->imageFormat,
225 textureImage->bytes);
226 free(textureImage->bytes);
231 solid->materials[matIndex] = material;
233 glBindTexture(GL_TEXTURE_2D, 0);
235 aiReleaseImport(scene);
239 static const struct aiScene* importScene(const char* path) {
240 const struct aiScene* scene = aiImportFile(path, aiProcess_PreTransformVertices);
241 if (scene != NULL && scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) {
242 logError("Incomplete scene imported from %s", path);
243 aiReleaseImport(scene);
249 static Vector3D triangleNormal(Vector3D v1, Vector3D v2, Vector3D v3) {
250 return normalized(crossProduct(subtractVectors(v2, v1), subtractVectors(v3, v1)));
253 static Vector3D convertAiVector3D(struct aiVector3D vect) {
254 return (Vector3D) { .x = vect.x,
261 * The following function will not work properly with texture
262 * file names (excluding directory part) beginning with '.'
264 static const char* replaceFileExtension(const struct aiString path, const char* ext) {
265 size_t lengthToCopy = path.length;
267 char* lastDotSubstr = strrchr(path.data, '.');
268 if (lastDotSubstr != NULL) {
269 if (strpbrk(lastDotSubstr, "\\/") == NULL) {
270 lengthToCopy = lastDotSubstr - path.data;
274 size_t extLength = strlen(ext) + 1;
275 char* newPath = malloc(lengthToCopy + extLength);
276 strncpy(newPath, path.data, lengthToCopy);
277 strncpy(newPath + lengthToCopy, ext, extLength);