Vuo  2.0.2
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 
73 VuoShaderFile::VuoShaderFile(VuoFileUtilities::File file, const string &overriddenFragmentSource)
74 {
75  basename = file.basename();
76  extension = file.extension();
77  init(file, overriddenFragmentSource);
78 }
79 
80 void 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 
250 vector<VuoShaderFile::Port> VuoShaderFile::inputPorts()
251 {
252  return _inputPorts;
253 }
254 
258 void 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 
294 void VuoShaderFile::setVertexSource(const string &source)
295 {
296  _vertexSource = source;
297 }
298 
299 void 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 
381 void 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 
418 void 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 
450 void 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 
484 string VuoShaderFile::readStage(VuoFileUtilities::File file)
485 {
486  if (!file.exists())
487  return "";
488 
489  return file.getContentsAsString();
490 }
491 
492 void 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 
535 void 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 
755 string 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 
801 string 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 
876 string 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 
927 void 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 
943 string VuoShaderFile::stageFileContents(string &source, json_object *vuoModuleMetadata)
944 {
945  ostringstream ss;
946  if (vuoModuleMetadata)
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 
1138 string 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 }