Vuo  2.4.0
VuoShaderFile.cc
Go to the documentation of this file.
1
10#include "VuoShaderFile.hh"
11
12#include <regex.h>
13#include <sstream>
15#include "VuoException.hh"
16#include "VuoJsonUtilities.hh"
17#include "VuoStringUtilities.hh"
18#include "VuoShaderIssues.hh"
19
24{
25 init(getTemplate(type));
26}
27
32{
33 string path = VuoFileUtilities::getVuoFrameworkPath() + "/Resources";
34
35 string filename;
36 if (type == GLSLImageFilter)
37 filename = "GLSLImageFilter.fs";
38 else if (type == GLSLImageGenerator)
39 filename = "GLSLImageGenerator.fs";
40 else if (type == GLSLImageTransition)
41 filename = "GLSLImageTransition.fs";
42 else if (type == GLSLObjectRenderer)
43 filename = "GLSLObjectRenderer.fs";
44 else // if (type == GLSLObjectFilter)
45 filename = "GLSLObjectFilter.vs";
46
47 return VuoFileUtilities::File(path, filename);
48}
49
54{
55 if (stage == Vertex)
56 return "Vertex";
57 else if (stage == Geometry)
58 return "Geometry";
59 else if (stage == Fragment)
60 return "Fragment";
61 else // if (stage == Program)
62 return "Program";
63}
64
73VuoShaderFile::VuoShaderFile(VuoFileUtilities::File file, const string &overriddenFragmentSource)
74{
75 basename = file.basename();
76 extension = file.extension();
77 init(file, overriddenFragmentSource);
78}
79
80void VuoShaderFile::init(VuoFileUtilities::File file, const string &overriddenFragmentSource)
81{
82 _showsTime = false;
83
84 json_object *isfMetadata = NULL;
85
86 splitMetadataAndSource(readStage(file.fileWithDifferentExtension("vs")), &isfMetadata, _vertexSource);
87 splitMetadataAndSource(readStage(file.fileWithDifferentExtension("gs")), &isfMetadata, _geometrySource);
88 splitMetadataAndSource(! overriddenFragmentSource.empty() ? overriddenFragmentSource : readStage(file.fileWithDifferentExtension("fs")), &isfMetadata, _fragmentSource);
89
90 parseMetadata(isfMetadata);
91}
92
97{
98 VUserLog("\"%s\"", _name.c_str());
99 VUserLog(" type: %s", typeName().c_str());
100 VUserLog(" isfVersion: %s", isfVersion.c_str());
101 VUserLog(" shaderVersion: %s", shaderVersion.c_str());
102 VUserLog(" copyright: %s",copyright.c_str());
103 VUserLog(" license: %s",license.c_str());
104 VUserLog(" description: %s",description.c_str());
105 VUserLog(" homepageLink: %s", homepageLink.c_str());
106 VUserLog(" documentationLink: %s", documentationLink.c_str());
107 VUserLog(" categories:");
108 for (vector<string>::iterator i = categories.begin(); i != categories.end(); ++i)
109 VUserLog(" %s", i->c_str());
110 VUserLog(" keywords:");
111 for (vector<string>::iterator i = keywords.begin(); i != keywords.end(); ++i)
112 VUserLog(" %s", i->c_str());
113 VUserLog(" inputs:");
114 for (vector<Port>::iterator i = _inputPorts.begin(); i != _inputPorts.end(); ++i)
115 VUserLog(" %s %s %s", i->vuoTypeName.c_str(), i->key.c_str(), json_object_to_json_string(i->vuoPortDetails));
116 VUserLog(" showsTime : %d", _showsTime);
117 VUserLog(" outputKey : %s", outputKey.c_str());
118 VUserLog(" outputName: %s", outputName.c_str());
119 VUserLog("VS [%s]",_vertexSource.c_str());
120 VUserLog("GS [%s]",_geometrySource.c_str());
121 VUserLog("FS [%s]",_fragmentSource.c_str());
122 VUserLog("VuoModuleMetadata %s",json_object_to_json_string(vuoModuleMetadata()));
123}
124
129{
130 return _name;
131}
132
137{
138 return _type;
139}
140
145{
146 if (_type == VuoShaderFile::GLSLImageFilter)
147 return "Image Filter";
148 else if (_type == VuoShaderFile::GLSLImageGenerator)
149 return "Image Generator";
150 else if (_type == VuoShaderFile::GLSLImageTransition)
151 return "Image Transition";
152 else if (_type == VuoShaderFile::GLSLObjectRenderer)
153 return "3D Object Renderer";
154 else
155 return "3D Object Filter";
156}
157
162{
163 if (_type == VuoShaderFile::GLSLImageFilter
164 || _type == VuoShaderFile::GLSLImageGenerator
165 || _type == VuoShaderFile::GLSLImageTransition)
166 return "IMAGE";
167 else if (_type == VuoShaderFile::GLSLObjectRenderer)
168 return "OBJECT_RENDER";
169 else
170 return "OBJECT_FILTER";
171}
172
177{
178 return _type == VuoShaderFile::GLSLObjectRenderer
179 || _type == VuoShaderFile::GLSLObjectFilter;
180}
181
186{
187 return _type == VuoShaderFile::GLSLImageFilter
188 || _type == VuoShaderFile::GLSLImageGenerator
189 || _type == VuoShaderFile::GLSLImageTransition
190 || _type == VuoShaderFile::GLSLObjectRenderer;
191}
192
197{
199 metadata->setDefaultName("VuoShader");
200 metadata->setName(_name);
201 metadata->setVersion(shaderVersion);
202 metadata->setCopyright(copyright);
203 metadata->setLicense(license);
204 metadata->setDescription(description);
205 metadata->setHomepageURL(homepageLink);
206 metadata->setDocumentationURL(documentationLink);
207 metadata->setKeywords(keywords);
208 metadata->setCategories(categories);
209 return metadata;
210}
211
216{
217 string customizedName = metadata->getCustomizedName();
218 if (! customizedName.empty())
219 _name = customizedName;
220
221 shaderVersion = metadata->getVersion();
222 copyright = metadata->getCopyright();
223 license = metadata->getLicense();
224 description = metadata->getDescription();
225 homepageLink = metadata->getHomepageURL();
226 documentationLink = metadata->getDocumentationURL();
227 keywords = metadata->getKeywords();
228 categories = metadata->getCategories();
229}
230
235{
236 return _vuoModuleMetadata;
237}
238
243{
244 _vuoModuleMetadata = metadata;
245}
246
250vector<VuoShaderFile::Port> VuoShaderFile::inputPorts()
251{
252 return _inputPorts;
253}
254
258void VuoShaderFile::setInputPorts(vector<VuoShaderFile::Port> ports)
259{
260 _inputPorts = ports;
261}
262
268{
269 return _showsTime;
270}
271
276{
277 json_object *vuoPortDetails = json_object_new_object();
278 json_object_object_add(vuoPortDetails, "name", json_object_new_string(outputName.c_str()));
279 return (Port){outputKey, "", vuoPortDetails};
280}
281
287{
288 return _vertexSource;
289}
290
294void VuoShaderFile::setVertexSource(const string &source)
295{
296 _vertexSource = source;
297}
298
299void VuoShaderFile::insertPreamble(ostringstream &oss, bool isFragment)
300{
301 oss << "#version 120\n"
302
303 // Ensure errors in the preamble don't overlap with errors in user-entered code.
304 << "#line " << VuoShaderIssues::PreambleLine << "\n"
305
306 "\n"
307 "// Standard ISF variables\n"
308
309 "varying vec2 isf_FragNormCoord;\n"
310// "varying vec3 isf_VertNorm;\n"
311// "varying vec3 isf_VertPos;\n"
312
313 "uniform int PASSINDEX;\n"
314
315// "uniform vec2 RENDERSIZE;\n"
316 "uniform vec2 viewportSize;\n"
317 "#define RENDERSIZE viewportSize\n"
318
319 "uniform float TIME;\n"
320 "uniform float TIMEDELTA;\n"
321 "uniform vec4 DATE;\n"
322 "uniform int FRAMEINDEX;\n"
323
324 // ISF v1
325 "#define vv_FragNormCoord isf_FragNormCoord\n"
326 "#define vv_vertShaderInit isf_vertShaderInit\n";
327
328 oss << "\n"
329 "// Input ports\n";
330 for (vector<Port>::iterator it = _inputPorts.begin(); it != _inputPorts.end(); ++it)
331 oss << glslDeclarationForPort(*it);
332
333 if (isFragment)
334 oss << "\n"
335 "#include \"ISFGLMacro2D.txt\"\n"
336// "#include \"ISFGLMacro2DBias.txt\"\n"
337 "#include \"ISFGLMacro2DRect.txt\"\n"
338// "#include \"ISFGLMacro2DRectBias.txt\"\n"
339 "\n";
340}
341
342
348{
349 ostringstream oss;
350
351 insertPreamble(oss, false);
352
353 oss << "#include \"ISFGLSceneVertShaderIncludeVarDec.txt\"\n"
354 << "void isf_vertShaderInit(void)\n"
355 << "{\n"
356 << "\t#include \"ISFGLSceneVertShaderIncludeInitFunc.txt\"\n"
357 // @@@ set _texCoord + _normTexCoord
358 << "}\n";
359
360 if (_vertexSource.empty())
361 oss << "#include \"ISFGLScenePassthru.vs\"";
362 else
363 oss << "#line 0\n"
364 << _vertexSource;
365
366 return oss.str();
367}
368
374{
375 return _geometrySource;
376}
377
381void VuoShaderFile::setGeometrySource(const string &source)
382{
383 _geometrySource = source;
384}
385
392{
393 if (_geometrySource.empty())
394 return "";
395
396 ostringstream oss;
397
398 insertPreamble(oss, false);
399
400 oss << "#line 0\n"
401 << _geometrySource;
402
403 return oss.str();
404}
405
411{
412 return _fragmentSource;
413}
414
418void VuoShaderFile::setFragmentSource(const string &source)
419{
420 _fragmentSource = source;
421}
422
429{
430 if (_type == GLSLObjectFilter)
431 return "";
432
433 ostringstream oss;
434
435 insertPreamble(oss, true);
436
437 oss << "#line 0\n"
438 << _fragmentSource;
439
440 return oss.str();
441}
442
450void VuoShaderFile::save(string filePath)
451{
452 string basename = filePath.substr(0, filePath.find_last_of('.'));
453
454 string vertexFile = basename + ".vs";
455 string geometryFile = basename + ".gs";
456 string fragmentFile = basename + ".fs";
457
458 if (_type == GLSLImageFilter
459 || _type == GLSLImageGenerator
460 || _type == GLSLImageTransition
461 || _type == GLSLObjectRenderer)
462 {
463 saveStage(vertexFile, _vertexSource);
464 saveStage(geometryFile, _geometrySource);
465 saveStage(fragmentFile, _fragmentSource, _vuoModuleMetadata);
466 }
467 else // if (type == GLSLObjectFilter)
468 {
469 saveStage(vertexFile, _vertexSource, _vuoModuleMetadata);
470 saveStage(geometryFile, _geometrySource);
471 string empty = "";
472 saveStage(fragmentFile, empty); // Delete stale file.
473 }
474}
475
480{
481 return stageFileContents(_fragmentSource, _vuoModuleMetadata);
482}
483
484string VuoShaderFile::readStage(VuoFileUtilities::File file)
485{
486 if (!file.exists())
487 return "";
488
489 return file.getContentsAsString();
490}
491
492void VuoShaderFile::splitMetadataAndSource(string inputString, json_object **outputMetadata, string &outputSourceString)
493{
494 regex_t metadataRegex;
495 regcomp(&metadataRegex, "/\\*[\\s\r\n]*{.*}[\\s\r\n]*\\*/", REG_EXTENDED);
496 size_t nmatch = 1;
497 regmatch_t pmatch[nmatch];
498 bool metadataFound = !regexec(&metadataRegex, inputString.c_str(), nmatch, pmatch, 0);
499 regfree(&metadataRegex);
500 if (metadataFound)
501 {
502 string metadataString = inputString.substr(pmatch[0].rm_so + 2, (pmatch[0].rm_eo - 1) - pmatch[0].rm_so - 3);
503 outputSourceString = inputString.substr(0, pmatch[0].rm_so) + inputString.substr(pmatch[0].rm_eo);
504
505 json_tokener_error error;
506 *outputMetadata = json_tokener_parse_verbose(metadataString.c_str(), &error);
507 if (!*outputMetadata)
508 {
509 VUserLog("Error: The metadata header isn't valid JSON: %s", json_tokener_error_desc(error));
510 return;
511 }
512 }
513 else
514 outputSourceString = inputString;
515
516 outputSourceString = VuoStringUtilities::trim(outputSourceString);
517
518 // Check whether the `TIME` uniform is used.
519 if (!_showsTime)
520 {
521 string::size_type len = outputSourceString.length();
522 string::size_type pos = 0;
523 while ( (pos = outputSourceString.find("TIME", pos)) != string::npos )
524 if ( (pos > 1) && !isalnum(outputSourceString[pos - 1])
525 && (pos + 4 < len) && !isalnum(outputSourceString[pos + 4]) )
526 {
527 _showsTime = true;
528 break;
529 }
530 else
531 pos += 4;
532 }
533}
534
535void VuoShaderFile::parseMetadata(json_object *metadata)
536{
537 string normalizedBasename = basename;
538 while (VuoStringUtilities::endsWith(normalizedBasename, "." + extension))
539 normalizedBasename = normalizedBasename.substr(0, normalizedBasename.length() - extension.length() - 1);
540
541 isfVersion = VuoJsonUtilities::parseString(metadata, "ISFVSN", "1.0");
542 string typeString = VuoJsonUtilities::parseString(metadata, "TYPE", "IMAGE");
543 _name = VuoJsonUtilities::parseString(metadata, "LABEL");
544 shaderVersion = VuoJsonUtilities::parseString(metadata, "VSN");
545 copyright = VuoJsonUtilities::parseString(metadata, "CREDIT");
546 license = VuoJsonUtilities::parseString(metadata, "LICENSE");
547 description = VuoJsonUtilities::parseString(metadata, "DESCRIPTION");
548 homepageLink = VuoJsonUtilities::parseObjectString(metadata, "LINKS", "HOMEPAGE");
549 documentationLink = VuoJsonUtilities::parseObjectString(metadata, "LINKS", "DOCUMENTATION");
550 categories = VuoJsonUtilities::parseArrayOfStrings(metadata, "CATEGORIES");
551 keywords = VuoJsonUtilities::parseArrayOfStrings(metadata, "KEYWORDS");
552 examples = VuoJsonUtilities::parseArrayOfStrings(metadata, "EXAMPLES");
553
554 json_object *passes;
555 if (json_object_object_get_ex(metadata, "PASSES", &passes)
556 && ((json_object_is_type(passes, json_type_array ) && json_object_array_length(passes))
557 || (json_object_is_type(passes, json_type_object) && json_object_object_length(passes))))
558 throw VuoException("This shader uses ISF's multipass feature, which Vuo doesn't support yet.");
559
560 json_object *imported;
561 if (json_object_object_get_ex(metadata, "IMPORTED", &imported)
562 && ((json_object_is_type(imported, json_type_array ) && json_object_array_length(imported))
563 || (json_object_is_type(imported, json_type_object) && json_object_object_length(imported))))
564 throw VuoException("This shader uses ISF's image-importing feature, which Vuo doesn't support yet.");
565
566 bool hasInputImage = false;
567 bool hasStartImage = false;
568 bool hasEndImage = false;
569 bool hasProgress = false;
570 json_object *inputsArray;
571 if (json_object_object_get_ex(metadata, "INPUTS", &inputsArray))
572 {
573 int inputCount = json_object_array_length(inputsArray);
574 for (int i = 0; i < inputCount; ++i)
575 {
576 json_object *inputObject = json_object_array_get_idx(inputsArray, i);
577 string key = VuoJsonUtilities::parseString(inputObject, "NAME");
578 string type = VuoJsonUtilities::parseString(inputObject, "TYPE");
579 string name = VuoJsonUtilities::parseString(inputObject, "LABEL");
580
581 if (key == "inputImage")
582 hasInputImage = true;
583 else if (key == "startImage")
584 hasStartImage = true;
585 else if (key == "endImage")
586 hasEndImage = true;
587 else if (key == "progress")
588 hasProgress = true;
589
590 string vuoType = vuoTypeForIsfType(type);
591 if (vuoType.empty())
592 {
593 VUserLog("Warning: Input port '%s' has unknown type '%s'.", key.c_str(), type.c_str());
594 continue;
595 }
596
597 json_object *vuoPortDetails = json_object_new_object();
598
599 if (name.empty() && key == "inputImage")
600 name = "Image";
601
602 if (!name.empty())
603 json_object_object_add(vuoPortDetails, "name", json_object_new_string(name.c_str()));
604
605 json_object *defaultValue = NULL;
606 if (json_object_object_get_ex(inputObject, "DEFAULT", &defaultValue))
607 json_object_object_add(vuoPortDetails, "default", defaultValue);
608
609 json_object *minValue = NULL;
610 if (json_object_object_get_ex(inputObject, "MIN", &minValue))
611 json_object_object_add(vuoPortDetails, "suggestedMin", minValue);
612 else
613 {
614 if (vuoType == "VuoReal")
615 json_object_object_add(vuoPortDetails, "suggestedMin", json_object_new_double(0));
616 else if (vuoType == "VuoPoint2d")
617 {
618 json_object_object_add(vuoPortDetails, "scaleToSamplerRect", json_object_new_boolean(true));
619 json_object_object_add(vuoPortDetails, "suggestedMin", json_object_new_string("0,0"));
620 }
621 }
622
623 json_object *maxValue = NULL;
624 if (json_object_object_get_ex(inputObject, "MAX", &maxValue))
625 json_object_object_add(vuoPortDetails, "suggestedMax", maxValue);
626 else
627 {
628 if (vuoType == "VuoReal")
629 json_object_object_add(vuoPortDetails, "suggestedMax", json_object_new_double(1));
630 else if (vuoType == "VuoPoint2d")
631 {
632 json_object_object_add(vuoPortDetails, "scaleToSamplerRect", json_object_new_boolean(true));
633 json_object_object_add(vuoPortDetails, "suggestedMax", json_object_new_string("1,1"));
634 }
635 }
636
637 json_object *stepValue = NULL;
638 if (json_object_object_get_ex(inputObject, "STEP", &stepValue))
639 json_object_object_add(vuoPortDetails, "suggestedStep", stepValue);
640
641 if (VuoStringUtilities::beginsWith(vuoType, "VuoList_"))
642 {
643 json_object *maxItemsValue = NULL;
644 if (json_object_object_get_ex(inputObject, "MAX_ITEMS", &maxItemsValue))
645 json_object_object_add(vuoPortDetails, "maxItems", maxItemsValue);
646 }
647
648 json_object *values = NULL;
649 json_object *labels = NULL;
650 if (json_object_object_get_ex(inputObject, "VALUES", &values)
651 && json_object_object_get_ex(inputObject, "LABELS", &labels))
652 {
653 json_object *menuItems = json_object_new_array();
654 int valueCount = json_object_array_length(values);
655 for (int i = 0; i < valueCount; ++i)
656 {
657 json_object *menuItem = json_object_new_object();
658 json_object_object_add(menuItem, "value", json_object_array_get_idx(values, i));
659 json_object_object_add(menuItem, "name", json_object_array_get_idx(labels, i));
660 json_object_array_add(menuItems, menuItem);
661 }
662 json_object_object_add(vuoPortDetails, "menuItems", menuItems);
663 }
664
665 _inputPorts.push_back((Port){ key, vuoType, vuoPortDetails });
666 }
667 }
668
669 json_object *outputsArray;
670 if (json_object_object_get_ex(metadata, "OUTPUTS", &outputsArray))
671 {
672 int outputCount = json_object_array_length(outputsArray);
673 for (int i = 0; i < outputCount; ++i)
674 {
675 json_object *outputObject = json_object_array_get_idx(outputsArray, i);
676 outputKey = VuoJsonUtilities::parseString(outputObject, "NAME");
677 outputName = VuoJsonUtilities::parseString(outputObject, "LABEL");
678 }
679 }
680
681 if (typeString == "IMAGE")
682 {
683 if (hasInputImage)
684 _type = GLSLImageFilter;
685 else if (hasStartImage && hasEndImage && hasProgress)
686 _type = GLSLImageTransition;
687 else
688 _type = GLSLImageGenerator;
689
690 if (outputKey.empty())
691 outputKey = "outputImage";
692 if (outputName.empty())
693 outputName = VuoStringUtilities::expandCamelCase(outputKey);
694 }
695 else if (typeString == "OBJECT_RENDER")
696 {
697 _type = GLSLObjectRenderer;
698
699 if (outputKey.empty())
700 outputKey = "shader";
701 if (outputName.empty())
702 outputName = VuoStringUtilities::expandCamelCase(outputKey);
703 }
704 else if (typeString == "OBJECT_FILTER")
705 {
706 _type = GLSLObjectFilter;
707
708 if (outputKey.empty())
709 outputKey = "outputObject";
710 if (outputName.empty())
711 outputName = VuoStringUtilities::expandCamelCase(outputKey);
712 }
713
714 _vuoModuleMetadata = json_object_new_object();
715
716 string title = (! _name.empty() ? _name : normalizedBasename);
717 json_object_object_add(_vuoModuleMetadata, "title", json_object_new_string(title.c_str()));
718
719 string metadataDescription = description;
720 if (!copyright.empty())
721 metadataDescription += "\n\n" + copyright;
722 if (!metadataDescription.empty())
723 json_object_object_add(_vuoModuleMetadata, "description", json_object_new_string(metadataDescription.c_str()));
724
725 if (!shaderVersion.empty())
726 json_object_object_add(_vuoModuleMetadata, "version", json_object_new_string(shaderVersion.c_str()));
727 if (!keywords.empty())
728 json_object_object_add(_vuoModuleMetadata, "keywords", VuoJsonUtilities::getJson(keywords));
729
730 if (!examples.empty())
731 {
732 json_object *nodeMetadataJson = json_object_new_object();
733 json_object_object_add(nodeMetadataJson, "exampleCompositions", VuoJsonUtilities::getJson(examples));
734 json_object_object_add(_vuoModuleMetadata, "node", nodeMetadataJson);
735 }
736
737 if (_type == VuoShaderFile::GLSLImageFilter
738 || _type == VuoShaderFile::GLSLImageGenerator
739 || _type == VuoShaderFile::GLSLImageTransition
740 || _type == VuoShaderFile::GLSLObjectFilter)
741 {
742 json_object *dependenciesJson = json_object_new_array();
743
744 if (_type == VuoShaderFile::GLSLImageFilter
745 || _type == VuoShaderFile::GLSLImageGenerator
746 || _type == VuoShaderFile::GLSLImageTransition)
747 json_object_array_add(dependenciesJson, json_object_new_string("VuoImageRenderer"));
748 else if (_type == VuoShaderFile::GLSLObjectFilter)
749 json_object_array_add(dependenciesJson, json_object_new_string("VuoSceneObjectRenderer"));
750
751 json_object_object_add(_vuoModuleMetadata, "dependencies", dependenciesJson);
752 }
753}
754
755string VuoShaderFile::vuoTypeForIsfType(string isfType)
756{
757 if (isfType == "event")
758 return "event";
759
760 else if (isfType == "bool")
761 return "VuoBoolean";
762 else if (isfType == "long")
763 return "VuoInteger";
764 else if (isfType == "float")
765 return "VuoReal";
766 else if (isfType == "point2D")
767 return "VuoPoint2d";
768 else if (isfType == "point3D")
769 return "VuoPoint3d";
770 else if (isfType == "point4D")
771 return "VuoPoint4d";
772 else if (isfType == "color")
773 return "VuoColor";
774
775 else if (isfType == "bool[]")
776 return "VuoList_VuoBoolean";
777 else if (isfType == "long[]")
778 return "VuoList_VuoInteger";
779 else if (isfType == "float[]")
780 return "VuoList_VuoReal";
781 else if (isfType == "point2D[]")
782 return "VuoList_VuoPoint2d";
783 else if (isfType == "point3D[]")
784 return "VuoList_VuoPoint3d";
785 else if (isfType == "point4D[]")
786 return "VuoList_VuoPoint4d";
787 else if (isfType == "color[]")
788 return "VuoList_VuoColor";
789
790 else if (isfType == "image")
791 return "VuoImage";
792
793 else if (isfType == "size")
794 return "VuoSize";
795 else if (isfType == "colorDepth")
796 return "VuoImageColorDepth";
797
798 return "";
799}
800
801string VuoShaderFile::isfTypeForVuoType(string vuoType)
802{
803 if (vuoType == "event")
804 return "event";
805 else if (vuoType == "VuoBoolean")
806 return "bool";
807 else if (vuoType == "VuoInteger")
808 return "long";
809 else if (vuoType == "VuoReal")
810 return "float";
811 else if (vuoType == "VuoPoint2d")
812 return "point2D";
813 else if (vuoType == "VuoPoint3d")
814 return "point3D";
815 else if (vuoType == "VuoPoint4d")
816 return "point4D";
817 else if (vuoType == "VuoColor")
818 return "color";
819
820 else if (vuoType == "VuoList_VuoBoolean")
821 return "bool[]";
822 else if (vuoType == "VuoList_VuoInteger")
823 return "long[]";
824 else if (vuoType == "VuoList_VuoReal")
825 return "float[]";
826 else if (vuoType == "VuoList_VuoPoint2d")
827 return "point2D[]";
828 else if (vuoType == "VuoList_VuoPoint3d")
829 return "point3D[]";
830 else if (vuoType == "VuoList_VuoPoint4d")
831 return "point4D[]";
832 else if (vuoType == "VuoList_VuoColor")
833 return "color[]";
834
835 else if (vuoType == "VuoImage")
836 return "image";
837
838 else if (vuoType == "VuoSize")
839 return "size";
840 else if (vuoType == "VuoImageColorDepth")
841 return "colorDepth";
842
843 return "";
844}
845
850{
851 set<string> types;
852
853 types.insert("VuoBoolean");
854 types.insert("VuoInteger");
855 types.insert("VuoReal");
856 types.insert("VuoPoint2d");
857 types.insert("VuoPoint3d");
858 types.insert("VuoPoint4d");
859 types.insert("VuoColor");
860
861 types.insert("VuoList_VuoBoolean");
862 types.insert("VuoList_VuoInteger");
863 types.insert("VuoList_VuoReal");
864 types.insert("VuoList_VuoPoint2d");
865 types.insert("VuoList_VuoPoint3d");
866 types.insert("VuoList_VuoPoint4d");
867 types.insert("VuoList_VuoColor");
868
869 types.insert("VuoImage");
870 types.insert("VuoSize");
871 types.insert("VuoImageColorDepth");
872
873 return types;
874}
875
876string VuoShaderFile::glslDeclarationForPort(Port port)
877{
878 string maxItems = to_string(VuoJsonUtilities::parseInt(port.vuoPortDetails, "maxItems", 16));
879
880 if (port.vuoTypeName == "event"
881 || port.vuoTypeName == "VuoBoolean")
882 return "uniform bool " + port.key + ";\n";
883 else if (port.vuoTypeName == "VuoInteger")
884 return "uniform int " + port.key + ";\n";
885 else if (port.vuoTypeName == "VuoReal")
886 return "uniform float " + port.key + ";\n";
887 else if (port.vuoTypeName == "VuoPoint2d")
888 return "uniform vec2 " + port.key + ";\n";
889 else if (port.vuoTypeName == "VuoPoint3d")
890 return "uniform vec3 " + port.key + ";\n";
891 else if (port.vuoTypeName == "VuoPoint4d")
892 return "uniform vec4 " + port.key + ";\n";
893 else if (port.vuoTypeName == "VuoColor")
894 return "uniform vec4 " + port.key + ";\n";
895
896 else if (port.vuoTypeName == "VuoList_VuoBoolean")
897 return "uniform bool " + port.key + "[" + maxItems + "];\n"
898 "uniform int _" + port.key + "_length;\n";
899 else if (port.vuoTypeName == "VuoList_VuoInteger")
900 return "uniform int " + port.key + "[" + maxItems + "];\n"
901 "uniform int _" + port.key + "_length;\n";
902 else if (port.vuoTypeName == "VuoList_VuoReal")
903 return "uniform float " + port.key + "[" + maxItems + "];\n"
904 "uniform int _" + port.key + "_length;\n";
905 else if (port.vuoTypeName == "VuoList_VuoPoint2d")
906 return "uniform vec2 " + port.key + "[" + maxItems + "];\n"
907 "uniform int _" + port.key + "_length;\n";
908 else if (port.vuoTypeName == "VuoList_VuoPoint3d")
909 return "uniform vec3 " + port.key + "[" + maxItems + "];\n"
910 "uniform int _" + port.key + "_length;\n";
911 else if (port.vuoTypeName == "VuoList_VuoPoint4d")
912 return "uniform vec4 " + port.key + "[" + maxItems + "];\n"
913 "uniform int _" + port.key + "_length;\n";
914 else if (port.vuoTypeName == "VuoList_VuoColor")
915 return "uniform vec4 " + port.key + "[" + maxItems + "];\n"
916 "uniform int _" + port.key + "_length;\n";
917
918 else if (port.vuoTypeName == "VuoImage")
919 return "//uniform VuoImage " + port.key + ";\n" // We don't know the sampler type until execution time, so use a placeholder.
920 "uniform vec4 _" + port.key + "_imgRect;\n"
921 "uniform vec2 _" + port.key + "_imgSize;\n"
922 "uniform bool _" + port.key + "_flip;\n";
923
924 return "";
925}
926
927void VuoShaderFile::saveStage(string filePath, string &source, json_object *vuoModuleMetadata)
928{
929 if (source.empty())
930 {
931 // This stage has been disabled. Delete the file,
932 // to avoid leaving stale files around.
933 if (VuoFileUtilities::fileExists(filePath))
935 return;
936 }
937
938 string fileContents = stageFileContents(source, vuoModuleMetadata);
939
940 VuoFileUtilities::writeStringToFile(fileContents, filePath);
941}
942
943string VuoShaderFile::stageFileContents(string &source, json_object *vuoModuleMetadata)
944{
945 ostringstream ss;
947 {
948 ss << "/*";
949
950 json_object *isfMetadata = json_object_new_object();
951
952 if (isfVersion.empty())
953 json_object_object_add(isfMetadata, "ISFVSN", json_object_new_string("2.0"));
954 else
955 json_object_object_add(isfMetadata, "ISFVSN", json_object_new_string(isfVersion.c_str()));
956
957 json_object_object_add(isfMetadata, "TYPE", json_object_new_string(typeNameISF().c_str()));
958
959 if (!_name.empty())
960 json_object_object_add(isfMetadata, "LABEL", json_object_new_string(_name.c_str()));
961
962 if (!shaderVersion.empty())
963 json_object_object_add(isfMetadata, "VSN", json_object_new_string(shaderVersion.c_str()));
964
965 if (!copyright.empty())
966 json_object_object_add(isfMetadata, "CREDIT", json_object_new_string(copyright.c_str()));
967
968 if (!license.empty())
969 json_object_object_add(isfMetadata, "LICENSE", json_object_new_string(license.c_str()));
970
971 if (!description.empty())
972 json_object_object_add(isfMetadata, "DESCRIPTION", json_object_new_string(description.c_str()));
973
974 if (!homepageLink.empty() || !documentationLink.empty())
975 {
976 json_object *links = json_object_new_object();
977 if (!homepageLink.empty())
978 json_object_object_add(links, "HOMEPAGE", json_object_new_string(homepageLink.c_str()));
979 if (!documentationLink.empty())
980 json_object_object_add(links, "DOCUMENTATION", json_object_new_string(documentationLink.c_str()));
981 json_object_object_add(isfMetadata, "LINKS", links);
982 }
983
984 if (!categories.empty())
985 json_object_object_add(isfMetadata, "CATEGORIES", VuoJsonUtilities::getJson(categories));
986
987 if (!keywords.empty())
988 json_object_object_add(isfMetadata, "KEYWORDS", VuoJsonUtilities::getJson(keywords));
989
990 if (!examples.empty())
991 json_object_object_add(isfMetadata, "EXAMPLES", VuoJsonUtilities::getJson(examples));
992
993 // INPUTS
994 if (!_inputPorts.empty())
995 {
996 json_object *inputs = json_object_new_array();
997 for (vector<Port>::iterator i = _inputPorts.begin(); i != _inputPorts.end(); ++i)
998 {
999 json_object *input = json_object_new_object();
1000
1001 if (!i->key.empty())
1002 json_object_object_add(input, "NAME", json_object_new_string(i->key.c_str()));
1003
1004 json_object_object_add(input, "TYPE", json_object_new_string(isfTypeForVuoType(i->vuoTypeName).c_str()));
1005
1006 json_object *nameValue = NULL;
1007 if (json_object_object_get_ex(i->vuoPortDetails, "name", &nameValue))
1008 json_object_object_add(input, "LABEL", nameValue);
1009
1010 json_object *defaultValue = NULL;
1011 if (json_object_object_get_ex(i->vuoPortDetails, "default", &defaultValue))
1012 {
1013 // Convert Vuo serialization to ISF serialization.
1014 if (VuoStringUtilities::beginsWith(i->vuoTypeName, "VuoPoint")
1015 && json_object_get_type(defaultValue) == json_type_object)
1016 {
1017 json_object *x = nullptr;
1018 json_object *y = nullptr;
1019 json_object *z = nullptr;
1020 json_object *w = nullptr;
1021 json_object_object_get_ex(defaultValue, "x", &x);
1022 json_object_object_get_ex(defaultValue, "y", &y);
1023 json_object_object_get_ex(defaultValue, "z", &z);
1024 json_object_object_get_ex(defaultValue, "w", &w);
1025
1026 defaultValue = json_object_new_array();
1027 json_object_array_add(defaultValue, x);
1028 json_object_array_add(defaultValue, y);
1029 if (i->vuoTypeName == "VuoPoint3d" || i->vuoTypeName == "VuoPoint4d")
1030 json_object_array_add(defaultValue, z);
1031 if (i->vuoTypeName == "VuoPoint4d")
1032 json_object_array_add(defaultValue, w);
1033 }
1034
1035 json_object_object_add(input, "DEFAULT", defaultValue);
1036 }
1037
1038 json_object *scaleToSamplerRectValue = NULL;
1039 if (!(json_object_object_get_ex(i->vuoPortDetails, "scaleToSamplerRect", &scaleToSamplerRectValue)
1040 && json_object_get_boolean(scaleToSamplerRectValue)))
1041 {
1042 json_object *minValue = NULL;
1043 if (json_object_object_get_ex(i->vuoPortDetails, "suggestedMin", &minValue))
1044 json_object_object_add(input, "MIN", minValue);
1045
1046 json_object *maxValue = NULL;
1047 if (json_object_object_get_ex(i->vuoPortDetails, "suggestedMax", &maxValue))
1048 json_object_object_add(input, "MAX", maxValue);
1049 }
1050
1051 json_object *stepValue = NULL;
1052 if (json_object_object_get_ex(i->vuoPortDetails, "suggestedStep", &stepValue))
1053 json_object_object_add(input, "STEP", stepValue);
1054
1055 json_object *maxItemsValue = NULL;
1056 if (json_object_object_get_ex(i->vuoPortDetails, "maxItems", &maxItemsValue))
1057 json_object_object_add(input, "MAX_ITEMS", maxItemsValue);
1058
1059 json_object *menuItemsValue = NULL;
1060 if (json_object_object_get_ex(i->vuoPortDetails, "menuItems", &menuItemsValue))
1061 {
1062 json_object *valueItems = json_object_new_array();
1063 json_object *labelItems = json_object_new_array();
1064 int len = json_object_array_length(menuItemsValue);
1065 for (int mi = 0; mi < len; ++mi)
1066 {
1067 json_object *menuItem = json_object_array_get_idx(menuItemsValue, mi);
1068
1069 json_object *valueItem = nullptr;
1070 json_object_object_get_ex(menuItem, "value", &valueItem);
1071 json_object_array_add(valueItems, valueItem);
1072
1073 json_object *nameItem = nullptr;
1074 json_object_object_get_ex(menuItem, "name", &nameItem);
1075 json_object_array_add(labelItems, nameItem);
1076 }
1077 json_object_object_add(input, "VALUES", valueItems);
1078 json_object_object_add(input, "LABELS", labelItems);
1079 }
1080
1081 json_object_array_add(inputs, input);
1082 }
1083 json_object_object_add(isfMetadata, "INPUTS", inputs);
1084 }
1085
1086 // OUTPUTS
1087 {
1088 json_object *output = json_object_new_object();
1089
1090 if (_type == VuoShaderFile::GLSLImageFilter
1091 || _type == VuoShaderFile::GLSLImageGenerator
1092 || _type == VuoShaderFile::GLSLImageTransition)
1093 {
1094 if (outputKey != "outputImage")
1095 json_object_object_add(output, "NAME", json_object_new_string(outputKey.c_str()));
1096 if (outputName != "Output Image")
1097 json_object_object_add(output, "LABEL", json_object_new_string(outputName.c_str()));
1098 }
1099 else if (_type == VuoShaderFile::GLSLObjectRenderer)
1100 {
1101 if (outputKey != "shader")
1102 json_object_object_add(output, "NAME", json_object_new_string(outputKey.c_str()));
1103 if (outputName != "Shader")
1104 json_object_object_add(output, "LABEL", json_object_new_string(outputName.c_str()));
1105 }
1106 else
1107 {
1108 if (outputKey != "outputObject")
1109 json_object_object_add(output, "NAME", json_object_new_string(outputKey.c_str()));
1110 if (outputName != "Output Object")
1111 json_object_object_add(output, "LABEL", json_object_new_string(outputName.c_str()));
1112 }
1113
1114 if (json_object_object_length(output))
1115 {
1116 json_object *outputs = json_object_new_array();
1117 json_object_array_add(outputs, output);
1118 json_object_object_add(isfMetadata, "OUTPUTS", outputs);
1119 }
1120 }
1121
1122 string json = json_object_to_json_string_ext(isfMetadata, JSON_C_TO_STRING_PRETTY);
1123 ss << spacesToTabs(json);
1124 ss << "*/\n\n";
1125 }
1126
1127 ss << source;
1128
1129 if (source[source.length() - 1] != '\n')
1130 ss << "\n";
1131
1132 return ss.str();
1133}
1134
1138string VuoShaderFile::spacesToTabs(string &str)
1139{
1140 stringstream strStream(str);
1141 string ln;
1142 ostringstream out;
1143 while (getline(strStream, ln))
1144 {
1145 size_t spaces = ln.find_first_not_of(' ');
1146 out << string(spaces / 2, '\t');
1147 out << ln.substr(spaces);
1148 if (!strStream.eof())
1149 out << '\n';
1150 }
1151 return out.str();
1152}