Vuo 2.4.4
Loading...
Searching...
No Matches
VuoSceneObjectRenderer.cc
Go to the documentation of this file.
1
11
12#include <Block.h>
13#include <CoreServices/CoreServices.h>
14#include <OpenGL/CGLMacro.h>
16#define glGenVertexArrays glGenVertexArraysAPPLE
17#define glBindVertexArray glBindVertexArrayAPPLE
18#define glDeleteVertexArrays glDeleteVertexArraysAPPLE
20
21extern "C"
22{
23#ifdef VUO_COMPILER
25 "title" : "VuoSceneObjectRenderer",
26 "dependencies" : [
27 "VuoSceneObject",
28 "VuoShader",
29 "VuoGlContext",
30 "VuoGlPool",
31 "OpenGL.framework"
32 ]
33 });
34#endif
35}
36
40typedef struct
41{
42 GLint position;
43 GLint normal;
44 GLint textureCoordinate;
45 GLint color;
46
47 unsigned int expectedOutputPrimitiveCount;
48 bool mayChangeOutputPrimitiveCount;
50
55{
56 VuoShader shader;
57
58 GLuint shamTexture;
59 GLuint shamFramebuffer;
60
61 GLuint query;
62
63 GLuint vertexArray;
64
67 VuoSceneObjectRenderer_Attributes triangleAttributes;
68};
69
71
78{
80 {
81 VUserLog("Error '%s' is not a transform feedback shader.", shader->name);
82 return NULL;
83 }
84
85 __block struct VuoSceneObjectRendererInternal *sceneObjectRenderer = (struct VuoSceneObjectRendererInternal *)malloc(sizeof(struct VuoSceneObjectRendererInternal));
86 VuoRegister(sceneObjectRenderer, VuoSceneObjectRenderer_destroy);
87
89 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
90 // Fetch the shader's attribute locations
91 bool havePoints = VuoShader_getAttributeLocations(shader, VuoMesh_Points, cgl_ctx, &sceneObjectRenderer->pointAttributes.position, &sceneObjectRenderer->pointAttributes.normal, &sceneObjectRenderer->pointAttributes.textureCoordinate, &sceneObjectRenderer->pointAttributes.color );
92 bool haveLines = VuoShader_getAttributeLocations(shader, VuoMesh_IndividualLines, cgl_ctx, &sceneObjectRenderer->lineAttributes.position, &sceneObjectRenderer->lineAttributes.normal, &sceneObjectRenderer->lineAttributes.textureCoordinate, &sceneObjectRenderer->lineAttributes.color );
93 bool haveTriangles = VuoShader_getAttributeLocations(shader, VuoMesh_IndividualTriangles, cgl_ctx, &sceneObjectRenderer->triangleAttributes.position, &sceneObjectRenderer->triangleAttributes.normal, &sceneObjectRenderer->triangleAttributes.textureCoordinate, &sceneObjectRenderer->triangleAttributes.color);
94 if (!havePoints || !haveLines || !haveTriangles)
95 {
96 VUserLog("Error: '%s' is missing programs for: %s %s %s", shader->name, havePoints? "" : "points", haveLines? "" : "lines", haveTriangles? "" : "triangles");
97 free(sceneObjectRenderer);
98 sceneObjectRenderer = NULL;
99 return;
100 }
101
102 sceneObjectRenderer->pointAttributes.expectedOutputPrimitiveCount = VuoShader_getExpectedOutputPrimitiveCount (shader, VuoMesh_Points);
103 sceneObjectRenderer->pointAttributes.mayChangeOutputPrimitiveCount = VuoShader_getMayChangeOutputPrimitiveCount(shader, VuoMesh_Points);
104
105 sceneObjectRenderer->lineAttributes.expectedOutputPrimitiveCount = VuoShader_getExpectedOutputPrimitiveCount (shader, VuoMesh_IndividualLines);
106 sceneObjectRenderer->lineAttributes.mayChangeOutputPrimitiveCount = VuoShader_getMayChangeOutputPrimitiveCount(shader, VuoMesh_IndividualLines);
107
108 sceneObjectRenderer->triangleAttributes.expectedOutputPrimitiveCount = VuoShader_getExpectedOutputPrimitiveCount (shader, VuoMesh_IndividualTriangles);
109 sceneObjectRenderer->triangleAttributes.mayChangeOutputPrimitiveCount = VuoShader_getMayChangeOutputPrimitiveCount(shader, VuoMesh_IndividualTriangles);
110
111 sceneObjectRenderer->shader = shader;
112 VuoRetain(sceneObjectRenderer->shader);
113
114 glGenVertexArrays(1, &sceneObjectRenderer->vertexArray);
115
116 // https://stackoverflow.com/questions/24112671/transform-feedback-without-a-framebuffer
117 sceneObjectRenderer->shamTexture = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_Allocate, GL_TEXTURE_2D, GL_RGBA, 1, 1, GL_BGRA, NULL);
118 VuoGlTexture_retain(sceneObjectRenderer->shamTexture, NULL, NULL);
119 glGenFramebuffers(1, &sceneObjectRenderer->shamFramebuffer);
120 glBindFramebuffer(GL_FRAMEBUFFER, sceneObjectRenderer->shamFramebuffer);
121 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, sceneObjectRenderer->shamTexture, 0);
122 glBindFramebuffer(GL_FRAMEBUFFER, 0);
123
124 glGenQueries(1, &sceneObjectRenderer->query);
125 });
126
127 return (VuoSceneObjectRenderer)sceneObjectRenderer;
128}
129
134static void VuoSceneObjectRenderer_drawSingle(CGLContextObj cgl_ctx, struct VuoSceneObjectRendererInternal *sceneObjectRenderer, VuoSceneObject sceneObject, float modelviewMatrix[16])
135{
136 VuoMesh mesh = VuoSceneObject_getMesh(sceneObject);
137 if (!mesh)
138 return;
139
141 GLuint outputPrimitiveGlMode;
143 int primitiveVertexMultiplier;
144 if (outputPrimitiveMode == VuoMesh_IndividualTriangles)
145 {
146 outputPrimitiveGlMode = GL_TRIANGLES;
147 attributes = sceneObjectRenderer->triangleAttributes;
148 primitiveVertexMultiplier = 3;
149 }
150 else if (outputPrimitiveMode == VuoMesh_IndividualLines)
151 {
152 outputPrimitiveGlMode = GL_LINES;
153 attributes = sceneObjectRenderer->lineAttributes;
154 primitiveVertexMultiplier = 2;
155 }
156 else // if (submesh.elementAssemblyMethod == VuoMesh_Points)
157 {
158 outputPrimitiveGlMode = GL_POINTS;
159 attributes = sceneObjectRenderer->pointAttributes;
160 primitiveVertexMultiplier = 1;
161 }
162
163 unsigned int vertexCount, combinedBuffer, elementCount, elementBuffer;
164 void *normalOffset, *textureCoordinateOffset, *colorOffset;
165 VuoMesh_getGPUBuffers(mesh, &vertexCount, &combinedBuffer, &normalOffset, &textureCoordinateOffset, &colorOffset, &elementCount, &elementBuffer);
166
167
168 // Attach the input combinedBuffer for rendering.
169 glBindBuffer(GL_ARRAY_BUFFER, combinedBuffer);
170
171 VuoGlProgram program;
172 if (!VuoShader_activate(sceneObjectRenderer->shader, VuoMesh_getElementAssemblyMethod(mesh), cgl_ctx, &program))
173 {
174 VUserLog("Shader activation failed.");
175 return;
176 }
177
178
179 GLint modelviewMatrixUniform = VuoGlProgram_getUniformLocation(program, "modelviewMatrix");
180 if (modelviewMatrixUniform != -1)
181 glUniformMatrix4fv(modelviewMatrixUniform, 1, GL_FALSE, modelviewMatrix);
182
183
184 GLint modelviewMatrixInverseUniform = VuoGlProgram_getUniformLocation(program, "modelviewMatrixInverse");
185 if (modelviewMatrixInverseUniform != -1)
186 {
187 float modelviewMatrixInverse[16];
188 VuoTransform_invertMatrix4x4(modelviewMatrix, modelviewMatrixInverse);
189 glUniformMatrix4fv(modelviewMatrixInverseUniform, 1, GL_FALSE, modelviewMatrixInverse);
190 }
191
192
193 int stride = sizeof(float) * 3;
194 glEnableVertexAttribArray(attributes.position);
195 glVertexAttribPointer(attributes.position, 3 /* XYZ */, GL_FLOAT, GL_FALSE, stride, (void*)0);
196 bool hasNormals = normalOffset && attributes.normal >= 0;
197 if (hasNormals)
198 {
199 glEnableVertexAttribArray(attributes.normal);
200 glVertexAttribPointer(attributes.normal, 3 /* XYZ */, GL_FLOAT, GL_FALSE, stride, normalOffset);
201 }
202 bool hasTextureCoordinates = textureCoordinateOffset && attributes.textureCoordinate >= 0;
203 if (hasTextureCoordinates)
204 {
205 glEnableVertexAttribArray(attributes.textureCoordinate);
206 glVertexAttribPointer(attributes.textureCoordinate, 2 /* XY */, GL_FLOAT, GL_FALSE, sizeof(float) * 2, textureCoordinateOffset);
207 }
208 bool hasColors = colorOffset && attributes.color >= 0;
209 if (hasColors)
210 {
211 glEnableVertexAttribArray(attributes.color);
212 glVertexAttribPointer(attributes.color, 4 /* RGBA */, GL_FLOAT, GL_FALSE, sizeof(float) * 4, colorOffset);
213 }
214
215
216 // Attach the input elementBuffer for rendering.
217 if (elementBuffer)
218 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
219
220
221 // Create and attach the output combinedBuffer.
222 // The output buffer will always contain all 4 vertex attributes (position, normal, textureCoordinate, color).
223 unsigned long outputVertexCount = VuoMesh_getSplitVertexCount(mesh) * attributes.expectedOutputPrimitiveCount;
224 unsigned long combinedOutputBufferSize = sizeof(float) * (3 + 3 + 2 + 4) * outputVertexCount;
225 GLuint combinedOutputBuffer = VuoGlPool_use(cgl_ctx, VuoGlPool_ArrayBuffer, combinedOutputBufferSize);
226 VuoGlPool_retain(combinedOutputBuffer);
227 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, combinedOutputBuffer);
228// glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, combinedOutputBufferSize, NULL, GL_STATIC_READ);
229// glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 0, combinedOutputBuffer);
230
231 int offset = 0;
232 int size = sizeof(float) * 3 * outputVertexCount;
233 glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 0, combinedOutputBuffer, 0, size);
234 offset += size;
235 glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 1, combinedOutputBuffer, offset, size);
236 offset += size;
237 size = sizeof(float) * 2 * outputVertexCount;
238 glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 2, combinedOutputBuffer, offset, size);
239 offset += size;
240 size = sizeof(float) * 4 * outputVertexCount;
241 glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 3, combinedOutputBuffer, offset, size);
242
243
244
245 // Execute the filter.
246 GLenum mode = VuoMesh_getGlMode(mesh);
247 if (attributes.mayChangeOutputPrimitiveCount)
248 glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT, sceneObjectRenderer->query);
249 glBeginTransformFeedbackEXT(outputPrimitiveGlMode);
250
251#ifdef VUO_PROFILE
252 GLuint timeElapsedQuery;
253 glGenQueries(1, &timeElapsedQuery);
254 glBeginQuery(GL_TIME_ELAPSED_EXT, timeElapsedQuery);
255#endif
256
257 unsigned long completeInputElementCount = VuoMesh_getCompleteElementCount(mesh);
258 if (elementCount)
259 glDrawElements(mode, completeInputElementCount, GL_UNSIGNED_INT, (void*)0);
260 else if (vertexCount)
261 glDrawArrays(mode, 0, completeInputElementCount);
262
263#ifdef VUO_PROFILE
264 double seconds;
265 glEndQuery(GL_TIME_ELAPSED_EXT);
266 GLuint nanoseconds;
267 glGetQueryObjectuiv(timeElapsedQuery, GL_QUERY_RESULT, &nanoseconds);
268 seconds = ((double)nanoseconds) / NSEC_PER_SEC;
269 glDeleteQueries(1, &timeElapsedQuery);
270
271 double objectPercent = seconds / (1./60.) * 100.;
272 VLog("%6.2f %% of 60 Hz frame %s (%s)", objectPercent, sceneObject->name, sceneObjectRenderer->shader->name);
273#endif
274
275
276 glEndTransformFeedbackEXT();
277 if (attributes.mayChangeOutputPrimitiveCount)
278 glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT);
279
280 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 0);
281
282 if (elementBuffer)
283 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
284
285 if (hasColors)
286 glDisableVertexAttribArray(attributes.color);
287 if (hasTextureCoordinates)
288 glDisableVertexAttribArray(attributes.textureCoordinate);
289 if (hasNormals)
290 glDisableVertexAttribArray(attributes.normal);
291 glDisableVertexAttribArray(attributes.position);
292
293 VuoShader_deactivate(sceneObjectRenderer->shader, VuoMesh_getElementAssemblyMethod(mesh), cgl_ctx);
294
295
296 GLuint actualVertexCount = 0;
297 if (attributes.mayChangeOutputPrimitiveCount)
298 {
299 glGetQueryObjectuiv(sceneObjectRenderer->query, GL_QUERY_RESULT, &actualVertexCount);
300 actualVertexCount *= primitiveVertexMultiplier;
301 }
302 else
303 actualVertexCount = outputVertexCount;
304
305
306#if 0 // NOCOMMIT
307 // Print out the result of the filter, for debugging.
308 VLog("inputElements=%lu actualVertexCount=%d normals=%d textureCoordinates=%d colors=%d", completeInputElementCount, actualVertexCount, hasNormals, hasTextureCoordinates, hasColors);
309 glFlush();
310 GLfloat feedback[actualVertexCount * (3 + 3 + 2 + 4)];
311 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, combinedOutputBuffer);
312 glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 0, sizeof(feedback), feedback);
313 for (int i = 0; i < actualVertexCount; ++i)
314 fprintf(stderr, "\t%3d = pos %5.2f %5.2f %5.2f normal %5.2f %5.2f %5.2f tc %5.2f %5.2f color %5.2f %5.2f %5.2f %5.2f\n", i,
315 feedback[i * 3], feedback[i * 3 + 1], feedback[i * 3 + 2],
316 feedback[(outputVertexCount + i) * 3], feedback[(outputVertexCount + i) * 3 + 1], feedback[(outputVertexCount + i) * 3 + 2],
317 feedback[outputVertexCount * (3 + 3) + i * 2], feedback[outputVertexCount * (3 + 3) + i * 2 + 1],
318 feedback[outputVertexCount * (3 + 3 + 2) + i * 4], feedback[outputVertexCount * (3 + 3 + 2) + i * 4 + 1], feedback[outputVertexCount * (3 + 3 + 2) + i * 4 + 2], feedback[outputVertexCount * (3 + 3 + 2) + i * 4 + 3]);
319 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 0);
320#endif
321
322 // https://vuo.org/node/1571
323 // https://b33p.net/kosada/node/12431
324 // The output buffer will always have room for all 4 vertex attributes,
325 // though (depending on input) some might not contain contain useful data.
326
327 // https://b33p.net/kosada/vuo/vuo/-/issues/18103
328 // Though normals are technically optional
329 // (since they're omitted for 2D-only meshes such as that used by VuoImageRenderer),
330 // we expect that all 3D object filters will produce valid normals
331 // (even if they don't use the input mesh's normals),
332 // since composition authors may use a lighting shader or subsequent 3D object filters.
333
335 actualVertexCount, combinedOutputBuffer, combinedOutputBufferSize,
336 (void *)(sizeof(float) * (3 ) * outputVertexCount),
337 hasTextureCoordinates ? (void *)(sizeof(float) * (3 + 3 ) * outputVertexCount) : nullptr,
338 hasColors ? (void *)(sizeof(float) * (3 + 3 + 2) * outputVertexCount) : nullptr,
339 0, 0, 0, // Since transform feedback doesn't provide an elementBuffer, render this submesh using glDrawArrays().
340 outputPrimitiveMode);
343
344 VuoSceneObject_setMesh(sceneObject, newMesh);
345}
346
348#define ELEM(i) (elementCount ? elements[i] : i)
349
353static inline void VuoSceneObjectRenderer_copyElement(VuoMesh mesh, int start, float *source, unsigned int elementCount, unsigned int *elements, VuoMesh_ElementAssemblyMethod elementAssemblyMethod, int verticesPerPrimitive, int floatsPerVertex, float *destination, float *defaultValues)
354{
355 if (!source)
356 {
357 for (int i = 0; i < verticesPerPrimitive * floatsPerVertex; ++i)
358 destination[i] = defaultValues[i % floatsPerVertex];
359 return;
360 }
361
362 if (elementAssemblyMethod == VuoMesh_IndividualTriangles
363 || elementAssemblyMethod == VuoMesh_IndividualLines
364 || elementAssemblyMethod == VuoMesh_Points)
365 {
366 if (elementCount)
367 {
368 unsigned int *e = elements + start;
369 for (int i = 0; i < verticesPerPrimitive; ++i)
370 for (int j = 0; j < floatsPerVertex; ++j)
371 destination[i * floatsPerVertex + j] = source[*(e + i) * floatsPerVertex + j];
372 }
373 else
374 {
375 for (int i = 0; i < verticesPerPrimitive; ++i)
376 for (int j = 0; j < floatsPerVertex; ++j)
377 destination[i * floatsPerVertex + j] = source[(start + i) * floatsPerVertex + j];
378 }
379 }
380 else if (elementAssemblyMethod == VuoMesh_TriangleStrip)
381 {
382 // Expand the triangle strip to individual triangles.
383 if ((start / 3) % 2 == 0)
384 {
385 for (int i = 0; i < verticesPerPrimitive; ++i)
386 for (int j = 0; j < floatsPerVertex; ++j)
387 destination[i * floatsPerVertex + j] = source[ELEM(start/3 + i) * floatsPerVertex + j];
388 }
389 else
390 {
391 for (int j = 0; j < floatsPerVertex; ++j)
392 {
393 destination[0 * floatsPerVertex + j] = source[ELEM(start/3 + 1) * floatsPerVertex + j];
394 destination[1 * floatsPerVertex + j] = source[ELEM(start/3 ) * floatsPerVertex + j];
395 destination[2 * floatsPerVertex + j] = source[ELEM(start/3 + 2) * floatsPerVertex + j];
396 }
397 }
398 }
399 else if (elementAssemblyMethod == VuoMesh_TriangleFan)
400 {
401 // Expand the triangle fan to individual triangles.
402 for (int j = 0; j < floatsPerVertex; ++j)
403 {
404 destination[0 * floatsPerVertex + j] = source[ELEM(0 ) * floatsPerVertex + j];
405 destination[1 * floatsPerVertex + j] = source[ELEM(start/3 + 1) * floatsPerVertex + j];
406 destination[2 * floatsPerVertex + j] = source[ELEM(start/3 + 2) * floatsPerVertex + j];
407 }
408 }
409 else if (elementAssemblyMethod == VuoMesh_LineStrip)
410 {
411 // Expand the line strip to individual lines.
412 for (int j = 0; j < floatsPerVertex; ++j)
413 {
414 destination[0 * floatsPerVertex + j] = source[ELEM(start/2 ) * floatsPerVertex + j];
415 destination[1 * floatsPerVertex + j] = source[ELEM(start/2 + 1) * floatsPerVertex + j];
416 }
417 }
418}
419
424static void VuoSceneObjectRenderer_drawSingleOnCPU(VuoSceneObject sceneObject, float modelviewMatrix[16], VuoSceneObjectRenderer_CPUGeometryOperator cpuGeometryOperator)
425{
426 VuoMesh mesh = VuoSceneObject_getMesh(sceneObject);
427 if (!mesh)
428 return;
429
430 float *inputPositions, *inputNormals, *inputTextureCoordinates, *inputColors;
431 unsigned int elementCount, *elements;
432 VuoMesh_getCPUBuffers(mesh, nullptr, &inputPositions, &inputNormals, &inputTextureCoordinates, &inputColors, &elementCount, &elements);
433
434 float modelMatrixInverse[16];
435 VuoTransform_invertMatrix4x4(modelviewMatrix, modelMatrixInverse);
436
437
438
439// unsigned int vertexCount;
440// VuoMesh_getCPUBuffers(mesh, &vertexCount, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
441// for (int i = 0; i < vertexCount; ++i)
442// VLog(" input[%2d] = %f",i,inputPositions[i]);
443
444
445
446
447 unsigned int inputCount = VuoMesh_getSplitVertexCount(mesh);
448
449 unsigned int allocatedVertices = inputCount;
450 float *newPositions, *newNormals, *newTextureCoordinates, *newColors;
451 VuoMesh_allocateCPUBuffers(allocatedVertices, &newPositions, &newNormals, &newTextureCoordinates, &newColors, 0, nullptr);
452 unsigned int newVertexCount = 0;
453
454 int verticesPerPrimitive;
456 if (elementAssemblyMethod == VuoMesh_IndividualTriangles
457 || elementAssemblyMethod == VuoMesh_TriangleStrip
458 || elementAssemblyMethod == VuoMesh_TriangleFan)
459 verticesPerPrimitive = 3;
460 else if (elementAssemblyMethod == VuoMesh_IndividualLines
461 || elementAssemblyMethod == VuoMesh_LineStrip)
462 verticesPerPrimitive = 2;
463 else if (elementAssemblyMethod == VuoMesh_Points)
464 verticesPerPrimitive = 1;
465
466 float defaultPosition[3] = {0,0,0};
467 float defaultNormal[3] = {0,0,1};
468 float defaultColor[4] = {1,1,1,1};
469
470 for (unsigned int e = 0; e < inputCount; e += verticesPerPrimitive)
471 {
472 if (newVertexCount + VuoSceneObjectRenderer_maxOutputVertices > allocatedVertices)
473 {
474 allocatedVertices *= 2;
475 newPositions = (float *)realloc(newPositions, sizeof(float) * 3 * allocatedVertices);
476 newNormals = (float *)realloc(newNormals, sizeof(float) * 3 * allocatedVertices);
477 newTextureCoordinates = (float *)realloc(newTextureCoordinates, sizeof(float) * 2 * allocatedVertices);
478 newColors = (float *)realloc(newColors, sizeof(float) * 4 * allocatedVertices);
479 }
480
481 float *positions = newPositions + newVertexCount * 3;
482 float *normals = newNormals + newVertexCount * 3;
483 float *textureCoordinates = newTextureCoordinates + newVertexCount * 2;
484 float *colors = newColors + newVertexCount * 4;
485 VuoSceneObjectRenderer_copyElement(mesh, e, inputPositions, elementCount, elements, elementAssemblyMethod, verticesPerPrimitive, 3, positions, defaultPosition);
486 VuoSceneObjectRenderer_copyElement(mesh, e, inputNormals, elementCount, elements, elementAssemblyMethod, verticesPerPrimitive, 3, normals, defaultNormal);
487 VuoSceneObjectRenderer_copyElement(mesh, e, inputTextureCoordinates, elementCount, elements, elementAssemblyMethod, verticesPerPrimitive, 2, textureCoordinates, defaultPosition);
488 VuoSceneObjectRenderer_copyElement(mesh, e, inputColors, elementCount, elements, elementAssemblyMethod, verticesPerPrimitive, 4, colors, defaultColor);
489
490 int vertexCount = verticesPerPrimitive;
491
492// for (int i = 0; i < vertexCount; ++ i)
493// VLog("prim %3d vertex %d pos %f,%f,%f norm=%f,%f,%f tc=%f,%f color=%f,%f,%f,%f",e,i,
494// positions[i*3],positions[i*3+1],positions[i*3+2],
495// normals[i*3],normals[i*3+1],normals[i*3+2],
496// textureCoordinates[i*2],textureCoordinates[i*2+1],
497// colors[i*4],colors[i*4+1],colors[i*4+2],colors[i*4+3]);
498
499 cpuGeometryOperator(modelviewMatrix, modelMatrixInverse, &vertexCount, positions, normals, textureCoordinates, colors);
500
501// for (int i = 0; i < vertexCount; ++ i)
502// VLog("prim %3d vertex %d pos %f,%f,%f norm=%f,%f,%f tc=%f,%f color=%f,%f,%f,%f",e,i,
503// positions[i*3],positions[i*3+1],positions[i*3+2],
504// normals[i*3],normals[i*3+1],normals[i*3+2],
505// textureCoordinates[i*2],textureCoordinates[i*2+1],
506// colors[i*4],colors[i*4+1],colors[i*4+2],colors[i*4+3]);
507
508 if (vertexCount < 0 || vertexCount > VuoSceneObjectRenderer_maxOutputVertices)
509 {
510 VUserLog("Error: cpuGeometryOperator must output between 0 and %d vertices.", VuoSceneObjectRenderer_maxOutputVertices);
511 return;
512 }
513
514 if (vertexCount % verticesPerPrimitive)
515 {
516 VUserLog("Error: When %d vertices are input to cpuGeometryOperator, it must output a multiple of %d vertices.", verticesPerPrimitive, verticesPerPrimitive);
517 return;
518 }
519
520 newVertexCount += vertexCount;
521 }
522
523
524// for (int i = 0; i < newVertexCount; ++i)
525// VLog("output[%2d] = %f",i,newPositions[i]);
526
527
528 bool originalMeshHasTextureCoordinates = inputTextureCoordinates;
529 VuoMesh newMesh = VuoMesh_makeFromCPUBuffers(newVertexCount, newPositions, newNormals, originalMeshHasTextureCoordinates ? newTextureCoordinates : nullptr, newColors,
530 0, nullptr,
531 elementAssemblyMethod);
532 if (!originalMeshHasTextureCoordinates)
533 free(newTextureCoordinates);
536
537 VuoSceneObject_setMesh(sceneObject, newMesh);
538}
539
549{
550 return Block_copy(^(float *modelMatrix, float *modelMatrixInverse, int *vertexCount, float *positions, float *normals, float *textureCoordinates, float *colors) {
551 VuoPoint3d positionInScene[3];
552 VuoPoint3d deformedPositionInScene[3];
553 VuoPoint3d normalInScene[3];
554 VuoPoint2d textureCoordinate[3];
555 for (int i = 0; i < *vertexCount; ++i)
556 {
557 // Keep this in sync with deform.glsl.
558
559 // Position ============================================================
560
561 // Transform into worldspace.
562 VuoPoint3d position = VuoPoint3d_makeFromArray(&positions[i * 3]);
563 VuoPoint3d normal = VuoPoint3d_makeFromArray(&normals[i * 3]);
564 textureCoordinate[i] = VuoPoint2d_makeFromArray(&textureCoordinates[i * 2]);
565 positionInScene[i] = VuoTransform_transformPoint(modelMatrix, position);
566 normalInScene[i] = VuoPoint3d_normalize(VuoTransform_transformVector(modelMatrix, normal));
567
568// VLog("vertex %d pos %f,%f,%f norm=%f,%f,%f",i,
569// positions[i*3],positions[i*3+1],positions[i*3+2],
570// normals[i*3],normals[i*3+1],normals[i*3+2]);
571
572 // Apply the deformation.
573 VuoPoint3d deformedPosition = deform(positionInScene[i],
574 normalInScene[i],
575 textureCoordinate[i]);
576 deformedPositionInScene[i] = deformedPosition;
577
578 // Transform back into modelspace.
579 VuoPoint3d positionInModelspace = VuoTransform_transformPoint(modelMatrixInverse, deformedPosition);
580 VuoPoint3d_setArray(&positions[i * 3], positionInModelspace);
581 }
582
583
584 // Normal ==============================================================
585
586 // First, find the tangent and bitangent vectors at the original (pre-deformation) position.
587 VuoPoint3d tangentInScene[3], bitangentInScene[3];
588 if (*vertexCount == 3)
589 {
590 // Based on "Computing Tangent Space Basis Vectors for an Arbitrary Mesh" by Eric Lengyel,
591 // Terathon Software 3D Graphics Library, 2001.
592 // https://web.archive.org/web/20160306000702/http://www.terathon.com/code/tangent.html
593
594 VuoPoint3d tan1[3], tan2[3];
595 bzero(tan1, sizeof(VuoPoint3d) * 3);
596 bzero(tan2, sizeof(VuoPoint3d) * 3);
597
598 VuoPoint3d v1 = positionInScene[0];
599 VuoPoint3d v2 = positionInScene[1];
600 VuoPoint3d v3 = positionInScene[2];
601
602 VuoPoint2d w1 = textureCoordinate[0];
603 VuoPoint2d w2 = textureCoordinate[1];
604 VuoPoint2d w3 = textureCoordinate[2];
605
606 float x1 = v2.x - v1.x;
607 float x2 = v3.x - v1.x;
608 float y1 = v2.y - v1.y;
609 float y2 = v3.y - v1.y;
610 float z1 = v2.z - v1.z;
611 float z2 = v3.z - v1.z;
612
613 float s1 = w2.x - w1.x;
614 float s2 = w3.x - w1.x;
615 float t1 = w2.y - w1.y;
616 float t2 = w3.y - w1.y;
617
618 float r = 1.0F / (s1 * t2 - s2 * t1);
619 VuoPoint3d sdir = (VuoPoint3d){
620 (t2 * x1 - t1 * x2) * r,
621 (t2 * y1 - t1 * y2) * r,
622 (t2 * z1 - t1 * z2) * r};
623 VuoPoint3d tdir = (VuoPoint3d){
624 (s1 * x2 - s2 * x1) * r,
625 (s1 * y2 - s2 * y1) * r,
626 (s1 * z2 - s2 * z1) * r};
627
628 tan1[0] += sdir;
629 tan1[1] += sdir;
630 tan1[2] += sdir;
631
632 tan2[0] += tdir;
633 tan2[1] += tdir;
634 tan2[2] += tdir;
635
636 for (int i = 0; i < *vertexCount; ++i)
637 {
638 VuoPoint3d n = normalInScene[i];
639 VuoPoint3d t = tan1[i];
640 VuoPoint3d t2 = tan2[i];
641
642 // Gram-Schmidt orthogonalize
643 tangentInScene[i] = VuoPoint3d_normalize(t - n * VuoPoint3d_dotProduct(n, t));
644 bitangentInScene[i] = VuoPoint3d_normalize(t2 - n * VuoPoint3d_dotProduct(n, t2));
645 }
646 }
647 else
648 for (int i = 0; i < *vertexCount; ++i)
649 {
650 tangentInScene[i] = (VuoPoint3d){1,0,0};
651 bitangentInScene[i] = (VuoPoint3d){0,1,0};
652 }
653
654 // Next, apply the deformation to neighboring positions.
655 // Based on https://web.archive.org/web/20170202071451/https://http.developer.nvidia.com/GPUGems/gpugems_ch42.html
656 // section "An Approximate Numerical Technique".
657 for (int i = 0; i < *vertexCount; ++i)
658 {
659 const float scale = .01;
660 VuoPoint3d deformedAlongTangent = deform(positionInScene[i] + tangentInScene[i] * scale,
661 normalInScene[i],
662 textureCoordinate[i] + (VuoPoint2d){scale, 0});
663 VuoPoint3d deformedAlongBitangent = deform(positionInScene[i] + bitangentInScene[i] * scale,
664 normalInScene[i],
665 textureCoordinate[i] + (VuoPoint2d){0, scale});
666
667 VuoPoint3d deformedPosition = deformedPositionInScene[i];
668
669 // Calculate the orthonormal basis of the tangent plane at deformedPosition.
670 VuoPoint3d deformedTangent = deformedAlongTangent - deformedPosition;
671 VuoPoint3d deformedBitangent = deformedAlongBitangent - deformedPosition;
672 VuoPoint3d deformedNormal = VuoPoint3d_crossProduct(deformedTangent, deformedBitangent);
673
674 // Transform back into modelspace.
675 VuoPoint3d deformedNormalInModelspace = VuoPoint3d_normalize(VuoTransform_transformVector(modelMatrixInverse, deformedNormal));
676
677 VuoPoint3d_setArray(&normals[i * 3], deformedNormalInModelspace);
678 }
679 });
680}
681
688{
689 static bool gpuTransformFeedback;
690 static dispatch_once_t once = 0;
691 dispatch_once(&once, ^{
692 // If the user set the `gpuTransformFeedback` preference, use it.
693 Boolean overridden = false;
694 gpuTransformFeedback = (int)CFPreferencesGetAppIntegerValue(CFSTR("gpuTransformFeedback"), CFSTR("org.vuo.Editor"), &overridden);
695
696 if (!overridden)
697 {
698 // https://b33p.net/kosada/node/13622
699 // Some GPUs / GPU drivers have broken support for transform feedback,
700 // so use the CPU fallbacks instead.
701 __block const char *renderer;
702 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
703 renderer = (const char *)glGetString(GL_RENDERER);
704 });
705
706 if (strncmp(renderer, "Intel", 5) == 0
707 || strncmp(renderer, "AMD ", 4) == 0
708 || strncmp(renderer, "NVIDIA GeForce 9400M", 4) == 0
709 || strncmp(renderer, "Apple ", 6) == 0
710 || strncmp(renderer, "ATI ", 4) == 0)
711 gpuTransformFeedback = false;
712 else
713 // NVIDIA (except 9400M) seems to be the only GPU whose transform feedback works consistently.
714 gpuTransformFeedback = true;
715 }
716
717 VUserLog("gpuTransformFeedback = %d", gpuTransformFeedback);
718 });
719
720 return gpuTransformFeedback;
721}
722
740{
741 if (!sor)
742 return nullptr;
743
745 {
746 __block VuoSceneObject sceneObjectCopy = VuoSceneObject_copy(sceneObject);
747
748 struct VuoSceneObjectRendererInternal *sceneObjectRenderer = (struct VuoSceneObjectRendererInternal *)sor;
749 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
750 glEnable(GL_RASTERIZER_DISCARD_EXT);
751 glBindVertexArray(sceneObjectRenderer->vertexArray);
752 glBindFramebuffer(GL_FRAMEBUFFER, sceneObjectRenderer->shamFramebuffer);
753
754 VuoSceneObject_apply(sceneObjectCopy, ^(VuoSceneObject currentObject, float modelviewMatrix[16]) {
755 VuoSceneObjectRenderer_drawSingle(cgl_ctx, sceneObjectRenderer, currentObject, modelviewMatrix);
756 });
757
758 glBindBuffer(GL_ARRAY_BUFFER, 0);
759 glBindFramebuffer(GL_FRAMEBUFFER, 0);
761 glDisable(GL_RASTERIZER_DISCARD_EXT);
762
763 // Ensure commands are submitted before we try to use the generated object on another context.
764 // https://b33p.net/kosada/node/10467
765 glFlushRenderAPPLE();
766 });
767
768 return sceneObjectCopy;
769 }
770
771 else
772 {
773 VuoSceneObject sceneObjectCopy = VuoSceneObject_copy(sceneObject);
774
775 VuoSceneObject_apply(sceneObjectCopy, ^(VuoSceneObject currentObject, float modelviewMatrix[16]) {
776 VuoSceneObjectRenderer_drawSingleOnCPU(currentObject, modelviewMatrix, cpuGeometryOperator);
777 });
778
779 return sceneObjectCopy;
780 }
781}
782
789{
790 struct VuoSceneObjectRendererInternal *sceneObjectRenderer = (struct VuoSceneObjectRendererInternal *)sor;
791
793 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
794 VuoRelease(sceneObjectRenderer->shader);
795
796 glBindFramebuffer(GL_FRAMEBUFFER, sceneObjectRenderer->shamFramebuffer);
797 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
798 VuoGlTexture_release(VuoGlTexturePool_Allocate, GL_TEXTURE_2D, GL_RGBA, 1, 1, sceneObjectRenderer->shamTexture);
799// glBindFramebuffer(GL_FRAMEBUFFER, 0); // handled by glDeleteFramebuffers
800 glDeleteFramebuffers(1, &sceneObjectRenderer->shamFramebuffer);
801
802 glDeleteVertexArrays(1, &sceneObjectRenderer->vertexArray);
803
804 glDeleteQueries(1, &sceneObjectRenderer->query);
805 });
806
807 free(sceneObjectRenderer);
808}