Vuo  2.0.0
VuoSceneObjectGet.c
Go to the documentation of this file.
1 
10 #include "VuoSceneObjectGet.h"
11 
12 #include "VuoUrlFetch.h"
13 #include "VuoImageGet.h"
14 #include "VuoMeshUtility.h"
15 
16 #include <OpenGL/CGLMacro.h>
17 
18 #pragma clang diagnostic push
19 #pragma clang diagnostic ignored "-Wdocumentation"
20  #include <cimport.h>
21  #include <config.h>
22  #include <scene.h>
23  #include <postprocess.h>
24 
25  // cfileio.h won't compile without these.
26  #ifndef DOXYGEN
27  typedef enum aiOrigin aiOrigin;
28  typedef struct aiFile aiFile;
29  typedef struct aiFileIO aiFileIO;
30  #endif
31  #include <cfileio.h>
32 #pragma clang diagnostic pop
33 
34 #include "module.h"
35 
36 #ifdef VUO_COMPILER
38  "title" : "VuoSceneGet",
39  "dependencies" : [
40  "VuoSceneObject",
41  "VuoGlContext",
42  "VuoImageGet",
43  "VuoShader",
44  "VuoUrlFetch",
45  "VuoList_VuoShader",
46  "VuoMeshUtility",
47  "oai",
48  "z"
49  ]
50  });
51 #endif
52 
53 
54 
58 typedef struct
59 {
60  size_t dataLength;
61  void *data;
62  size_t position;
64 
68 static size_t VuoSceneObjectGet_read(aiFile *af, char *buffer, size_t size, size_t count)
69 {
70 // VLog(" %ld (%ld * %ld)", size*count, size, count);
72  if (d->position >= d->dataLength)
73  {
74 // VLog(" refusing to read past end");
75  return 0;
76  }
77 
78  size_t bytesToRead = MIN(d->dataLength - d->position, size*count);
79  memcpy(buffer, d->data + d->position, bytesToRead);
80 // VLog(" actually read %ld", bytesToRead);
81  d->position += bytesToRead;
82  return bytesToRead;
83 }
84 
88 static size_t VuoSceneObjectGet_tell(aiFile *af)
89 {
91 // VLog(" position = %ld", d->position);
92  return d->position;
93 }
94 
98 static size_t VuoSceneObjectGet_size(aiFile *af)
99 {
100  VuoSceneObjectGet_data *d = (VuoSceneObjectGet_data *)af->UserData;
101  return d->dataLength;
102 }
103 
107 static aiReturn VuoSceneObjectGet_seek(aiFile *af, size_t p, aiOrigin origin)
108 {
109 // VLog(" seek by %ld (mode %d)", p, origin);
110  VuoSceneObjectGet_data *d = (VuoSceneObjectGet_data *)af->UserData;
111 
112  size_t proposedPosition;
113  if (origin == aiOrigin_SET)
114  proposedPosition = p;
115  else if (origin == aiOrigin_CUR)
116  proposedPosition = d->position + p;
117  else if (origin == aiOrigin_END)
118  proposedPosition = d->dataLength - p;
119  else
120  return aiReturn_FAILURE;
121 
122  if (proposedPosition >= d->dataLength)
123  return aiReturn_FAILURE;
124 
125  d->position = proposedPosition;
126  return aiReturn_SUCCESS;
127 }
128 
132 static aiFile *VuoSceneObjectGet_open(aiFileIO *afio, const char *filename, const char *mode)
133 {
134 // VLog("%s", filename);
135 
136  if (strcmp(mode, "rb") != 0)
137  {
138  VUserLog("Error: Unknown file mode '%s'",mode);
139  return NULL;
140  }
141 
142  void *data;
143  unsigned int dataLength;
144  if (!VuoUrl_fetch(filename, &data, &dataLength))
145  return NULL;
146 
147  if (dataLength == 0)
148  {
149  free(data);
150  VUserLog("Warning: '%s' is empty", filename);
151  return NULL;
152  }
153 
155  d->data = data;
156  d->dataLength = dataLength;
157  d->position = 0;
158 
159  aiFile *af = (aiFile *)malloc(sizeof(aiFile));
160  af->UserData = (aiUserData)d;
161 
162  af->ReadProc = VuoSceneObjectGet_read;
163  af->TellProc = VuoSceneObjectGet_tell;
164  af->FileSizeProc = VuoSceneObjectGet_size;
165  af->SeekProc = VuoSceneObjectGet_seek;
166  af->WriteProc = NULL;
167  af->FlushProc = NULL;
168 
169  return af;
170 }
171 
175 static void VuoSceneObjectGet_close(aiFileIO *afio, aiFile *af)
176 {
177  VuoSceneObjectGet_data *d = (VuoSceneObjectGet_data *)af->UserData;
178  free(d->data);
179  free(af);
180 }
181 
182 
183 
187 static void convertAINodesToVuoSceneObjectsRecursively(const struct aiScene *scene, const struct aiNode *node, VuoShader *shaders, bool *shadersUsed, VuoSceneObject *sceneObject)
188 {
189  VuoSceneObject_setName(*sceneObject, VuoText_make(node->mName.data));
190 
191  // Copy the node's transform into our VuoSceneObject.
192  {
193  struct aiMatrix4x4 m = node->mTransformation;
194  struct aiVector3D scaling;
195  struct aiQuaternion rotation;
196  struct aiVector3D position;
197  aiDecomposeMatrix(&m, &scaling, &rotation, &position);
199  (VuoPoint3d){position.x, position.y, position.z},
200  (VuoPoint4d){rotation.x, rotation.y, rotation.z, rotation.w},
201  (VuoPoint3d){scaling.x, scaling.y, scaling.z}));
202  }
203 
204  // Convert each aiMesh to either a single leaf VuoSceneObject,
205  // or a VuoSceneObject group with multiple child VuoSceneObjects.
206  if (node->mNumMeshes && node->mNumMeshes == 1 && node->mNumChildren == 0)
207  VuoSceneObject_setType(*sceneObject, VuoSceneObjectSubType_Mesh);
208  else
209  {
210  VuoSceneObject_setType(*sceneObject, VuoSceneObjectSubType_Group);
212  }
213  for (unsigned int meshIndex = 0; meshIndex < node->mNumMeshes; ++meshIndex)
214  {
215  const struct aiMesh *meshObj = scene->mMeshes[node->mMeshes[meshIndex]];
216 
217  if (!meshObj->mVertices)
218  {
219  VUserLog("Error: Mesh '%s' doesn't contain any positions. Skipping.", meshObj->mName.data);
220  continue;
221  }
222 
223  if (!meshObj->mNormals || !meshObj->mTangents || !meshObj->mBitangents || !meshObj->mTextureCoords[0])
224  VUserLog("Warning: Mesh '%s' is missing%s%s%s%s. These channels will be automatically generated, but lighting and 3D object filters may not work correctly.",
225  meshObj->mName.data,
226  meshObj->mNormals ? "" : " [normals]",
227  meshObj->mTangents ? "" : " [tangents]",
228  meshObj->mBitangents ? "" : " [bitangents]",
229  meshObj->mTextureCoords[0] ? "" : " [texture coordinates]");
230 
231  VuoPoint4d *positions, *normals, *tangents, *bitangents, *textureCoordinates;
232  unsigned int *elements;
233  VuoMesh_allocateCPUBuffers(meshObj->mNumVertices, &positions, &normals, &tangents, &bitangents, &textureCoordinates, meshObj->mNumFaces * 3, &elements);
234 
235  for (unsigned int vertex = 0; vertex < meshObj->mNumVertices; ++vertex)
236  {
237  struct aiVector3D position = meshObj->mVertices[vertex];
238  positions[vertex] = VuoPoint4d_make(position.x, position.y, position.z, 1);
239 
240  if (meshObj->mNormals)
241  {
242  struct aiVector3D normal = meshObj->mNormals[vertex];
243  normals[vertex] = VuoPoint4d_make(normal.x, normal.y, normal.z, 0);
244  }
245 
246  if (meshObj->mNormals && meshObj->mTangents)
247  {
248  struct aiVector3D normal = meshObj->mNormals[vertex];
249  struct aiVector3D tangent = meshObj->mTangents[vertex];
250  tangents[vertex] = VuoPoint4d_make(tangent.x, tangent.y, tangent.z, 0);
251 
252  VuoPoint3d n3 = VuoPoint3d_make(normal.x, normal.y, normal.z);
253  VuoPoint3d t3 = VuoPoint3d_make(tangent.x, tangent.y, tangent.z);
254  VuoPoint3d bitangent = VuoPoint3d_crossProduct(n3, t3);
255  bitangents[vertex] = VuoPoint4d_make(bitangent.x, bitangent.y, bitangent.z, 0);
256  }
257 
258  if (meshObj->mTextureCoords[0])
259  {
260  struct aiVector3D textureCoordinate = meshObj->mTextureCoords[0][vertex];
261  textureCoordinates[vertex] = VuoPoint4d_make(textureCoordinate.x, textureCoordinate.y, textureCoordinate.z, 0);
262  }
263 
265  }
266 
267  unsigned int numValidElements = 0;
268  for (unsigned int face = 0; face < meshObj->mNumFaces; ++face)
269  {
270  const struct aiFace *faceObj = &meshObj->mFaces[face];
271  if (faceObj->mNumIndices != 3)
272  {
273  VUserLog("Warning: Face %u isn't a triangle (it has %u indices); skipping.",face,faceObj->mNumIndices);
274  continue;
275  }
276 
277  elements[numValidElements++] = faceObj->mIndices[0];
278  elements[numValidElements++] = faceObj->mIndices[1];
279  elements[numValidElements++] = faceObj->mIndices[2];
280  }
281 
282  VuoMesh mesh = VuoMesh_makeFromCPUBuffers(meshObj->mNumVertices, positions, normals, tangents, bitangents, textureCoordinates, meshObj->mNumFaces * 3, elements, VuoMesh_IndividualTriangles);
283 
284  // if no texture coordinates found, attempt to generate passable ones.
285  if (!meshObj->mTextureCoords[0])
287 
288  // if mesh doesn't have tangents, but does have normals and textures, we can generate tangents & bitangents.
289  if (!meshObj->mTangents && meshObj->mNormals)
291 
292  if (node->mNumMeshes == 1 && node->mNumChildren == 0)
293  {
294  VuoSceneObject_setMesh(*sceneObject, mesh);
295  VuoSceneObject_setShader(*sceneObject, shaders[meshObj->mMaterialIndex]);
296  shadersUsed[meshObj->mMaterialIndex] = true;
297  }
298  else
299  {
300  // Add this aiMesh as a child of this VuoSceneObject.
301  VuoSceneObject child = VuoSceneObject_makeMesh(mesh, shaders[meshObj->mMaterialIndex], VuoTransform_makeIdentity());
303  shadersUsed[meshObj->mMaterialIndex] = true;
304  }
305  }
306 
307  for (unsigned int child = 0; child < node->mNumChildren; ++child)
308  {
309  VuoSceneObject childSceneObject = VuoSceneObject_makeEmpty();
310  convertAINodesToVuoSceneObjectsRecursively(scene, node->mChildren[child], shaders, shadersUsed, &childSceneObject);
311  VuoListAppendValue_VuoSceneObject(VuoSceneObject_getChildObjects(*sceneObject), childSceneObject);
312  }
313 }
314 
321 bool VuoSceneObject_get(VuoText sceneURL, VuoSceneObject *scene, bool center, bool fit, bool hasLeftHandedCoordinates)
322 {
323  *scene = NULL;
324 
325  if (VuoText_isEmpty(sceneURL))
326  return false;
327 
328  struct aiPropertyStore *props = aiCreatePropertyStore();
329  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
330  GLint maxIndices;
331  glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices);
332  aiSetImportPropertyInteger(props, AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, maxIndices/3);
333 
334  GLint maxVertices;
335  glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices);
336  aiSetImportPropertyInteger(props, AI_CONFIG_PP_SLM_VERTEX_LIMIT, maxVertices);
337  });
338 
339  struct aiFileIO fileHandlers;
340  fileHandlers.OpenProc = VuoSceneObjectGet_open;
341  fileHandlers.CloseProc = VuoSceneObjectGet_close;
342 
343  const struct aiScene *ais = aiImportFileExWithProperties(
344  sceneURL,
345  aiProcess_Triangulate
346 // | aiProcess_PreTransformVertices
347  | aiProcess_CalcTangentSpace
348  | aiProcess_GenSmoothNormals
349  | aiProcess_SplitLargeMeshes
350  | aiProcess_GenUVCoords,
351 // | aiProcess_OptimizeMeshes
352 // | aiProcess_OptimizeGraph
353  &fileHandlers,
354  props);
355  aiReleasePropertyStore(props);
356  if (!ais)
357  {
358  VUserLog("Error: %s\n", aiGetErrorString());
359  return false;
360  }
361 
362  if (ais->mFlags & AI_SCENE_FLAGS_INCOMPLETE)
363  VUserLog("Warning: Open Asset Import wasn't able to parse everything in this file.");
364 
365  VuoText normalizedSceneURL = VuoUrl_normalize(sceneURL, VuoUrlNormalize_default);
366  VuoRetain(normalizedSceneURL);
367  size_t lastSlashInSceneURL = VuoText_findLastOccurrence(normalizedSceneURL, "/");
368  VuoText sceneURLWithoutFilename = VuoText_substring(normalizedSceneURL, 1, lastSlashInSceneURL);
369  VuoRetain(sceneURLWithoutFilename);
370  VuoRelease(normalizedSceneURL);
371 
372  VuoShader shaders[ais->mNumMaterials];
373  bool shadersUsed[ais->mNumMaterials];
374  for (int i=0; i<ais->mNumMaterials; ++i)
375  {
376  struct aiMaterial *m = ais->mMaterials[i];
377 
378  struct aiString name;
379  aiGetMaterialString(m, AI_MATKEY_NAME, &name);
380  // VLog("material %d: %s",i,name.data);
381 
382  // Some meshes (such as the leaves in "Tree 2 N020414.3DS") obviously expect two-sided rendering,
383  // so I tried using material property AI_MATKEY_TWOSIDED. But I wasn't able to find (or create with Blender)
384  // any meshes having materials where AI_MATKEY_TWOSIDED is nonzero.
385 // int twosided = 0;
386 // aiGetMaterialIntegerArray(m, AI_MATKEY_TWOSIDED, &twosided, NULL);
387 
388  VuoShader shader = NULL;
389 
390  struct aiColor4D diffuseColorAI = {1,1,1,1};
391  aiGetMaterialColor(m, AI_MATKEY_COLOR_DIFFUSE, &diffuseColorAI);
392  VuoColor diffuseColor = VuoColor_makeWithRGBA(diffuseColorAI.r, diffuseColorAI.g, diffuseColorAI.b, diffuseColorAI.a);
393 // VLog("\tdiffuseColor: %s",VuoColor_getSummary(diffuseColor));
394 
395  struct aiColor4D specularColorAI = {1,1,1,1};
396  aiGetMaterialColor(m, AI_MATKEY_COLOR_SPECULAR, &specularColorAI);
397  VuoColor specularColor = VuoColor_makeWithRGBA(specularColorAI.r, specularColorAI.g, specularColorAI.b, specularColorAI.a);
398  // VLog("\tspecularColor: %s",VuoColor_getSummary(specularColor));
399 
400  float shininess = 10;
401  aiGetMaterialFloatArray(m, AI_MATKEY_SHININESS, &shininess, NULL);
402  if (shininess)
403  shininess = MAX(1.0001 - 1./shininess, 0);
404  // VLog("\tshininess: %g",shininess);
405 
406  int diffuseTextures = aiGetMaterialTextureCount(m, aiTextureType_DIFFUSE);
407  VuoImage diffuseImage = NULL;
409  if (diffuseTextures)
410  {
411  struct aiString path;
412  aiGetMaterialTexture(m, aiTextureType_DIFFUSE, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
413 
414  VuoText urlParts[2] = {sceneURLWithoutFilename, path.data};
415  VuoText textureURL = VuoText_append(urlParts, 2);
416  VuoRetain(textureURL);
417 // VLog("\tdiffuse: %s",textureURL);
418 
419  diffuseImage = VuoImage_get(textureURL);
420 
421  VuoRelease(textureURL);
422  }
423 
424  int normalTextures = aiGetMaterialTextureCount(m, aiTextureType_NORMALS);
425  VuoImage normalImage = NULL;
426  if (normalTextures)
427  {
428  struct aiString path;
429  aiGetMaterialTexture(m, aiTextureType_NORMALS, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
430 
431  VuoText urlParts[2] = {sceneURLWithoutFilename, path.data};
432  VuoText textureURL = VuoText_append(urlParts, 2);
433  VuoRetain(textureURL);
434 // VLog("\tnormal: %s",textureURL);
435 
436  normalImage = VuoImage_get(textureURL);
437 
438  VuoRelease(textureURL);
439  }
440 
441  int specularTextures = aiGetMaterialTextureCount(m, aiTextureType_SPECULAR);
442  VuoImage specularImage = NULL;
443  if (specularTextures)
444  {
445  struct aiString path;
446  aiGetMaterialTexture(m, aiTextureType_SPECULAR, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
447 
448  VuoText urlParts[2] = {sceneURLWithoutFilename, path.data};
449  VuoText textureURL = VuoText_append(urlParts, 2);
450  VuoRetain(textureURL);
451 // VLog("\tspecular: %s",textureURL);
452 
453  specularImage = VuoImage_get(textureURL);
454 
455  VuoRelease(textureURL);
456  }
457 
458 // Other texture types to consider supporting eventually...
459 #if 0
460  int ambientTextures = aiGetMaterialTextureCount(m, aiTextureType_AMBIENT);
461  if (ambientTextures)
462  {
463  struct aiString path;
464  aiGetMaterialTexture(m, aiTextureType_AMBIENT, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
465  VUserLog("\tambient: %s",path.data);
466  }
467 
468  int emissiveTextures = aiGetMaterialTextureCount(m, aiTextureType_EMISSIVE);
469  if (emissiveTextures)
470  {
471  struct aiString path;
472  aiGetMaterialTexture(m, aiTextureType_EMISSIVE, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
473  VUserLog("\temissive: %s",path.data);
474  }
475 
476  int opacityTextures = aiGetMaterialTextureCount(m, aiTextureType_OPACITY);
477  if (opacityTextures)
478  {
479  struct aiString path;
480  aiGetMaterialTexture(m, aiTextureType_OPACITY, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
481  VUserLog("\topacity: %s",path.data);
482  }
483 
484  int lightmapTextures = aiGetMaterialTextureCount(m, aiTextureType_LIGHTMAP);
485  if (lightmapTextures)
486  {
487  struct aiString path;
488  aiGetMaterialTexture(m, aiTextureType_LIGHTMAP, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
489  VUserLog("\tlightmap: %s",path.data);
490  }
491 
492  int reflectionTextures = aiGetMaterialTextureCount(m, aiTextureType_REFLECTION);
493  if (reflectionTextures)
494  {
495  struct aiString path;
496  aiGetMaterialTexture(m, aiTextureType_REFLECTION, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
497  VUserLog("\treflection: %s",path.data);
498  }
499 
500  int displacementTextures = aiGetMaterialTextureCount(m, aiTextureType_DISPLACEMENT);
501  if (displacementTextures)
502  {
503  struct aiString path;
504  aiGetMaterialTexture(m, aiTextureType_DISPLACEMENT, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
505  VUserLog("\tdisplacement: %s",path.data);
506  }
507 
508  int heightTextures = aiGetMaterialTextureCount(m, aiTextureType_HEIGHT);
509  if (heightTextures)
510  {
511  struct aiString path;
512  aiGetMaterialTexture(m, aiTextureType_HEIGHT, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
513  VUserLog("\theight: %s",path.data);
514  }
515 
516  int unknownTextures = aiGetMaterialTextureCount(m, aiTextureType_UNKNOWN);
517  if (unknownTextures)
518  {
519  struct aiString path;
520  aiGetMaterialTexture(m, aiTextureType_UNKNOWN, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
521  VUserLog("\tunknown: %s",path.data);
522  }
523 #endif
524 
525  if (normalImage || specularImage)
526  {
527  if (!diffuseImage)
528  diffuseImage = VuoImage_makeColorImage(diffuseColor, 1, 1);
529 
530  if (!normalImage)
531  normalImage = VuoImage_makeColorImage(VuoColor_makeWithRGBA(0.5,0.5,1,1), 1, 1);
532 
533  if (!specularImage)
534  specularImage = VuoImage_makeColorImage(VuoColor_makeWithRGBA(1,1,1,.9), 1, 1);
535 
536  VuoImage_setWrapMode(diffuseImage, VuoImageWrapMode_Repeat);
537  VuoImage_setWrapMode(normalImage, VuoImageWrapMode_Repeat);
538  VuoImage_setWrapMode(specularImage, VuoImageWrapMode_Repeat);
539  shader = VuoShader_makeLitImageDetailsShader(diffuseImage, 1, specularImage, normalImage);
540  }
541  else if (diffuseImage)
542  {
543  VuoImage_setWrapMode(diffuseImage, VuoImageWrapMode_Repeat);
544  shader = VuoShader_makeLitImageShader(diffuseImage, 1, specularColor, shininess);
545  }
546  else
547  shader = VuoShader_makeLitColorShader(diffuseColor, specularColor, shininess);
548 
549  VuoRelease(shader->name);
550  shader->name = VuoText_make(name.data);
551  VuoRetain(shader->name);
552 
553  // VLog("\tshader: %s",shader->summary);//VuoShader_getSummary(shader));
554 
555  shaders[i] = shader;
556  shadersUsed[i] = false;
557  }
558  VuoRelease(sceneURLWithoutFilename);
559 
560  *scene = VuoSceneObject_makeEmpty();
561  convertAINodesToVuoSceneObjectsRecursively(ais, ais->mRootNode, shaders, shadersUsed, scene);
562 
563  for (unsigned int i = 0; i < ais->mNumMaterials; ++i)
564  if (!shadersUsed[i])
565  {
566  VuoRetain(shaders[i]);
567  VuoRelease(shaders[i]);
568  }
569 
570  if(center)
571  VuoSceneObject_center(*scene);
572 
573  if(fit)
574  VuoSceneObject_normalize(*scene);
575 
576  if(hasLeftHandedCoordinates)
577  {
578  VuoSceneObject_apply(*scene, ^(VuoSceneObject currentObject, float modelviewMatrix[16])
579  {
580  // VuoTransform flipAxis = VuoTransform_makeEuler( (VuoPoint3d) {0,0,0}, (VuoPoint3d){0,0,0}, (VuoPoint3d){-1, 1, 1} );
581  // float matrix[16];
582  // VuoTransform_getMatrix(flipAxis, matrix);
583 
584  VuoMesh mesh = VuoSceneObject_getMesh(currentObject);
585  if (mesh)
586  {
587  unsigned int vertexCount, elementCount, *elements;
588  VuoPoint4d *positions;
589  VuoMesh_getCPUBuffers(mesh, &vertexCount, &positions, NULL, NULL, NULL, NULL, &elementCount, &elements);
590 
591  for (int n = 0; n < vertexCount; n++)
592  positions[n].x *= -1;
593 
594  // flip triangle winding order
595  switch (VuoMesh_getElementAssemblyMethod(mesh))
596  {
598  for(int i = 0; i < elementCount; i+= 3)
599  {
600  unsigned int tmp = elements[i];
601  elements[i] = elements[i+2];
602  elements[i+2] = tmp;
603  }
604  break;
605 
606  // @todo - fill in additional winding order flip methods whenever this loader
607  // provides the ability to read 'em
608  // http://www.asawicki.info/news_1524_how_to_flip_triangles_in_triangle_mesh.html
609  // case VuoMesh_TriangleStrip:
610  // {
611  // unsigned int elementCount = msh.elementCount;
612 
613  // // add an extra duplicate vertex at the beginning of the strip
614  // // if the element count is even
615  // if(elementCount % 2 == 0)
616  // {
617  // msh.elementCount++;
618  // unsigned int *resized = malloc(sizeof(unsigned int) * msh.elementCount);
619  // resized[0] = msh.elements[0];
620  // memcpy(&resized[1], msh.elements, sizeof(unsigned int) * msh.elementCount);
621  // msh.elements = resized;
622  // }
623  // else
624  // {
625  // for(int i = 0; i < elementCount / 2; i++)
626  // {
627  // unsigned int tmp = msh.elements[i];
628  // msh.elements[i] = msh.elements[elementCount-i];
629  // msh.elements[i] = tmp;
630  // }
631  // }
632  // } break;
633 
634  default:
635  break;
636  }
637  }
638  });
639  }
640 
641  aiReleaseImport(ais);
642 
643  return true;
644 }