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