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