Vuo  2.4.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
58typedef struct
59{
60 size_t dataLength;
61 void *data;
62 size_t position;
64
68static 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
88static size_t VuoSceneObjectGet_tell(aiFile *af)
89{
91// VLog(" position = %ld", d->position);
92 return d->position;
93}
94
98static size_t VuoSceneObjectGet_size(aiFile *af)
99{
101 return d->dataLength;
102}
103
107static aiReturn VuoSceneObjectGet_seek(aiFile *af, size_t p, aiOrigin origin)
108{
109// VLog(" seek by %ld (mode %d)", p, origin);
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
132static 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
177static void VuoSceneObjectGet_close(aiFileIO *afio, aiFile *af)
178{
180 free(d->data);
181 free(af);
182}
183
184
185
189static 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);
313 }
314}
315
322bool 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)
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
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}