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