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