Vuo  2.0.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 
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_object();
654  int valueCount = json_object_array_length(values);
655  for (int i = 0; i < valueCount; ++i)
656  json_object_object_add(menuItems, json_object_get_string(json_object_array_get_idx(values, i)), json_object_array_get_idx(labels, i));
657  json_object_object_add(vuoPortDetails, "menuItems", menuItems);
658  }
659 
660  _inputPorts.push_back((Port){ key, vuoType, vuoPortDetails });
661  }
662  }
663 
664  json_object *outputsArray;
665  if (json_object_object_get_ex(metadata, "OUTPUTS", &outputsArray))
666  {
667  int outputCount = json_object_array_length(outputsArray);
668  for (int i = 0; i < outputCount; ++i)
669  {
670  json_object *outputObject = json_object_array_get_idx(outputsArray, i);
671  outputKey = VuoJsonUtilities::parseString(outputObject, "NAME");
672  outputName = VuoJsonUtilities::parseString(outputObject, "LABEL");
673  }
674  }
675 
676  if (typeString == "IMAGE")
677  {
678  if (hasInputImage)
679  _type = GLSLImageFilter;
680  else if (hasStartImage && hasEndImage && hasProgress)
681  _type = GLSLImageTransition;
682  else
683  _type = GLSLImageGenerator;
684 
685  if (outputKey.empty())
686  outputKey = "outputImage";
687  if (outputName.empty())
688  outputName = VuoStringUtilities::expandCamelCase(outputKey);
689  }
690  else if (typeString == "OBJECT_RENDER")
691  {
692  _type = GLSLObjectRenderer;
693 
694  if (outputKey.empty())
695  outputKey = "shader";
696  if (outputName.empty())
697  outputName = VuoStringUtilities::expandCamelCase(outputKey);
698  }
699  else if (typeString == "OBJECT_FILTER")
700  {
701  _type = GLSLObjectFilter;
702 
703  if (outputKey.empty())
704  outputKey = "outputObject";
705  if (outputName.empty())
706  outputName = VuoStringUtilities::expandCamelCase(outputKey);
707  }
708 
709  _vuoModuleMetadata = json_object_new_object();
710 
711  string title = (! _name.empty() ? _name : normalizedBasename);
712  json_object_object_add(_vuoModuleMetadata, "title", json_object_new_string(title.c_str()));
713 
714  string metadataDescription = description;
715  if (!copyright.empty())
716  metadataDescription += "\n\n" + copyright;
717  if (!metadataDescription.empty())
718  json_object_object_add(_vuoModuleMetadata, "description", json_object_new_string(metadataDescription.c_str()));
719 
720  if (!shaderVersion.empty())
721  json_object_object_add(_vuoModuleMetadata, "version", json_object_new_string(shaderVersion.c_str()));
722  if (!keywords.empty())
723  json_object_object_add(_vuoModuleMetadata, "keywords", VuoJsonUtilities::getJson(keywords));
724 
725  if (!examples.empty())
726  {
727  json_object *nodeMetadataJson = json_object_new_object();
728  json_object_object_add(nodeMetadataJson, "exampleCompositions", VuoJsonUtilities::getJson(examples));
729  json_object_object_add(_vuoModuleMetadata, "node", nodeMetadataJson);
730  }
731 
732  if (_type == VuoShaderFile::GLSLImageFilter
733  || _type == VuoShaderFile::GLSLImageGenerator
734  || _type == VuoShaderFile::GLSLImageTransition
735  || _type == VuoShaderFile::GLSLObjectFilter)
736  {
737  json_object *dependenciesJson = json_object_new_array();
738 
739  if (_type == VuoShaderFile::GLSLImageFilter
740  || _type == VuoShaderFile::GLSLImageGenerator
741  || _type == VuoShaderFile::GLSLImageTransition)
742  json_object_array_add(dependenciesJson, json_object_new_string("VuoImageRenderer"));
743  else if (_type == VuoShaderFile::GLSLObjectFilter)
744  json_object_array_add(dependenciesJson, json_object_new_string("VuoSceneObjectRenderer"));
745 
746  json_object_object_add(_vuoModuleMetadata, "dependencies", dependenciesJson);
747  }
748 }
749 
750 string VuoShaderFile::vuoTypeForIsfType(string isfType)
751 {
752  if (isfType == "event")
753  return "event";
754 
755  else if (isfType == "bool")
756  return "VuoBoolean";
757  else if (isfType == "long")
758  return "VuoInteger";
759  else if (isfType == "float")
760  return "VuoReal";
761  else if (isfType == "point2D")
762  return "VuoPoint2d";
763  else if (isfType == "point3D")
764  return "VuoPoint3d";
765  else if (isfType == "point4D")
766  return "VuoPoint4d";
767  else if (isfType == "color")
768  return "VuoColor";
769 
770  else if (isfType == "bool[]")
771  return "VuoList_VuoBoolean";
772  else if (isfType == "long[]")
773  return "VuoList_VuoInteger";
774  else if (isfType == "float[]")
775  return "VuoList_VuoReal";
776  else if (isfType == "point2D[]")
777  return "VuoList_VuoPoint2d";
778  else if (isfType == "point3D[]")
779  return "VuoList_VuoPoint3d";
780  else if (isfType == "point4D[]")
781  return "VuoList_VuoPoint4d";
782  else if (isfType == "color[]")
783  return "VuoList_VuoColor";
784 
785  else if (isfType == "image")
786  return "VuoImage";
787 
788  else if (isfType == "size")
789  return "VuoSize";
790  else if (isfType == "colorDepth")
791  return "VuoImageColorDepth";
792 
793  return "";
794 }
795 
796 string VuoShaderFile::isfTypeForVuoType(string vuoType)
797 {
798  if (vuoType == "event")
799  return "event";
800  else if (vuoType == "VuoBoolean")
801  return "bool";
802  else if (vuoType == "VuoInteger")
803  return "long";
804  else if (vuoType == "VuoReal")
805  return "float";
806  else if (vuoType == "VuoPoint2d")
807  return "point2D";
808  else if (vuoType == "VuoPoint3d")
809  return "point3D";
810  else if (vuoType == "VuoPoint4d")
811  return "point4D";
812  else if (vuoType == "VuoColor")
813  return "color";
814 
815  else if (vuoType == "VuoList_VuoBoolean")
816  return "bool[]";
817  else if (vuoType == "VuoList_VuoInteger")
818  return "long[]";
819  else if (vuoType == "VuoList_VuoReal")
820  return "float[]";
821  else if (vuoType == "VuoList_VuoPoint2d")
822  return "point2D[]";
823  else if (vuoType == "VuoList_VuoPoint3d")
824  return "point3D[]";
825  else if (vuoType == "VuoList_VuoPoint4d")
826  return "point4D[]";
827  else if (vuoType == "VuoList_VuoColor")
828  return "color[]";
829 
830  else if (vuoType == "VuoImage")
831  return "image";
832 
833  else if (vuoType == "VuoSize")
834  return "size";
835  else if (vuoType == "VuoImageColorDepth")
836  return "colorDepth";
837 
838  return "";
839 }
840 
845 {
846  set<string> types;
847 
848  types.insert("VuoBoolean");
849  types.insert("VuoInteger");
850  types.insert("VuoReal");
851  types.insert("VuoPoint2d");
852  types.insert("VuoPoint3d");
853  types.insert("VuoPoint4d");
854  types.insert("VuoColor");
855 
856  types.insert("VuoList_VuoBoolean");
857  types.insert("VuoList_VuoInteger");
858  types.insert("VuoList_VuoReal");
859  types.insert("VuoList_VuoPoint2d");
860  types.insert("VuoList_VuoPoint3d");
861  types.insert("VuoList_VuoPoint4d");
862  types.insert("VuoList_VuoColor");
863 
864  types.insert("VuoImage");
865  types.insert("VuoSize");
866  types.insert("VuoImageColorDepth");
867 
868  return types;
869 }
870 
871 string VuoShaderFile::glslDeclarationForPort(Port port)
872 {
873  string maxItems = to_string(VuoJsonUtilities::parseInt(port.vuoPortDetails, "maxItems", 16));
874 
875  if (port.vuoTypeName == "event"
876  || port.vuoTypeName == "VuoBoolean")
877  return "uniform bool " + port.key + ";\n";
878  else if (port.vuoTypeName == "VuoInteger")
879  return "uniform int " + port.key + ";\n";
880  else if (port.vuoTypeName == "VuoReal")
881  return "uniform float " + port.key + ";\n";
882  else if (port.vuoTypeName == "VuoPoint2d")
883  return "uniform vec2 " + port.key + ";\n";
884  else if (port.vuoTypeName == "VuoPoint3d")
885  return "uniform vec3 " + port.key + ";\n";
886  else if (port.vuoTypeName == "VuoPoint4d")
887  return "uniform vec4 " + port.key + ";\n";
888  else if (port.vuoTypeName == "VuoColor")
889  return "uniform vec4 " + port.key + ";\n";
890 
891  else if (port.vuoTypeName == "VuoList_VuoBoolean")
892  return "uniform bool " + port.key + "[" + maxItems + "];\n"
893  "uniform int _" + port.key + "_length;\n";
894  else if (port.vuoTypeName == "VuoList_VuoInteger")
895  return "uniform int " + port.key + "[" + maxItems + "];\n"
896  "uniform int _" + port.key + "_length;\n";
897  else if (port.vuoTypeName == "VuoList_VuoReal")
898  return "uniform float " + port.key + "[" + maxItems + "];\n"
899  "uniform int _" + port.key + "_length;\n";
900  else if (port.vuoTypeName == "VuoList_VuoPoint2d")
901  return "uniform vec2 " + port.key + "[" + maxItems + "];\n"
902  "uniform int _" + port.key + "_length;\n";
903  else if (port.vuoTypeName == "VuoList_VuoPoint3d")
904  return "uniform vec3 " + port.key + "[" + maxItems + "];\n"
905  "uniform int _" + port.key + "_length;\n";
906  else if (port.vuoTypeName == "VuoList_VuoPoint4d")
907  return "uniform vec4 " + port.key + "[" + maxItems + "];\n"
908  "uniform int _" + port.key + "_length;\n";
909  else if (port.vuoTypeName == "VuoList_VuoColor")
910  return "uniform vec4 " + port.key + "[" + maxItems + "];\n"
911  "uniform int _" + port.key + "_length;\n";
912 
913  else if (port.vuoTypeName == "VuoImage")
914  return "//uniform VuoImage " + port.key + ";\n" // We don't know the sampler type until execution time, so use a placeholder.
915  "uniform vec4 _" + port.key + "_imgRect;\n"
916  "uniform vec2 _" + port.key + "_imgSize;\n"
917  "uniform bool _" + port.key + "_flip;\n";
918 
919  return "";
920 }
921 
922 void VuoShaderFile::saveStage(string filePath, string &source, json_object *vuoModuleMetadata)
923 {
924  if (source.empty())
925  {
926  // This stage has been disabled. Delete the file,
927  // to avoid leaving stale files around.
928  if (VuoFileUtilities::fileExists(filePath))
930  return;
931  }
932 
933  string fileContents = stageFileContents(source, vuoModuleMetadata);
934 
935  VuoFileUtilities::writeStringToFile(fileContents, filePath);
936 }
937 
938 string VuoShaderFile::stageFileContents(string &source, json_object *vuoModuleMetadata)
939 {
940  ostringstream ss;
941  if (vuoModuleMetadata)
942  {
943  ss << "/*";
944 
945  json_object *isfMetadata = json_object_new_object();
946 
947  if (isfVersion.empty())
948  json_object_object_add(isfMetadata, "ISFVSN", json_object_new_string("2.0"));
949  else
950  json_object_object_add(isfMetadata, "ISFVSN", json_object_new_string(isfVersion.c_str()));
951 
952  json_object_object_add(isfMetadata, "TYPE", json_object_new_string(typeNameISF().c_str()));
953 
954  if (!_name.empty())
955  json_object_object_add(isfMetadata, "LABEL", json_object_new_string(_name.c_str()));
956 
957  if (!shaderVersion.empty())
958  json_object_object_add(isfMetadata, "VSN", json_object_new_string(shaderVersion.c_str()));
959 
960  if (!copyright.empty())
961  json_object_object_add(isfMetadata, "CREDIT", json_object_new_string(copyright.c_str()));
962 
963  if (!license.empty())
964  json_object_object_add(isfMetadata, "LICENSE", json_object_new_string(license.c_str()));
965 
966  if (!description.empty())
967  json_object_object_add(isfMetadata, "DESCRIPTION", json_object_new_string(description.c_str()));
968 
969  if (!homepageLink.empty() || !documentationLink.empty())
970  {
971  json_object *links = json_object_new_object();
972  if (!homepageLink.empty())
973  json_object_object_add(links, "HOMEPAGE", json_object_new_string(homepageLink.c_str()));
974  if (!documentationLink.empty())
975  json_object_object_add(links, "DOCUMENTATION", json_object_new_string(documentationLink.c_str()));
976  json_object_object_add(isfMetadata, "LINKS", links);
977  }
978 
979  if (!categories.empty())
980  json_object_object_add(isfMetadata, "CATEGORIES", VuoJsonUtilities::getJson(categories));
981 
982  if (!keywords.empty())
983  json_object_object_add(isfMetadata, "KEYWORDS", VuoJsonUtilities::getJson(keywords));
984 
985  if (!examples.empty())
986  json_object_object_add(isfMetadata, "EXAMPLES", VuoJsonUtilities::getJson(examples));
987 
988  // INPUTS
989  if (!_inputPorts.empty())
990  {
991  json_object *inputs = json_object_new_array();
992  for (vector<Port>::iterator i = _inputPorts.begin(); i != _inputPorts.end(); ++i)
993  {
994  json_object *input = json_object_new_object();
995 
996  if (!i->key.empty())
997  json_object_object_add(input, "NAME", json_object_new_string(i->key.c_str()));
998 
999  json_object_object_add(input, "TYPE", json_object_new_string(isfTypeForVuoType(i->vuoTypeName).c_str()));
1000 
1001  json_object *nameValue = NULL;
1002  if (json_object_object_get_ex(i->vuoPortDetails, "name", &nameValue))
1003  json_object_object_add(input, "LABEL", nameValue);
1004 
1005  json_object *defaultValue = NULL;
1006  if (json_object_object_get_ex(i->vuoPortDetails, "default", &defaultValue))
1007  {
1008  // Convert Vuo serialization to ISF serialization.
1009  if (VuoStringUtilities::beginsWith(i->vuoTypeName, "VuoPoint")
1010  && json_object_get_type(defaultValue) == json_type_object)
1011  {
1012  json_object *x = nullptr;
1013  json_object *y = nullptr;
1014  json_object *z = nullptr;
1015  json_object *w = nullptr;
1016  json_object_object_get_ex(defaultValue, "x", &x);
1017  json_object_object_get_ex(defaultValue, "y", &y);
1018  json_object_object_get_ex(defaultValue, "z", &z);
1019  json_object_object_get_ex(defaultValue, "w", &w);
1020 
1021  defaultValue = json_object_new_array();
1022  json_object_array_add(defaultValue, x);
1023  json_object_array_add(defaultValue, y);
1024  if (i->vuoTypeName == "VuoPoint3d" || i->vuoTypeName == "VuoPoint4d")
1025  json_object_array_add(defaultValue, z);
1026  if (i->vuoTypeName == "VuoPoint4d")
1027  json_object_array_add(defaultValue, w);
1028  }
1029 
1030  json_object_object_add(input, "DEFAULT", defaultValue);
1031  }
1032 
1033  json_object *scaleToSamplerRectValue = NULL;
1034  if (!(json_object_object_get_ex(i->vuoPortDetails, "scaleToSamplerRect", &scaleToSamplerRectValue)
1035  && json_object_get_boolean(scaleToSamplerRectValue)))
1036  {
1037  json_object *minValue = NULL;
1038  if (json_object_object_get_ex(i->vuoPortDetails, "suggestedMin", &minValue))
1039  json_object_object_add(input, "MIN", minValue);
1040 
1041  json_object *maxValue = NULL;
1042  if (json_object_object_get_ex(i->vuoPortDetails, "suggestedMax", &maxValue))
1043  json_object_object_add(input, "MAX", maxValue);
1044  }
1045 
1046  json_object *stepValue = NULL;
1047  if (json_object_object_get_ex(i->vuoPortDetails, "suggestedStep", &stepValue))
1048  json_object_object_add(input, "STEP", stepValue);
1049 
1050  json_object *maxItemsValue = NULL;
1051  if (json_object_object_get_ex(i->vuoPortDetails, "maxItems", &maxItemsValue))
1052  json_object_object_add(input, "MAX_ITEMS", maxItemsValue);
1053 
1054  json_object *menuItemsValue = NULL;
1055  if (json_object_object_get_ex(i->vuoPortDetails, "menuItems", &menuItemsValue))
1056  {
1057  json_object *valueItems = json_object_new_array();
1058  json_object *labelItems = json_object_new_array();
1059  json_object_object_foreach(menuItemsValue, key, val)
1060  {
1061  json_object_array_add(valueItems, json_object_new_string(key));
1062  json_object_array_add(labelItems, val);
1063  }
1064  json_object_object_add(input, "VALUES", valueItems);
1065  json_object_object_add(input, "LABELS", labelItems);
1066  }
1067 
1068  json_object_array_add(inputs, input);
1069  }
1070  json_object_object_add(isfMetadata, "INPUTS", inputs);
1071  }
1072 
1073  // OUTPUTS
1074  {
1075  json_object *output = json_object_new_object();
1076 
1077  if (_type == VuoShaderFile::GLSLImageFilter
1078  || _type == VuoShaderFile::GLSLImageGenerator
1079  || _type == VuoShaderFile::GLSLImageTransition)
1080  {
1081  if (outputKey != "outputImage")
1082  json_object_object_add(output, "NAME", json_object_new_string(outputKey.c_str()));
1083  if (outputName != "Output Image")
1084  json_object_object_add(output, "LABEL", json_object_new_string(outputName.c_str()));
1085  }
1086  else if (_type == VuoShaderFile::GLSLObjectRenderer)
1087  {
1088  if (outputKey != "shader")
1089  json_object_object_add(output, "NAME", json_object_new_string(outputKey.c_str()));
1090  if (outputName != "Shader")
1091  json_object_object_add(output, "LABEL", json_object_new_string(outputName.c_str()));
1092  }
1093  else
1094  {
1095  if (outputKey != "outputObject")
1096  json_object_object_add(output, "NAME", json_object_new_string(outputKey.c_str()));
1097  if (outputName != "Output Object")
1098  json_object_object_add(output, "LABEL", json_object_new_string(outputName.c_str()));
1099  }
1100 
1101  if (json_object_object_length(output))
1102  {
1103  json_object *outputs = json_object_new_array();
1104  json_object_array_add(outputs, output);
1105  json_object_object_add(isfMetadata, "OUTPUTS", outputs);
1106  }
1107  }
1108 
1109  string json = json_object_to_json_string_ext(isfMetadata, JSON_C_TO_STRING_PRETTY);
1110  ss << spacesToTabs(json);
1111  ss << "*/\n\n";
1112  }
1113 
1114  ss << source;
1115 
1116  if (source[source.length() - 1] != '\n')
1117  ss << "\n";
1118 
1119  return ss.str();
1120 }
1121 
1125 string VuoShaderFile::spacesToTabs(string &str)
1126 {
1127  stringstream strStream(str);
1128  string ln;
1129  ostringstream out;
1130  while (getline(strStream, ln))
1131  {
1132  size_t spaces = ln.find_first_not_of(' ');
1133  out << string(spaces / 2, '\t');
1134  out << ln.substr(spaces);
1135  if (!strStream.eof())
1136  out << '\n';
1137  }
1138  return out.str();
1139 }