Vuo  2.3.1
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  VuoText filenameT = VuoText_make(filename);
145  VuoLocal(filenameT);
146  if (!VuoUrl_fetch(filenameT, &data, &dataLength))
147  return NULL;
148 
149  if (dataLength == 0)
150  {
151  free(data);
152  VUserLog("Warning: '%s' is empty", filename);
153  return NULL;
154  }
155 
157  d->data = data;
158  d->dataLength = dataLength;
159  d->position = 0;
160 
161  aiFile *af = (aiFile *)malloc(sizeof(aiFile));
162  af->UserData = (aiUserData)d;
163 
164  af->ReadProc = VuoSceneObjectGet_read;
165  af->TellProc = VuoSceneObjectGet_tell;
166  af->FileSizeProc = VuoSceneObjectGet_size;
167  af->SeekProc = VuoSceneObjectGet_seek;
168  af->WriteProc = NULL;
169  af->FlushProc = NULL;
170 
171  return af;
172 }
173 
177 static void VuoSceneObjectGet_close(aiFileIO *afio, aiFile *af)
178 {
179  VuoSceneObjectGet_data *d = (VuoSceneObjectGet_data *)af->UserData;
180  free(d->data);
181  free(af);
182 }
183 
184 
185 
189 static void convertAINodesToVuoSceneObjectsRecursively(const struct aiScene *scene, const struct aiNode *node, VuoShader *shaders, bool *shadersUsed, VuoSceneObject *sceneObject)
190 {
191  VuoSceneObject_setName(*sceneObject, VuoText_make(node->mName.data));
192 
193  // Copy the node's transform into our VuoSceneObject.
194  {
195  struct aiMatrix4x4 m = node->mTransformation;
196  struct aiVector3D scaling;
197  struct aiQuaternion rotation;
198  struct aiVector3D position;
199  aiDecomposeMatrix(&m, &scaling, &rotation, &position);
201  (VuoPoint3d){position.x, position.y, position.z},
202  (VuoPoint4d){rotation.x, rotation.y, rotation.z, rotation.w},
203  (VuoPoint3d){scaling.x, scaling.y, scaling.z}));
204  }
205 
206  // Convert each aiMesh to either a single leaf VuoSceneObject,
207  // or a VuoSceneObject group with multiple child VuoSceneObjects.
208  if (node->mNumMeshes && node->mNumMeshes == 1 && node->mNumChildren == 0)
209  VuoSceneObject_setType(*sceneObject, VuoSceneObjectSubType_Mesh);
210  else
211  {
212  VuoSceneObject_setType(*sceneObject, VuoSceneObjectSubType_Group);
214  }
215  for (unsigned int meshIndex = 0; meshIndex < node->mNumMeshes; ++meshIndex)
216  {
217  const struct aiMesh *meshObj = scene->mMeshes[node->mMeshes[meshIndex]];
218 
219  if (!meshObj->mVertices)
220  {
221  VUserLog("Error: Mesh '%s' doesn't contain any positions. Skipping.", meshObj->mName.data);
222  continue;
223  }
224 
225  if (!meshObj->mNormals || !meshObj->mTextureCoords[0])
226  VUserLog("Warning: Mesh '%s' is missing%s%s. These channels will be automatically generated, but lighting and 3D object filters may not work correctly.",
227  meshObj->mName.data,
228  meshObj->mNormals ? "" : " [normals]",
229  meshObj->mTextureCoords[0] ? "" : " [texture coordinates]");
230 
231  float *positions, *normals = NULL, *textureCoordinates = NULL, *colors = NULL;
232  unsigned int *elements;
233  VuoMesh_allocateCPUBuffers(meshObj->mNumVertices,
234  &positions,
235  meshObj->mNormals ? &normals : NULL,
236  meshObj->mTextureCoords[0] ? &textureCoordinates : NULL,
237  meshObj->mColors[0] ? &colors : NULL,
238  meshObj->mNumFaces * 3, &elements);
239 
240  for (unsigned int vertex = 0; vertex < meshObj->mNumVertices; ++vertex)
241  {
242  struct aiVector3D position = meshObj->mVertices[vertex];
243  positions[vertex * 3 ] = position.x;
244  positions[vertex * 3 + 1] = position.y;
245  positions[vertex * 3 + 2] = position.z;
246 
247  if (meshObj->mNormals)
248  {
249  struct aiVector3D normal = meshObj->mNormals[vertex];
250  normals[vertex * 3 ] = normal.x;
251  normals[vertex * 3 + 1] = normal.y;
252  normals[vertex * 3 + 2] = normal.z;
253  }
254 
255  if (meshObj->mTextureCoords[0])
256  {
257  struct aiVector3D textureCoordinate = meshObj->mTextureCoords[0][vertex];
258  textureCoordinates[vertex * 2 ] = textureCoordinate.x;
259  textureCoordinates[vertex * 2 + 1] = textureCoordinate.y;
260  }
261 
262  if (meshObj->mColors[0])
263  {
264  struct aiColor4D color = meshObj->mColors[0][vertex];
265  colors[vertex * 4 ] = color.r;
266  colors[vertex * 4 + 1] = color.g;
267  colors[vertex * 4 + 2] = color.b;
268  colors[vertex * 4 + 3] = color.a;
269  }
270  }
271 
272  unsigned int numValidElements = 0;
273  for (unsigned int face = 0; face < meshObj->mNumFaces; ++face)
274  {
275  const struct aiFace *faceObj = &meshObj->mFaces[face];
276  if (faceObj->mNumIndices != 3)
277  {
278  VUserLog("Warning: Face %u isn't a triangle (it has %u indices); skipping.",face,faceObj->mNumIndices);
279  continue;
280  }
281 
282  elements[numValidElements++] = faceObj->mIndices[0];
283  elements[numValidElements++] = faceObj->mIndices[1];
284  elements[numValidElements++] = faceObj->mIndices[2];
285  }
286 
287  VuoMesh mesh = VuoMesh_makeFromCPUBuffers(meshObj->mNumVertices, positions, normals, textureCoordinates, colors, numValidElements, elements, VuoMesh_IndividualTriangles);
288 
289  // if no texture coordinates found, attempt to generate passable ones.
290  if (!meshObj->mTextureCoords[0])
292 
293  if (node->mNumMeshes == 1 && node->mNumChildren == 0)
294  {
295  VuoSceneObject_setMesh(*sceneObject, mesh);
296  VuoSceneObject_setShader(*sceneObject, shaders[meshObj->mMaterialIndex]);
297  shadersUsed[meshObj->mMaterialIndex] = true;
298  }
299  else
300  {
301  // Add this aiMesh as a child of this VuoSceneObject.
302  VuoSceneObject child = VuoSceneObject_makeMesh(mesh, shaders[meshObj->mMaterialIndex], VuoTransform_makeIdentity());
304  shadersUsed[meshObj->mMaterialIndex] = true;
305  }
306  }
307 
308  for (unsigned int child = 0; child < node->mNumChildren; ++child)
309  {
310  VuoSceneObject childSceneObject = VuoSceneObject_makeEmpty();
311  convertAINodesToVuoSceneObjectsRecursively(scene, node->mChildren[child], shaders, shadersUsed, &childSceneObject);
312  VuoListAppendValue_VuoSceneObject(VuoSceneObject_getChildObjects(*sceneObject), childSceneObject);
313  }
314 }
315 
322 bool VuoSceneObject_get(VuoText sceneURL, VuoSceneObject *scene, bool center, bool fit, bool hasLeftHandedCoordinates)
323 {
324  *scene = NULL;
325 
326  if (VuoText_isEmpty(sceneURL))
327  return false;
328 
329  struct aiPropertyStore *props = aiCreatePropertyStore();
330  VuoGlContext_perform(^(CGLContextObj cgl_ctx){
331  GLint maxIndices;
332  glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices);
333  aiSetImportPropertyInteger(props, AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, maxIndices/3);
334 
335  GLint maxVertices;
336  glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices);
337  aiSetImportPropertyInteger(props, AI_CONFIG_PP_SLM_VERTEX_LIMIT, maxVertices);
338 
339  static bool limitsLogged = false;
340  if (!limitsLogged)
341  {
342  limitsLogged = true;
343  VDebugLog("OpenGL driver reports maxIndices=%d, maxVertices=%d", maxIndices, maxVertices);
344  }
345  });
346 
347  struct aiFileIO fileHandlers;
348  fileHandlers.OpenProc = VuoSceneObjectGet_open;
349  fileHandlers.CloseProc = VuoSceneObjectGet_close;
350 
351  const struct aiScene *ais = aiImportFileExWithProperties(
352  sceneURL,
353  aiProcess_Triangulate
354 // | aiProcess_PreTransformVertices
355 // | aiProcess_CalcTangentSpace
356  | aiProcess_GenSmoothNormals
357  | aiProcess_SplitLargeMeshes
358  | aiProcess_GenUVCoords,
359 // | aiProcess_OptimizeMeshes
360 // | aiProcess_OptimizeGraph
361  &fileHandlers,
362  props);
363  aiReleasePropertyStore(props);
364  if (!ais)
365  {
366  VUserLog("Error: %s", aiGetErrorString());
367  return false;
368  }
369 
370  if (ais->mFlags & AI_SCENE_FLAGS_INCOMPLETE)
371  VUserLog("Warning: Open Asset Import wasn't able to parse everything in this file.");
372 
373  VuoText normalizedSceneURL = VuoUrl_normalize(sceneURL, VuoUrlNormalize_default);
374  VuoRetain(normalizedSceneURL);
375  size_t lastSlashInSceneURL = VuoText_findLastOccurrence(normalizedSceneURL, "/");
376  VuoText sceneURLWithoutFilename = VuoText_substring(normalizedSceneURL, 1, lastSlashInSceneURL);
377  VuoRetain(sceneURLWithoutFilename);
378  VuoRelease(normalizedSceneURL);
379 
380  VuoShader shaders[ais->mNumMaterials];
381  bool shadersUsed[ais->mNumMaterials];
382  for (int i=0; i<ais->mNumMaterials; ++i)
383  {
384  struct aiMaterial *m = ais->mMaterials[i];
385 
386  struct aiString name;
387  aiGetMaterialString(m, AI_MATKEY_NAME, &name);
388  // VLog("material %d: %s",i,name.data);
389 
390  // Some meshes (such as the leaves in "Tree 2 N020414.3DS") obviously expect two-sided rendering,
391  // so I tried using material property AI_MATKEY_TWOSIDED. But I wasn't able to find (or create with Blender)
392  // any meshes having materials where AI_MATKEY_TWOSIDED is nonzero.
393 // int twosided = 0;
394 // aiGetMaterialIntegerArray(m, AI_MATKEY_TWOSIDED, &twosided, NULL);
395 
396  VuoShader shader = NULL;
397 
398  struct aiColor4D diffuseColorAI = {1,1,1,1};
399  aiGetMaterialColor(m, AI_MATKEY_COLOR_DIFFUSE, &diffuseColorAI);
400  VuoColor diffuseColor = VuoColor_makeWithRGBA(diffuseColorAI.r, diffuseColorAI.g, diffuseColorAI.b, diffuseColorAI.a);
401 // VLog("\tdiffuseColor: %s",VuoColor_getSummary(diffuseColor));
402 
403  struct aiColor4D specularColorAI = {1,1,1,1};
404  aiGetMaterialColor(m, AI_MATKEY_COLOR_SPECULAR, &specularColorAI);
405  VuoColor specularColor = VuoColor_makeWithRGBA(specularColorAI.r, specularColorAI.g, specularColorAI.b, specularColorAI.a);
406  // VLog("\tspecularColor: %s",VuoColor_getSummary(specularColor));
407 
408  float shininess = 10;
409  aiGetMaterialFloatArray(m, AI_MATKEY_SHININESS, &shininess, NULL);
410  if (shininess)
411  shininess = MAX(1.0001 - 1./shininess, 0);
412  // VLog("\tshininess: %g",shininess);
413 
414  int diffuseTextures = aiGetMaterialTextureCount(m, aiTextureType_DIFFUSE);
415  VuoImage diffuseImage = NULL;
417  if (diffuseTextures)
418  {
419  struct aiString path;
420  aiGetMaterialTexture(m, aiTextureType_DIFFUSE, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
421 
422  VuoText urlParts[2] = {sceneURLWithoutFilename, path.data};
423  VuoText textureURL = VuoText_append(urlParts, 2);
424  VuoRetain(textureURL);
425 // VLog("\tdiffuse: %s",textureURL);
426 
427  diffuseImage = VuoImage_get(textureURL);
428 
429  VuoRelease(textureURL);
430  }
431 
432  int normalTextures = aiGetMaterialTextureCount(m, aiTextureType_NORMALS);
433  VuoImage normalImage = NULL;
434  if (normalTextures)
435  {
436  struct aiString path;
437  aiGetMaterialTexture(m, aiTextureType_NORMALS, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
438 
439  VuoText urlParts[2] = {sceneURLWithoutFilename, path.data};
440  VuoText textureURL = VuoText_append(urlParts, 2);
441  VuoRetain(textureURL);
442 // VLog("\tnormal: %s",textureURL);
443 
444  normalImage = VuoImage_get(textureURL);
445 
446  VuoRelease(textureURL);
447  }
448 
449  int specularTextures = aiGetMaterialTextureCount(m, aiTextureType_SPECULAR);
450  VuoImage specularImage = NULL;
451  if (specularTextures)
452  {
453  struct aiString path;
454  aiGetMaterialTexture(m, aiTextureType_SPECULAR, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
455 
456  VuoText urlParts[2] = {sceneURLWithoutFilename, path.data};
457  VuoText textureURL = VuoText_append(urlParts, 2);
458  VuoRetain(textureURL);
459 // VLog("\tspecular: %s",textureURL);
460 
461  specularImage = VuoImage_get(textureURL);
462 
463  VuoRelease(textureURL);
464  }
465 
466 // Other texture types to consider supporting eventually...
467 #if 0
468  int ambientTextures = aiGetMaterialTextureCount(m, aiTextureType_AMBIENT);
469  if (ambientTextures)
470  {
471  struct aiString path;
472  aiGetMaterialTexture(m, aiTextureType_AMBIENT, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
473  VUserLog("\tambient: %s",path.data);
474  }
475 
476  int emissiveTextures = aiGetMaterialTextureCount(m, aiTextureType_EMISSIVE);
477  if (emissiveTextures)
478  {
479  struct aiString path;
480  aiGetMaterialTexture(m, aiTextureType_EMISSIVE, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
481  VUserLog("\temissive: %s",path.data);
482  }
483 
484  int opacityTextures = aiGetMaterialTextureCount(m, aiTextureType_OPACITY);
485  if (opacityTextures)
486  {
487  struct aiString path;
488  aiGetMaterialTexture(m, aiTextureType_OPACITY, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
489  VUserLog("\topacity: %s",path.data);
490  }
491 
492  int lightmapTextures = aiGetMaterialTextureCount(m, aiTextureType_LIGHTMAP);
493  if (lightmapTextures)
494  {
495  struct aiString path;
496  aiGetMaterialTexture(m, aiTextureType_LIGHTMAP, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
497  VUserLog("\tlightmap: %s",path.data);
498  }
499 
500  int reflectionTextures = aiGetMaterialTextureCount(m, aiTextureType_REFLECTION);
501  if (reflectionTextures)
502  {
503  struct aiString path;
504  aiGetMaterialTexture(m, aiTextureType_REFLECTION, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
505  VUserLog("\treflection: %s",path.data);
506  }
507 
508  int displacementTextures = aiGetMaterialTextureCount(m, aiTextureType_DISPLACEMENT);
509  if (displacementTextures)
510  {
511  struct aiString path;
512  aiGetMaterialTexture(m, aiTextureType_DISPLACEMENT, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
513  VUserLog("\tdisplacement: %s",path.data);
514  }
515 
516  int heightTextures = aiGetMaterialTextureCount(m, aiTextureType_HEIGHT);
517  if (heightTextures)
518  {
519  struct aiString path;
520  aiGetMaterialTexture(m, aiTextureType_HEIGHT, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
521  VUserLog("\theight: %s",path.data);
522  }
523 
524  int unknownTextures = aiGetMaterialTextureCount(m, aiTextureType_UNKNOWN);
525  if (unknownTextures)
526  {
527  struct aiString path;
528  aiGetMaterialTexture(m, aiTextureType_UNKNOWN, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL);
529  VUserLog("\tunknown: %s",path.data);
530  }
531 #endif
532 
533  if (normalImage || specularImage)
534  {
535  if (!diffuseImage)
536  diffuseImage = VuoImage_makeColorImage(diffuseColor, 1, 1);
537 
538  if (!normalImage)
539  normalImage = VuoImage_makeColorImage(VuoColor_makeWithRGBA(0.5,0.5,1,1), 1, 1);
540 
541  if (!specularImage)
542  specularImage = VuoImage_makeColorImage(VuoColor_makeWithRGBA(1,1,1,.9), 1, 1);
543 
544  VuoImage_setWrapMode(diffuseImage, VuoImageWrapMode_Repeat);
545  VuoImage_setWrapMode(normalImage, VuoImageWrapMode_Repeat);
546  VuoImage_setWrapMode(specularImage, VuoImageWrapMode_Repeat);
547  shader = VuoShader_makeLitImageDetailsShader(diffuseImage, 1, specularImage, normalImage);
548  }
549  else if (diffuseImage)
550  {
551  VuoImage_setWrapMode(diffuseImage, VuoImageWrapMode_Repeat);
552  shader = VuoShader_makeLitImageShader(diffuseImage, 1, specularColor, shininess);
553  }
554  else
555  shader = VuoShader_makeLitColorShader(diffuseColor, specularColor, shininess);
556 
557  VuoRelease(shader->name);
558  shader->name = VuoText_make(name.data);
559  VuoRetain(shader->name);
560 
561  // VLog("\tshader: %s",shader->summary);//VuoShader_getSummary(shader));
562 
563  shaders[i] = shader;
564  shadersUsed[i] = false;
565  }
566  VuoRelease(sceneURLWithoutFilename);
567 
568  *scene = VuoSceneObject_makeEmpty();
569  convertAINodesToVuoSceneObjectsRecursively(ais, ais->mRootNode, shaders, shadersUsed, scene);
570 
571  for (unsigned int i = 0; i < ais->mNumMaterials; ++i)
572  if (!shadersUsed[i])
573  {
574  VuoRetain(shaders[i]);
575  VuoRelease(shaders[i]);
576  }
577 
578  if(center)
579  VuoSceneObject_center(*scene);
580 
581  if(fit)
582  VuoSceneObject_normalize(*scene);
583 
584  if(hasLeftHandedCoordinates)
585  {
586  VuoSceneObject_apply(*scene, ^(VuoSceneObject currentObject, float modelviewMatrix[16])
587  {
588  // VuoTransform flipAxis = VuoTransform_makeEuler( (VuoPoint3d) {0,0,0}, (VuoPoint3d){0,0,0}, (VuoPoint3d){-1, 1, 1} );
589  // float matrix[16];
590  // VuoTransform_getMatrix(flipAxis, matrix);
591 
592  VuoMesh mesh = VuoSceneObject_getMesh(currentObject);
593  if (mesh)
594  {
595  unsigned int vertexCount, elementCount, *elements;
596  float *positions;
597  VuoMesh_getCPUBuffers(mesh, &vertexCount, &positions, NULL, NULL, NULL, &elementCount, &elements);
598 
599  for (int n = 0; n < vertexCount; n++)
600  positions[n * 3] *= -1;
601 
602  // flip triangle winding order
603  switch (VuoMesh_getElementAssemblyMethod(mesh))
604  {
606  for(int i = 0; i < elementCount; i+= 3)
607  {
608  unsigned int tmp = elements[i];
609  elements[i] = elements[i+2];
610  elements[i+2] = tmp;
611  }
612  break;
613 
614  // @todo - fill in additional winding order flip methods whenever this loader
615  // provides the ability to read 'em
616  // http://www.asawicki.info/news_1524_how_to_flip_triangles_in_triangle_mesh.html
617  // case VuoMesh_TriangleStrip:
618  // {
619  // unsigned int elementCount = msh.elementCount;
620 
621  // // add an extra duplicate vertex at the beginning of the strip
622  // // if the element count is even
623  // if(elementCount % 2 == 0)
624  // {
625  // msh.elementCount++;
626  // unsigned int *resized = malloc(sizeof(unsigned int) * msh.elementCount);
627  // resized[0] = msh.elements[0];
628  // memcpy(&resized[1], msh.elements, sizeof(unsigned int) * msh.elementCount);
629  // msh.elements = resized;
630  // }
631  // else
632  // {
633  // for(int i = 0; i < elementCount / 2; i++)
634  // {
635  // unsigned int tmp = msh.elements[i];
636  // msh.elements[i] = msh.elements[elementCount-i];
637  // msh.elements[i] = tmp;
638  // }
639  // }
640  // } break;
641 
642  default:
643  break;
644  }
645  }
646  });
647  }
648 
649  aiReleaseImport(ais);
650 
651  return true;
652 }