Developer Documentation
ShaderGenerator.cc
1 /*===========================================================================*\
2  * *
3  * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39  * *
40 \*===========================================================================*/
41 
42 /*===========================================================================*\
43  * *
44  * $Revision$ *
45  * $Author$ *
46  * $Date$ *
47  * *
48 \*===========================================================================*/
49 
50 #include "ShaderGenerator.hh"
51 #include <cstdio>
52 #include <iostream>
53 
54 #include <QFile>
55 #include <QFileInfo>
56 #include <QDir>
57 #include <QTextStream>
58 #include <QDateTime>
59 
60 namespace ACG
61 {
62 
63 
65 std::vector<ShaderModifier*> ShaderProgGenerator::registeredModifiers_;
66 
67 
68 
69 // space naming
70 // OS : object space
71 // VS : view space
72 // CS : clip space
73 
74 
75 ShaderGenerator::Keywords::Keywords()
76 // attribute request keywords
77 : macro_requestPosVS("#define SG_REQUEST_POSVS"),
78 macro_requestPosOS("#define SG_REQUEST_POSOS"),
79 macro_requestTexcoord("#define SG_REQUEST_TEXCOORD"),
80 macro_requestVertexColor("#define SG_REQUEST_VERTEXCOLOR"),
81 macro_requestNormalVS("#define SG_REQUEST_NORMALVS"),
82 macro_requestNormalOS("#define SG_REQUEST_NORMALOS"),
83 // // renormalize normal-vec before lighting in fragment shader
84 macro_requestRenormalize("#define SG_REQUEST_RENORMARLIZE"),
85 
86 // generic default attribute input keywords
87 // these are extended by the correct input name by the generator for each stage
88 macro_inputPosVS("SG_INPUT_POSVS"),
89 macro_inputPosOS("SG_INPUT_POSOS"),
90 macro_inputPosCS("SG_INPUT_POSCS"),
91 macro_inputNormalVS("SG_INPUT_NORMALVS"),
92 macro_inputNormalOS("SG_INPUT_NORMALOS"),
93 macro_inputTexcoord("SG_INPUT_TEXCOORD"),
94 macro_inputVertexColor("SG_INPUT_VERTEXCOLOR"),
95 
96 macro_outputPosVS("SG_OUTPUT_POSVS"),
97 macro_outputPosOS("SG_OUTPUT_POSOS"),
98 macro_outputPosCS("SG_OUTPUT_POSCS"),
99 macro_outputNormalVS("SG_OUTPUT_NORMALVS"),
100 macro_outputNormalOS("SG_OUTPUT_NORMALOS"),
101 macro_outputTexcoord("SG_OUTPUT_TEXCOORD"),
102 macro_outputVertexColor("SG_OUTPUT_VERTEXCOLOR"),
103 
104 ioPosCS("PosCS"),
105 ioPosOS("PosOS"),
106 ioPosVS("PosVS"),
107 ioNormalVS("NormalVS"),
108 ioNormalOS("NormalOS"),
109 ioTexcoord("TexCoord"),
110 ioColor("Color"),
111 
112 vs_inputPrefix("in"),
113 vs_outputPrefix("outVertex"),
114 tcs_outputPrefix("outTc"),
115 tes_outputPrefix("outTe"),
116 gs_outputPrefix("outGeometry"),
117 fs_outputPrefix("outFragment"),
118 
119 vs_inputPosition(vs_inputPrefix + "Position"),
120 vs_inputNormal(vs_inputPrefix + "Normal"),
121 vs_inputTexCoord(vs_inputPrefix + ioTexcoord),
122 vs_inputColor(vs_inputPrefix + ioColor),
123 
124 vs_outputPosCS(vs_outputPrefix + ioPosCS),
125 vs_outputPosVS(vs_outputPrefix + ioPosVS),
126 vs_outputPosOS(vs_outputPrefix + ioPosOS),
127 vs_outputTexCoord(vs_outputPrefix + ioTexcoord),
128 vs_outputNormalVS(vs_outputPrefix + ioNormalVS),
129 vs_outputNormalOS(vs_outputPrefix + ioNormalOS),
130 vs_outputVertexColor(vs_outputPrefix + ioColor),
131 fs_outputFragmentColor(fs_outputPrefix)
132 {
133 }
134 
135 const ShaderGenerator::Keywords ShaderGenerator::keywords;
136 
137 
138 ShaderGenerator::ShaderGenerator()
139  : version_(150), inputArrays_(false), outputArrays_(false)
140 {
141 }
142 
143 ShaderGenerator::~ShaderGenerator()
144 {
145 
146 }
147 
148 
150 {
151  // set type of IO
152  inputArrays_ = false;
153  outputArrays_ = false;
154  inputPrefix_ = keywords.vs_inputPrefix; // inputs: inPosition, inTexCoord...
155  outputPrefix_ = keywords.vs_outputPrefix; // outputs: outVertexPosition, outVertexTexCoord..
156 
157  addInput("vec4", keywords.vs_inputPosition);
158  addOutput("vec4", keywords.vs_outputPosCS);
159 
160  if (_iodesc->inputNormal_)
161  addInput("vec3", keywords.vs_inputNormal);
162 
163  if (_desc->textured())
164  {
165  std::map<size_t,ShaderGenDesc::TextureType>::const_iterator iter = _desc->textureTypes().begin();
166 
168  if (iter->second.type == GL_TEXTURE_3D) {
169  addInput("vec3", keywords.vs_inputTexCoord);
170  addOutput("vec3", keywords.vs_outputTexCoord);
171  } else {
172  addInput("vec2", keywords.vs_inputTexCoord);
173  addOutput("vec2", keywords.vs_outputTexCoord);
174  }
175  }
176 
177  if (_iodesc->inputColor_)
178  addInput("vec4", keywords.vs_inputColor);
179 
180  if (_iodesc->passNormalVS_)
181  addOutput("vec3", keywords.vs_outputNormalVS);
182 
183  if (_iodesc->passNormalOS_)
184  addOutput("vec3", keywords.vs_outputNormalOS);
185 
186 
187  // vertex color output
188 
189  if (_desc->vertexColorsInterpolator.isEmpty())
190  {
191  QString strColorOut;
192  if (_desc->shadeMode == SG_SHADE_FLAT)
193  {
194  if (!_desc->geometryTemplateFile.isEmpty())
195  strColorOut = keywords.vs_outputVertexColor;
196  else
197  {
198  // Bypass the output setter, as we have to set that directly with the flat.
199  addStringToList("vec4 " + keywords.vs_outputVertexColor, &outputs_, "flat out ", "; ");
200  }
201  }
202  else if (_desc->shadeMode == SG_SHADE_GOURAUD || _desc->vertexColors || _iodesc->inputColor_)
203  strColorOut = keywords.vs_outputVertexColor;
204 
205  if (strColorOut.size())
206  addOutput("vec4", strColorOut);
207  }
208  else
209  addStringToList("vec4 " + keywords.vs_outputVertexColor, &outputs_, _desc->vertexColorsInterpolator + " out ", ";");
210 
211 
212 
213  // handle other requests: normals, positions, texcoords
214 
215  if (_iodesc->passPosVS_)
216  addOutput("vec4", keywords.vs_outputPosVS);
217 
218  if (_iodesc->passPosOS_)
219  addOutput("vec4", keywords.vs_outputPosOS);
220 
221  if (_iodesc->passTexCoord_ && !_desc->textured())
222  {
223  // assume 2d texcoords as default
224  int texdim = 2;
225 
226  if (_desc->texGenMode && _desc->texGenDim > 0 && _desc->texGenDim <= 4 && !_desc->texGenPerFragment)
227  texdim = _desc->texGenDim;
228 
229  QString texcoordType;
230  if (texdim > 1)
231  texcoordType.sprintf("vec%i", texdim);
232  else
233  texcoordType = "float";
234 
235  addInput(texcoordType, keywords.vs_inputTexCoord);
236  addOutput(texcoordType, keywords.vs_outputTexCoord);
237  }
238 
239 
240  defineIOAbstraction(_iodesc, true, false);
241 }
242 
244 {
245  // set type of IO
246  inputArrays_ = true;
247  outputArrays_ = true;
248  inputPrefix_ = _prevStage->outputPrefix_;
249  outputPrefix_ = keywords.tcs_outputPrefix; // outputs: outTcPosition, outTcTexCoord..
250 
251  matchInputs(_prevStage, true, inputPrefix_, outputPrefix_);
252 
253  defineIOAbstraction(_iodesc, false, false);
254 }
255 
257 {
258  // set type of IO
259  inputArrays_ = true;
260  outputArrays_ = false;
261  inputPrefix_ = _prevStage->outputPrefix_;
262  outputPrefix_ = keywords.tes_outputPrefix; // outputs: outTePosition, outTeTexCoord..
263 
264  matchInputs(_prevStage, true, inputPrefix_, outputPrefix_);
265 
266  defineIOAbstraction(_iodesc, false, false);
267 }
268 
270 {
271  // set type of IO
272  inputArrays_ = true;
273  outputArrays_ = false;
274  inputPrefix_ = _prevStage->outputPrefix_;
275  outputPrefix_ = keywords.gs_outputPrefix; // outputs: outGeometryPosition, outGeometryTexCoord..
276 
277  matchInputs(_prevStage, true, inputPrefix_, outputPrefix_);
278 
279  defineIOAbstraction(_iodesc, false, false);
280 }
281 
282 
283 
285 {
286  // set type of IO
287  inputArrays_ = false;
288  outputArrays_ = false;
289  inputPrefix_ = _prevStage->outputPrefix_;
290  outputPrefix_ = keywords.fs_outputPrefix;
291 
292  matchInputs(_prevStage, false);
293  addOutput("vec4", keywords.fs_outputFragmentColor);
294 
295  defineIOAbstraction(_iodesc, false, true);
296 }
297 
298 
299 void ShaderGenerator::defineIOAbstraction( const DefaultIODesc* _iodesc, bool _vs, bool _fs )
300 {
301  if (_vs)
302  {
303  // input name abstraction
304 
305  addIODefine(keywords.macro_inputPosOS, keywords.vs_inputPosition);
306 
307  if (_iodesc->inputTexCoord_)
308  addIODefine(keywords.macro_inputTexcoord, keywords.vs_inputTexCoord);
309 
310  if (_iodesc->inputNormal_)
311  addIODefine(keywords.macro_inputNormalOS, keywords.vs_inputNormal);
312 
313  if (_iodesc->inputColor_)
314  addIODefine(keywords.macro_inputVertexColor, keywords.vs_inputColor);
315 
316 
317 
318  // output name abstraction
319 
320  addIODefine(keywords.macro_outputPosCS, keywords.vs_outputPosCS);
321 
322  if (_iodesc->passPosVS_)
323  addIODefine(keywords.macro_outputPosVS, keywords.vs_outputPosVS);
324 
325  if (_iodesc->passPosOS_)
326  addIODefine(keywords.macro_outputPosOS, keywords.vs_outputPosOS);
327 
328  if (_iodesc->passTexCoord_)
329  addIODefine(keywords.macro_outputTexcoord, keywords.vs_outputTexCoord);
330 
331  if (_iodesc->passNormalVS_)
332  addIODefine(keywords.macro_outputNormalVS, keywords.vs_outputNormalVS);
333 
334  if (_iodesc->passNormalOS_)
335  addIODefine(keywords.macro_outputNormalOS, keywords.vs_outputNormalOS);
336 
337  if (_iodesc->passColor_)
338  addIODefine(keywords.macro_outputVertexColor, keywords.vs_outputVertexColor);
339  }
340  else
341  {
342  if (_iodesc->passPosVS_)
343  {
344  addIODefine(keywords.macro_inputPosVS, inputPrefix_ + keywords.ioPosVS);
345  if (!_fs)
346  addIODefine(keywords.macro_outputPosVS, outputPrefix_ + keywords.ioPosVS);
347  }
348 
349  if (_iodesc->passPosOS_)
350  {
351  addIODefine(keywords.macro_inputPosOS, inputPrefix_ + keywords.ioPosOS);
352  if (!_fs)
353  addIODefine(keywords.macro_outputPosOS, outputPrefix_ + keywords.ioPosOS);
354  }
355 
356  addIODefine(keywords.macro_inputPosCS, inputPrefix_ + keywords.ioPosCS);
357  if (!_fs)
358  addIODefine(keywords.macro_outputPosCS, outputPrefix_ + keywords.ioPosCS);
359 
360  if (_iodesc->passNormalVS_)
361  {
362  addIODefine(keywords.macro_inputNormalVS, inputPrefix_ + keywords.ioNormalVS);
363  if (!_fs)
364  addIODefine(keywords.macro_outputNormalVS, outputPrefix_ + keywords.ioNormalVS);
365  }
366 
367  if (_iodesc->passNormalOS_)
368  {
369  addIODefine(keywords.macro_inputNormalOS, inputPrefix_ + keywords.ioNormalOS);
370  if (!_fs)
371  addIODefine(keywords.macro_outputNormalOS, outputPrefix_ + keywords.ioNormalOS);
372  }
373 
374  if (_iodesc->passTexCoord_)
375  {
376  addIODefine(keywords.macro_inputTexcoord, inputPrefix_ + keywords.ioTexcoord);
377  if (!_fs)
378  addIODefine(keywords.macro_outputTexcoord, outputPrefix_ + keywords.ioTexcoord);
379  }
380 
381  if (_iodesc->passColor_)
382  {
383  addIODefine(keywords.macro_inputVertexColor, inputPrefix_ + keywords.ioColor);
384  if (!_fs)
385  addIODefine(keywords.macro_outputVertexColor, outputPrefix_ + keywords.ioColor);
386  }
387  }
388 
389 
390 }
391 
392 
393 
395 {
396  addUniform("mat4 g_mWVP" , " // Projection * Modelview"); // Transforms directly from Object space to NDC
397  addUniform("mat4 g_mWV" , " // Modelview matrix"); // Modelview transforms from Object to World to View coordinates
398  addUniform("mat3 g_mWVIT" , " // Modelview inverse transposed"); // Modelview inverse transposed, transforms from view across World into Object coordinates
399  addUniform("mat4 g_mP", " // Projection matrix"); // Projection Matrix
400 
401  addUniform("vec3 g_vCamPos");
402  addUniform("vec3 g_vCamDir");
403 
404  addUniform("vec3 g_cDiffuse");
405  addUniform("vec3 g_cAmbient");
406  addUniform("vec3 g_cEmissive");
407  addUniform("vec3 g_cSpecular");
408  addUniform("vec4 g_vMaterial");
409  addUniform("vec3 g_cLightModelAmbient");
410 }
411 
412 
413 #define ADDLIGHT(x) (sz.sprintf(x"_%d", lightIndex_), addUniform(sz))
414 
415 void ShaderGenerator::addLight(int lightIndex_, ShaderGenLightType _light)
416 {
417  QString sz;
418 
419  ADDLIGHT("vec3 g_cLightDiffuse");
420  ADDLIGHT("vec3 g_cLightAmbient");
421  ADDLIGHT("vec3 g_cLightSpecular");
422 
423  if (_light == SG_LIGHT_POINT ||
424  _light == SG_LIGHT_SPOT)
425  {
426  ADDLIGHT("vec3 g_vLightPos");
427  ADDLIGHT("vec3 g_vLightAtten");
428  }
429 
430  if (_light == SG_LIGHT_DIRECTIONAL ||
431  _light == SG_LIGHT_SPOT)
432  ADDLIGHT("vec3 g_vLightDir");
433 
434 
435  if (_light == SG_LIGHT_SPOT)
436  ADDLIGHT("vec2 g_vLightAngleExp");
437 }
438 
439 
440 
442  QStringList* _arr,
443  QString _prefix,
444  QString _postfix)
445 {
446  // Construct the whole string
447  QString tmp = _str;
448 
449  if (!_str.startsWith(_prefix))
450  tmp = _prefix + tmp;
451 
452  if (!_str.endsWith(_postfix))
453  tmp += _postfix;
454 
455  // normalize string
456  // remove tabs, double whitespace
457  tmp = tmp.simplified();
458 
459  // avoid duplicates
460  if (!_arr->contains(tmp))
461  _arr->push_back(tmp);
462 
463 }
464 
465 
466 void ShaderGenerator::addInput(const QString& _input)
467 {
468  addStringToList(_input, &inputs_, "in ", ";");
469 }
470 
471 
472 void ShaderGenerator::addOutput(const QString& _output)
473 {
474  addStringToList(_output, &outputs_, "out ", ";");
475 }
476 
477 
478 void ShaderGenerator::addDefine(const QString& _def)
479 {
480  addStringToList(_def, &genDefines_, "#define ");
481 }
482 
483 
484 void ShaderGenerator::addIODefine(const QString& _macroName, const QString& _resolvedName)
485 {
486  addDefine(_macroName + QString(" ") + _resolvedName);
487 }
488 
489 void ShaderGenerator::addMacros(const QStringList& _macros)
490 {
491  // prepend macros to the "defines" list
492 
493  // QStringList reverse_iterator:
494  typedef std::reverse_iterator<QStringList::const_iterator> QStringListReverseIterator;
495  QStringListReverseIterator rbegin( _macros.end() ), rend( _macros.begin() );
496 
497  for (QStringListReverseIterator it = rbegin; it != rend; ++it)
498  genDefines_.push_front(*it);
499 }
500 
501 bool ShaderGenerator::hasDefine(QString _define) const
502 {
503  if (genDefines_.contains(_define))
504  return true;
505 
506  // check trimmed strings and with startsWith()
507 
508  QString trimmedDef = _define.trimmed();
509 
510  for (QStringList::const_iterator it = genDefines_.constBegin(); it != genDefines_.constEnd(); ++it)
511  {
512  QString trimmedRef = it->trimmed();
513 
514  if (trimmedRef.startsWith(trimmedDef))
515  return true;
516  }
517 
518  // also check raw io blocks
519  for (QStringList::const_iterator it = rawIO_.constBegin(); it != rawIO_.constEnd(); ++it)
520  {
521  QString trimmedRef = it->trimmed();
522 
523  if (trimmedRef.startsWith(trimmedDef))
524  return true;
525  }
526 
527  return false;
528 }
529 
530 void ShaderGenerator::addLayout(QString _def)
531 {
532  addStringToList(_def, &layouts_);
533 }
534 
535 
536 void ShaderGenerator::addUniform(QString _uniform, QString _comment)
537 {
538  QString prefix = "";
539  if (!_uniform.startsWith("uniform ") && !_uniform.contains(" uniform "))
540  prefix = "uniform ";
541 
542  addStringToList(_uniform, &uniforms_, prefix, "; " + _comment );
543 }
544 
545 
546 
547 void ShaderGenerator::addIOToCode(const QStringList& _cmds)
548 {
549  QString it;
550  foreach(it, _cmds)
551  {
552  code_.push_back(it);
553  // append ; eventually
554 
555  if (!it.contains(";"))
556  code_.back().append(";");
557  }
558 }
559 
560 
561 
562 void ShaderGenerator::buildShaderCode(QStringList* _pMainCode, const QStringList& _defaultLightingFunctions)
563 {
564  QString glslversion;
565  glslversion.sprintf("#version %d", version_);
566 
567  code_.push_back(glslversion);
568 
569  // provide defines
570  QString it;
571 
572  foreach(it, genDefines_)
573  code_.push_back(it);
574 
575  // layouts
576  foreach(it, layouts_)
577  code_.push_back(it);
578 
579  // IO
580  addIOToCode(inputs_);
581  addIOToCode(outputs_);
582  addIOToCode(uniforms_);
583 
584  // eventually attach lighting functions if required
585  bool requiresLightingCode = false;
586 
587  // search for references in imports
588  foreach(it, imports_)
589  {
590  if (it.contains("LitDirLight") || it.contains("LitPointLight") || it.contains("LitSpotLight"))
591  requiresLightingCode = true;
592  }
593 
594  if (requiresLightingCode)
595  {
596  foreach(it, _defaultLightingFunctions)
597  code_.push_back(it);
598  }
599 
600  // provide imports
601  foreach(it, imports_)
602  code_.push_back(it);
603 
604 
605  // search for lighting references in main code
606 
607  if (!requiresLightingCode)
608  {
609  foreach(it, (*_pMainCode))
610  {
611  if (it.contains("LitDirLight") || it.contains("LitPointLight") || it.contains("LitSpotLight"))
612  requiresLightingCode = true;
613  }
614 
615  if (requiresLightingCode)
616  {
617  foreach(it, _defaultLightingFunctions)
618  code_.push_back(it);
619  }
620  }
621 
622 
623  // add raw IO code block
624  code_.append(rawIO_);
625 
626 
627  // main function
628  foreach(it, (*_pMainCode))
629  code_.push_back(it);
630 }
631 
632 
633 
634 void ShaderGenerator::addIncludeFile(QString _fileName)
635 {
636  QFile file(_fileName);
637 
638  if (file.open(QIODevice::ReadOnly | QIODevice::Text))
639  {
640  QTextStream fileStream(&file);
641 
642  // track source of include files in shader comment
643 
644  imports_.push_back("// ==============================================================================");
645  imports_.push_back(QString("// ShaderGenerator - begin of imported file: ") + _fileName);
646 
647 
648  while (!fileStream.atEnd())
649  {
650  QString tmpLine = fileStream.readLine();
651 
652  imports_.push_back(tmpLine.simplified());
653  }
654 
655 
656  // mark end of include file in comment
657 
658  imports_.push_back(QString("// ShaderGenerator - end of imported file #include \"") + _fileName);
659  imports_.push_back("// ==============================================================================");
660 
661  }
662 
663 }
664 
665 
666 
667 void ShaderGenerator::saveToFile(const char* _fileName)
668 {
669  QFile file(_fileName);
670  if (file.open(QIODevice::WriteOnly | QIODevice::Text))
671  {
672  QTextStream fileStream(&file);
673 
674  QString it;
675  foreach(it, code_)
676  fileStream << it << '\n';
677  }
678 }
679 
680 
681 
682 const QStringList& ShaderGenerator::getShaderCode()
683 {
684  return code_;
685 }
686 
688 {
689  version_ = _version;
690 }
691 
692 void ShaderGenerator::matchInputs(const ShaderGenerator* _previousShaderStage,
693  bool _passToNextStage,
694  QString _inputPrefix,
695  QString _outputPrefix)
696 {
697  if (!_previousShaderStage)
698  {
699  std::cout << "error: ShaderGenerator::matchInputs called without providing input stage" << std::endl;
700  return;
701  }
702 
703  QString it;
704  foreach(it, _previousShaderStage->outputs_)
705  {
706  QString input = it;
707 
708  QString outKeyword = "out ";
709  QString inKeyword = "in ";
710 
711  // replace first occurrence of "out" with "in"
712  input.replace(input.indexOf(outKeyword), outKeyword.size(), inKeyword);
713 
714  // special case for array IO
715 
716  if (inputArrays_ && !_previousShaderStage->outputArrays_)
717  {
718  QRegExp alphaNum("[a-zA-Z0-9]");
719  int lastNameChar = input.lastIndexOf(alphaNum);
720  input.insert(lastNameChar+1, "[]");
721 // input.insert(lastNameChar+1, "[gl_in.length()]");
722  }
723 
724 
725  // add to input list with duplicate check
726  addStringToList(input, &inputs_);
727 
728  if (_passToNextStage)
729  {
730  // replace prefixes of in/outputs to avoid name collision
731 
732  QString output = input;
733  output.replace(output.indexOf(_inputPrefix), _inputPrefix.size(), _outputPrefix);
734  output.replace(output.indexOf(inKeyword), inKeyword.size(), outKeyword);
735 
736  // take care of arrays
737  if (inputArrays_ && !outputArrays_)
738  {
739  int bracketStart = output.indexOf("[");
740  int bracketEnd = output.indexOf("]");
741  output.remove(bracketStart, bracketEnd-bracketStart+1);
742  }
743  else if (!inputArrays_ && outputArrays_)
744  {
745  QRegExp alphaNum("[a-zA-Z0-9]");
746  int lastNameChar = output.lastIndexOf(alphaNum);
747  output.insert(lastNameChar+1, "[]");
748 // output.insert(lastNameChar+1, "[gl_in.length()]");
749  }
750 
751 
752  // add to output list with duplicate check
753  addStringToList(output, &outputs_);
754  }
755  }
756 }
757 
759 {
760  return outputs_.size();
761 }
762 
763 QString ShaderGenerator::getOutputName(int _id) const
764 {
765  QString output = outputs_.at(_id);
766 
767  output.remove(";");
768  output.remove("out ");
769 
770  int bracketStart = output.indexOf("[");
771  int bracketEnd = output.lastIndexOf("]");
772 
773  if (bracketStart >= 0)
774  output.remove(bracketStart, bracketEnd-bracketStart+1);
775 
776  // decompose output declaration
777  QStringList decompOutput = output.split(" ");
778  return decompOutput.last();
779 }
780 
782 {
783  return inputs_.size();
784 }
785 
786 QString ShaderGenerator::getInputName(int _id) const
787 {
788  QString input = inputs_.at(_id);
789 
790  input.remove(";");
791  input.remove("out ");
792 
793  int bracketStart = input.indexOf("[");
794  int bracketEnd = input.lastIndexOf("]");
795 
796  if (bracketStart >= 0)
797  input.remove(bracketStart, bracketEnd-bracketStart+1);
798 
799  // decompose output declaration
800  QStringList decompInput = input.split(" ");
801  return decompInput.last();
802 }
803 
804 QString ShaderGenerator::getIOMapName(int _inId) const
805 {
806  QString inputName = getInputName(_inId);
807 
808  // output name = input name with swapped prefix
809  QString outputName = inputName;
810  outputName.replace(outputName.indexOf(inputPrefix_), inputPrefix_.size(), outputPrefix_);
811 
812  return outputName;
813 }
814 
815 
816 ShaderGenerator::DefaultIODesc::DefaultIODesc()
817  : inputTexCoord_(false),
818  inputColor_(false),
819  inputNormal_(false),
820  passPosVS_(false), passPosOS_(false),
821  passTexCoord_(false),
822  passColor_(false),
823  passNormalVS_(false), passNormalOS_(false)
824 {
825 }
826 
827 
828 
829 
830 QString ShaderProgGenerator::shaderDir_;
831 QStringList ShaderProgGenerator::lightingCode_;
832 
833 
835  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
836 {
837  init(_desc, (ShaderModifier**)0, 0);
838 }
839 
840 ShaderProgGenerator::ShaderProgGenerator( const ShaderGenDesc* _desc, const unsigned int* _modifierIDs, unsigned int _numActiveMods )
841  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
842 {
843  init(_desc, _modifierIDs, _numActiveMods);
844 }
845 
846 ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, const std::vector<unsigned int>& _modifierIDs)
847  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
848 {
849  init(_desc, _modifierIDs.empty() ? 0 : &_modifierIDs[0], (unsigned int)_modifierIDs.size());
850 }
851 
852 ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, const std::vector<unsigned int>* _modifierIDs)
853  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
854 {
855  unsigned int numMods = !_modifierIDs || _modifierIDs->empty() ? 0 : (unsigned int)_modifierIDs->size();
856  init(_desc, numMods ? &((*_modifierIDs)[0]) : 0, numMods);
857 }
858 
859 ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, ShaderModifier* const* _modifiers, unsigned int _numActiveMods)
860  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
861 {
862  init(_desc, _modifiers, _numActiveMods);
863 }
864 
865 ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, const std::vector<ShaderModifier*>& _modifierIDs)
866  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
867 {
868  init(_desc, _modifierIDs.empty() ? 0 : &(_modifierIDs[0]), (unsigned int)_modifierIDs.size());
869 }
870 
871 ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, const std::vector<ShaderModifier*>* _modifierIDs)
872  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
873 {
874  unsigned int numMods = !_modifierIDs || _modifierIDs->empty() ? 0 : (unsigned int)_modifierIDs->size();
875  init(_desc, numMods ? &((*_modifierIDs)[0]) : 0, numMods);
876 }
877 
878 
879 void ShaderProgGenerator::init( const ShaderGenDesc* _desc, const unsigned int* _modifierIDs, unsigned int _numActiveMods )
880 {
881  if (_modifierIDs && _numActiveMods)
882  {
883  activeMods_.resize(_numActiveMods);
884 
885  for (unsigned int i = 0; i < _numActiveMods; ++i)
886  activeMods_[i] = registeredModifiers_[ _modifierIDs[i] ];
887  }
888 
889  init(_desc, (ShaderModifier**)0, 0);
890 }
891 
892 void ShaderProgGenerator::init( const ShaderGenDesc* _desc, ShaderModifier* const* _modifiers, unsigned int _numActiveMods )
893 {
894  // mods provided by renderer are passed via parameters _modifiers, _numActiveMods
895  // mods provided by individual render objects are passed via ShaderGenDesc* _desc
896  // combine them
897  size_t numDescMods = _desc->shaderMods.size();
898  size_t numTotalMods = _numActiveMods + numDescMods;
899  if (numTotalMods)
900  {
901  activeMods_.resize(numTotalMods);
902 
903  for (size_t i = 0; i < numDescMods; ++i)
904  {
905  unsigned int modID = _desc->shaderMods[i];
906  activeMods_[i] = registeredModifiers_[modID];
907  }
908 
909  if (_modifiers && _numActiveMods)
910  {
911  for (unsigned int i = 0; i < _numActiveMods; ++i)
912  activeMods_[i + numDescMods] = _modifiers[i];
913  }
914  }
915 
916 
917 
918 
919  if (shaderDir_.isEmpty())
920  std::cout << "error: call ShaderProgGenerator::setShaderDir() first!" << std::endl;
921  else
922  {
923  desc_ = *_desc;
924 
925  // We need at least version 3.2 or higher to support geometry shaders
926  if ( !ACG::openGLVersion(3,2) )
927  {
928  if (!desc_.geometryTemplateFile.isEmpty())
929  std::cerr << "Warning: removing geometry shader from ShaderDesc" << std::endl;
930 
931  desc_.geometryTemplateFile.clear();
932  }
933 
934  // We need at least version 4.0 or higher to support tessellation
935  if ( !ACG::openGLVersion(4, 0) )
936  {
937  if (!desc_.tessControlTemplateFile.isEmpty() || !desc_.tessEvaluationTemplateFile.isEmpty())
938  std::cerr << "Warning: removing tessellation shader from ShaderDesc" << std::endl;
939 
940  desc_.tessControlTemplateFile.clear();
941  desc_.tessEvaluationTemplateFile.clear();
942  }
943 
944  // adjust glsl version to requirement
945 
946  if (!desc_.geometryTemplateFile.isEmpty())
947  desc_.version = std::max(desc_.version, 150);
948 
949  if (!desc_.tessControlTemplateFile.isEmpty() || !desc_.tessEvaluationTemplateFile.isEmpty())
950  desc_.version = std::max(desc_.version, 400);
951 
952 
953  loadLightingFunctions();
954 
955  generateShaders();
956  }
957 }
958 
959 
960 ShaderProgGenerator::~ShaderProgGenerator(void)
961 {
962  delete vertex_;
963  delete fragment_;
964  delete geometry_;
965  delete tessControl_;
966  delete tessEval_;
967 }
968 
969 
970 
971 bool ShaderProgGenerator::loadStringListFromFile(QString _fileName, QStringList* _out)
972 {
973  bool success = false;
974 
975  QString absFilename = getAbsFilePath(_fileName);
976 
977 
978  QFile file(absFilename);
979 
980  if (file.open(QIODevice::ReadOnly | QIODevice::Text))
981  {
982  if (!file.isReadable())
983  std::cout << "error: unreadable file -> \"" << absFilename.toStdString() << "\"" << std::endl;
984  else
985  {
986  QTextStream filestream(&file);
987 
988  while (!filestream.atEnd())
989  {
990  QString szLine = filestream.readLine();
991  _out->push_back(szLine.trimmed());
992  }
993 
994  success = true;
995  }
996 
997  file.close();
998  }
999  else
1000  std::cout << "error: " << file.errorString().toStdString() << " -> \"" << absFilename.toStdString() << "\"" << std::endl;
1001 
1002  return success;
1003 }
1004 
1005 
1006 void ShaderProgGenerator::loadLightingFunctions()
1007 {
1008  if (lightingCode_.size()) return;
1009 
1010  static const QString lightingCodeFile = "ShaderGen/SG_LIGHTING.GLSL";
1011 
1012  QString fileName = shaderDir_ + QDir::separator() + QString(lightingCodeFile);
1013 
1014  lightingCode_.push_back("// ==============================================================================");
1015  lightingCode_.push_back(QString("// ShaderGenerator - default lighting functions imported from file: ") + fileName);
1016 
1017 
1018  // load shader code from file
1019  loadStringListFromFile(fileName, &lightingCode_);
1020 
1021  lightingCode_.push_back(QString("// ShaderGenerator - end of default lighting functions"));
1022  lightingCode_.push_back("// ==============================================================================");
1023 }
1024 
1025 
1026 
1028 {
1029  switch (desc_.shadeMode)
1030  {
1031  case SG_SHADE_GOURAUD:
1032  _gen->addDefine("SG_GOURAUD 1"); break;
1033  case SG_SHADE_FLAT:
1034  _gen->addDefine("SG_FLAT 1"); break;
1035  case SG_SHADE_UNLIT:
1036  _gen->addDefine("SG_UNLIT 1"); break;
1037  case SG_SHADE_PHONG:
1038  _gen->addDefine("SG_PHONG 1"); break;
1039 
1040  default:
1041  std::cout << __FUNCTION__ << " -> unknown shade mode: " << desc_.shadeMode << std::endl;
1042  }
1043 
1044  if (desc_.textured())
1045  _gen->addDefine("SG_TEXTURED 1");
1046 
1047  if (desc_.vertexColors)
1048  _gen->addDefine("SG_VERTEX_COLOR 1");
1049 
1050 // if (desc_.shadeMode != SG_SHADE_UNLIT)
1051  if (ioDesc_.passNormalVS_)
1052  _gen->addDefine("SG_NORMALS 1");
1053 
1054  if (ioDesc_.passPosVS_)
1055  _gen->addDefine("SG_POSVS 1");
1056 
1057  if (ioDesc_.passPosOS_)
1058  _gen->addDefine("SG_POSOS 1");
1059 
1060  // # lights define
1061  QString strNumLights;
1062  strNumLights.sprintf("SG_NUM_LIGHTS %d", desc_.numLights);
1063  _gen->addDefine(strNumLights);
1064 
1065  // light types define
1066  const char* lightTypeNames[] = {"SG_LIGHT_DIRECTIONAL",
1067  "SG_LIGHT_POINT", "SG_LIGHT_SPOT"};
1068 
1069  for (int i = 0; i < 3; ++i)
1070  _gen->addDefine(lightTypeNames[i]);
1071 
1072 
1073  for (int i = 0; i < desc_.numLights; ++i)
1074  {
1075  QString strLightType;
1076  strLightType.sprintf("SG_LIGHT_TYPE_%d %s", i, lightTypeNames[desc_.lightTypes[i]]);
1077  _gen->addDefine(strLightType);
1078  }
1079 
1080  _gen->addDefine("SG_ALPHA g_vMaterial.y");
1081 
1082 
1083  _gen->addMacros(desc_.macros);
1084 }
1085 
1086 
1087 
1088 
1089 void ShaderProgGenerator::buildVertexShader()
1090 {
1091  delete vertex_;
1092 
1093  vertex_ = new ShaderGenerator();
1094  vertex_->setGLSLVersion(desc_.version);
1095 
1096  vertex_->initVertexShaderIO(&desc_, &ioDesc_);
1097 
1098  vertex_->initDefaultUniforms();
1099 
1100 
1101  if (desc_.texGenDim && (desc_.texGenMode == GL_OBJECT_LINEAR || desc_.texGenMode == GL_EYE_LINEAR) && !desc_.texGenPerFragment)
1102  {
1103  // application has to provide texture projection planes
1104  QString uniformDecl = "vec4 g_vTexGenPlane";
1105  if (desc_.texGenDim > 1)
1106  uniformDecl += "[" + QString::number(desc_.texGenDim) + "]";
1107  vertex_->addUniform(uniformDecl, " // texture projection planes");
1108  }
1109 
1110 
1111  // apply i/o modifiers
1112  for (size_t i = 0; i < activeMods_.size(); ++i)
1113  activeMods_[i]->modifyVertexIO(vertex_);
1114 
1115 
1116  initGenDefines(vertex_);
1117 
1118 
1119 
1120  // IO
1121 
1122  // when to use vertex lights
1123  if (desc_.shadeMode == SG_SHADE_GOURAUD ||
1124  desc_.shadeMode == SG_SHADE_FLAT)
1125  {
1126  for (int i = 0; i < desc_.numLights; ++i)
1127  vertex_->addLight(i, desc_.lightTypes[i]);
1128  }
1129 
1130 
1131  // assemble main function
1132  QStringList mainCode;
1133 
1134  if (!vertexTemplate_.size())
1135  {
1136  mainCode.push_back("void main()");
1137  mainCode.push_back("{");
1138 
1139  addVertexBeginCode(&mainCode);
1140  addVertexEndCode(&mainCode);
1141 
1142  mainCode.push_back("}");
1143  }
1144  else
1145  {
1146  // interpret loaded shader template:
1147  // import #includes and replace SG_VERTEX_BEGIN/END markers
1148 
1149  QString it;
1150  foreach(it,vertexTemplate_)
1151  {
1152  if (!checkForIncludes(it, vertex_, getPathName(vertexShaderFile_)))
1153  {
1154  // str line is no include directive
1155  // check for SG_ markers
1156 
1157  if (it.contains("SG_VERTEX_BEGIN"))
1158  addVertexBeginCode(&mainCode);
1159  else
1160  {
1161  if (it.contains("SG_VERTEX_END"))
1162  addVertexEndCode(&mainCode);
1163  else
1164  {
1165  // no SG marker
1166  mainCode.push_back(it);
1167  }
1168  }
1169 
1170  }
1171  }
1172 
1173  }
1174 
1175  vertex_->buildShaderCode(&mainCode, lightingCode_);
1176 
1177 }
1178 
1179 
1180 void ShaderProgGenerator::addVertexBeginCode(QStringList* _code)
1181 {
1182  // size in pixel of rendered point-lists, set by user via uniform
1183 
1184  _code->push_back(QString("vec4 sg_vPosPS = g_mWVP * ") + ShaderGenerator::keywords.macro_inputPosOS + QString(";"));
1185  _code->push_back("vec4 sg_vPosVS = g_mWV * inPosition;");
1186  _code->push_back("vec3 sg_vNormalVS = vec3(0.0, 1.0, 0.0);");
1187  _code->push_back("vec3 sg_vNormalOS = vec3(0.0, 1.0, 0.0);");
1188 
1189  if (desc_.vertexColors && (desc_.colorMaterialMode == GL_AMBIENT || desc_.colorMaterialMode == GL_AMBIENT_AND_DIFFUSE))
1190  _code->push_back(QString("vec4 sg_cColor = vec4(g_cEmissive + g_cLightModelAmbient * ") + ShaderGenerator::keywords.macro_inputVertexColor + QString(".xyz, SG_ALPHA);"));
1191  else
1192  _code->push_back("vec4 sg_cColor = vec4(g_cEmissive + g_cLightModelAmbient * g_cAmbient, SG_ALPHA);");
1193 
1194  if (ioDesc_.inputNormal_)
1195  {
1196  _code->push_back("sg_vNormalVS = normalize(g_mWVIT * inNormal);");
1197  _code->push_back("sg_vNormalOS = normalize(inNormal);");
1198  }
1199 
1200  if (ioDesc_.inputColor_ && (desc_.shadeMode == SG_SHADE_UNLIT || desc_.colorMaterialMode == GL_EMISSION))
1201  _code->push_back(QString("sg_cColor = ") + ShaderGenerator::keywords.macro_inputVertexColor + QString(";"));
1202 
1203  // texcoord generation
1204  addTexGenCode(_code, false);
1205 
1206 
1207  // apply modifiers
1208  for (size_t i = 0; i < activeMods_.size(); ++i)
1209  activeMods_[i]->modifyVertexBeginCode(_code);
1210 }
1211 
1212 
1213 void ShaderProgGenerator::addVertexEndCode(QStringList* _code)
1214 {
1215  if (desc_.shadeMode == SG_SHADE_GOURAUD ||
1216  desc_.shadeMode == SG_SHADE_FLAT)
1217  {
1218  // add lighting code here
1219 
1220  addLightingCode(_code);
1221  }
1222 
1223  _code->push_back("gl_Position = sg_vPosPS;");
1224  _code->push_back("outVertexPosCS = sg_vPosPS;");
1225 
1226  if (ioDesc_.passTexCoord_)
1227  _code->push_back("outVertexTexCoord = sg_vTexCoord;");
1228 
1229  if (ioDesc_.passColor_)
1230  _code->push_back("outVertexColor = sg_cColor;");
1231 
1232  if (ioDesc_.passNormalVS_)
1233  _code->push_back(ShaderGenerator::keywords.macro_outputNormalVS + QString(" = sg_vNormalVS;"));
1234 
1235  if (ioDesc_.passNormalOS_)
1236  _code->push_back(ShaderGenerator::keywords.macro_outputNormalOS + QString(" = sg_vNormalOS;"));
1237 
1238  if (ioDesc_.passPosVS_)
1239  _code->push_back(ShaderGenerator::keywords.macro_outputPosVS + QString(" = sg_vPosVS;"));
1240 
1241  if (ioDesc_.passPosOS_)
1242  _code->push_back(ShaderGenerator::keywords.macro_outputPosOS + QString(" = ") + ShaderGenerator::keywords.macro_inputPosOS + QString(";"));
1243 
1244 
1245 
1246  // apply modifiers
1247  for (size_t i = 0; i < activeMods_.size(); ++i)
1248  activeMods_[i]->modifyVertexEndCode(_code);
1249 }
1250 
1251 
1252 int ShaderProgGenerator::checkForIncludes(QString _str, ShaderGenerator* _gen, QString _includePath)
1253 {
1254  if (_str.contains("#include "))
1255  {
1256  QString strIncludeFile = _str.remove("#include ").remove('\"').remove('<').remove('>').trimmed();
1257 
1258  if (strIncludeFile.isEmpty())
1259  std::cout << "wrong include syntax: " << _str.toStdString() << std::endl;
1260  else
1261  {
1262  QString fullPathToIncludeFile = _includePath + QDir::separator() + strIncludeFile;
1263 
1264  _gen->addIncludeFile(fullPathToIncludeFile);
1265  }
1266 
1267  return 1;
1268  }
1269 
1270  return 0;
1271 }
1272 
1273 int ShaderProgGenerator::checkForIncludes(QString _str, QStringList* _outImport, QString _includePath)
1274 {
1275  if (_str.contains("#include "))
1276  {
1277  QString strIncludeFile = _str.remove("#include ").remove('\"').remove('<').remove('>').trimmed();
1278 
1279  if (strIncludeFile.isEmpty())
1280  std::cout << "wrong include syntax: " << _str.toStdString() << std::endl;
1281  else
1282  {
1283  QString fullPathToIncludeFile = _includePath + QDir::separator() + strIncludeFile;
1284 
1285  // unify separator chars
1286  fullPathToIncludeFile.replace('\\', '/');
1287 
1288  // get rid of ".." usage inside shader includes
1289  QString cleanFilepath = QDir::cleanPath(fullPathToIncludeFile);
1290 
1291  loadStringListFromFile(cleanFilepath, _outImport);
1292  }
1293 
1294  return 1;
1295  }
1296 
1297  return 0;
1298 }
1299 
1300 void ShaderProgGenerator::buildTessControlShader()
1301 {
1302  // Only build a tess-control shader if enabled
1303  if ( desc_.tessControlTemplateFile.isEmpty() )
1304  return;
1305 
1306  // the generator provides an IO mapping function and adds default uniforms to this stage
1307  // - template is necessary
1308  // - combination/modification of tess-control shader is not supported
1309  // - template may call sg_MapIO(inId) somewhere in code to take care of default IO pass-through
1310  // this function reads elements from gl_in[inID] and writes them to elements of gl_out[gl_InvocationID]
1311  // inId can be gl_InvocationID if the patch size is not modified
1312 
1313  delete tessControl_;
1314 
1315  tessControl_ = new ShaderGenerator();
1316  tessControl_->setGLSLVersion(desc_.version);
1317 
1318  QString it;
1319  foreach(it, tessControlLayout_)
1320  tessControl_->addLayout(it);
1321 
1322  // find previous shader stage
1323  ShaderGenerator* prevStage = vertex_;
1324 
1325  tessControl_->initTessControlShaderIO(&desc_, prevStage, &ioDesc_);
1326 
1327  tessControl_->initDefaultUniforms();
1328 
1329 
1330  // apply i/o modifiers
1331  for (size_t i = 0; i < activeMods_.size(); ++i)
1332  activeMods_[i]->modifyTessControlIO(tessControl_);
1333 
1334  initGenDefines(tessControl_);
1335 
1336 
1337 
1338  // assemble main function
1339  QStringList mainCode;
1340 
1341  // add simple io passthrough mapper
1342 
1343  {
1344  mainCode.push_back("void sg_MapIO(const int inIdx)");
1345  mainCode.push_back("{");
1346 
1347  // built-in IO
1348  mainCode.push_back("gl_out[gl_InvocationID].gl_Position = gl_in[inIdx].gl_Position;");
1349 
1350  // custom IO
1351  for (int i = 0; i < tessControl_->getNumInputs(); ++i)
1352  {
1353  QString inputName = tessControl_->getInputName(i);
1354  QString outputName = tessControl_->getIOMapName(i);
1355 
1356  QString outputAssignCode = outputName + QString("[gl_InvocationID] = ") + inputName + QString("[inIdx];");
1357 
1358  mainCode.push_back(outputAssignCode);
1359  }
1360 
1361  mainCode.push_back("}");
1362  }
1363 
1364 
1365  // interpret loaded shader template:
1366  // import #includes
1367  foreach(it,tessControlTemplate_)
1368  {
1369  if (!checkForIncludes(it, tessControl_, getPathName(tessControlShaderFile_)))
1370  {
1371  // str line is no include directive
1372  mainCode.push_back(it);
1373  }
1374  }
1375 
1376  tessControl_->buildShaderCode(&mainCode, lightingCode_);
1377 }
1378 
1379 void ShaderProgGenerator::buildTessEvalShader()
1380 {
1381  // Only build a tess-eval shader if enabled
1382  if ( desc_.tessEvaluationTemplateFile.isEmpty() )
1383  return;
1384 
1385  // the generator provides default interpolation functions and adds default uniforms to this stage
1386  // - template is necessary
1387  // - combination/modification of tess-eval shader is not supported
1388  // - template may call sg_MapIOBarycentric() or sg_MapIOBilinear() somewhere in code to take care of default IO pass-through
1389  // - barycentric interpolation can be used for triangle patches
1390  // - bilinear interpolation can be used for quad patches
1391  // - other interpolation schemes have to be user defined
1392 
1393  delete tessEval_;
1394 
1395  tessEval_ = new ShaderGenerator();
1396  tessEval_->setGLSLVersion(desc_.version);
1397 
1398 
1399  // find previous shader stage
1400  ShaderGenerator* prevStage = tessControl_;
1401 
1402  if (!prevStage)
1403  prevStage = vertex_;
1404 
1405  tessEval_->initTessEvalShaderIO(&desc_, prevStage, &ioDesc_);
1406 
1407  tessEval_->initDefaultUniforms();
1408 
1409  QString itLayout;
1410  foreach(itLayout, tessEvalLayout_)
1411  tessEval_->addLayout(itLayout);
1412 
1413  // apply i/o modifiers
1414  for (size_t i = 0; i < activeMods_.size(); ++i)
1415  activeMods_[i]->modifyTessControlIO(tessEval_);
1416 
1417  initGenDefines(tessEval_);
1418 
1419 
1420  // assemble main function
1421  QStringList mainCode;
1422 
1423  // add simple io passthrough mapper
1424 
1425  {
1426  // barycentric interpolation
1427 
1428  mainCode.push_back("void sg_MapIOBarycentric()");
1429  mainCode.push_back("{");
1430 
1431  // built-in IO
1432  mainCode.push_back("gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;");
1433 
1434  // custom IO
1435  for (int i = 0; i < tessEval_->getNumInputs(); ++i)
1436  {
1437  QString inputName = tessEval_->getInputName(i);
1438  QString outputName = tessEval_->getIOMapName(i);
1439 
1440  QString outputAssignCode = outputName + QString(" = ") +
1441  QString("gl_TessCoord.x*") + inputName + QString("[0] + ") +
1442  QString("gl_TessCoord.y*") + inputName + QString("[1] + ") +
1443  QString("gl_TessCoord.z*") + inputName + QString("[2];");
1444 
1445  mainCode.push_back(outputAssignCode);
1446  }
1447 
1448  mainCode.push_back("}");
1449 
1450 
1451  // bilinear interpolation
1452 
1453  mainCode.push_back("void sg_MapIOBilinear()");
1454  mainCode.push_back("{");
1455 
1456  // built-in IO
1457  mainCode.push_back("gl_Position = mix( mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x), mix(gl_in[2].gl_Position, gl_in[3].gl_Position, gl_TessCoord.x), gl_TessCoord.y);");
1458 
1459  // custom IO
1460  for (int i = 0; i < tessEval_->getNumInputs(); ++i)
1461  {
1462  QString inputName = tessEval_->getInputName(i);
1463  QString outputName = tessEval_->getIOMapName(i);
1464 
1465  QString outputAssignCode = outputName + QString(" = mix( ") +
1466  QString("mix(") + inputName + QString("[0], ") + inputName + QString("[1], gl_TessCoord.x), ") +
1467  QString("mix(") + inputName + QString("[2], ") + inputName + QString("[3], gl_TessCoord.x), gl_TessCoord.y); ");
1468 
1469  mainCode.push_back(outputAssignCode);
1470  }
1471 
1472  mainCode.push_back("}");
1473  }
1474 
1475 
1476  // interpret loaded shader template:
1477  // replace (SG_INPUT, SG_OUTPUT) with matching io pairs
1478  QStringList::iterator it;
1479  for (it = tessEvalTemplate_.begin(); it != tessEvalTemplate_.end(); ++it)
1480  {
1481  QString line = *it;
1482 
1483  // replace IO line matching the pattern:
1484  // SG_OUTPUT = r_expression(SG_INPUT);
1485  // the complete expression must be contained in a single line for this to work
1486  // more complex interpolation code should use #if SG_NORMALS etc.
1487 
1488  if (line.contains("SG_INPUT") || line.contains("SG_OUTPUT"))
1489  {
1490 
1491  QStringList resolvedCode;
1492 
1493  resolvedCode.push_back("// ----------------------------------------");
1494  resolvedCode.push_back("// ShaderGen: resolve SG_OUTPUT = expression(SG_INPUT);");
1495 
1496  int numOccurrences = 0;
1497 
1498  for (int i = 0; i < tessEval_->getNumInputs(); ++i)
1499  {
1500  QString inputName = tessEval_->getInputName(i);
1501  QString outputName = tessEval_->getIOMapName(i);
1502 
1503  // replace SG_INPUT, SG_OUTPUT with actual names
1504  QString resolvedLine = line;
1505 
1506  // avoid confusion with SG_INPUT_NORMALVS etc. naming convention
1507  // resolvedLine.replace("SG_INPUT", inputName);
1508  // resolvedLine.replace("SG_OUTPUT", outputName);
1509  // fails to do this
1510 
1511  // maybe this can be simplified with regexp
1512  // ie. replace SG_INPUT with inputName, but not SG_INPUTN, SG_INPUT_ ..
1513 
1514  for (int k = 0; k < 2; ++k)
1515  {
1516  const QString stringToReplace = k ? "SG_OUTPUT" : "SG_INPUT";
1517  const int lenStringToReplace = stringToReplace.length();
1518  const QString replacementString = k ? outputName : inputName;
1519 
1520  int linePos = resolvedLine.indexOf(stringToReplace);
1521 
1522  while (linePos >= 0)
1523  {
1524  bool replaceOcc = true;
1525 
1526  int nextCharPos = linePos + lenStringToReplace;
1527 
1528  if (nextCharPos >= resolvedLine.size())
1529  nextCharPos = -1;
1530 
1531  if (nextCharPos > 0)
1532  {
1533  QChar nextChar = resolvedLine.at(nextCharPos);
1534 
1535  if (nextChar == '_' || nextChar.isDigit() || nextChar.isLetter())
1536  {
1537  // name token continues, this should not be replaced!
1538 
1539  linePos += lenStringToReplace;
1540  replaceOcc = false;
1541  }
1542  }
1543 
1544  // replace
1545 
1546  if (replaceOcc)
1547  {
1548  resolvedLine.replace(linePos, lenStringToReplace, replacementString);
1549  ++numOccurrences;
1550  }
1551 
1552  linePos = resolvedLine.indexOf(stringToReplace, linePos + 1);
1553  }
1554  }
1555 
1556 
1557 
1558 
1559 
1560  resolvedCode.push_back(resolvedLine);
1561  }
1562 
1563  resolvedCode.push_back("// ----------------------------------------");
1564 
1565  if (numOccurrences)
1566  mainCode.append(resolvedCode);
1567  else
1568  mainCode.push_back(line); // nothing to replace in this line
1569  }
1570  else
1571  mainCode.push_back(line);
1572  }
1573 
1574  tessEval_->buildShaderCode(&mainCode, lightingCode_);
1575 }
1576 
1577 void ShaderProgGenerator::buildGeometryShader()
1578 {
1579  // Only build a geometry shader if enabled
1580  if ( desc_.geometryTemplateFile.isEmpty() )
1581  return;
1582 
1583 
1584  delete geometry_;
1585 
1586  geometry_ = new ShaderGenerator();
1587  geometry_->setGLSLVersion(desc_.version);
1588 
1589 
1590  // find previous shader stage
1591  ShaderGenerator* prevStage = tessEval_;
1592 
1593  if (!prevStage)
1594  prevStage = vertex_;
1595 
1596  geometry_->initGeometryShaderIO(&desc_, prevStage, &ioDesc_);
1597 
1598  geometry_->initDefaultUniforms();
1599 
1600 
1601  // apply i/o modifiers
1602  for (size_t i = 0; i < activeMods_.size(); ++i)
1603  activeMods_[i]->modifyGeometryIO(geometry_);
1604 
1605  initGenDefines(geometry_);
1606 
1607 
1608  // assemble main function
1609  QStringList mainCode;
1610 
1611  // add simple io passthrough mapper
1612 
1613  {
1614  mainCode.push_back("void sg_MapIO(const int inIdx)");
1615  mainCode.push_back("{");
1616 
1617  // built-in IO
1618  mainCode.push_back("gl_Position = gl_in[inIdx].gl_Position;");
1619  mainCode.push_back("gl_PrimitiveID = gl_PrimitiveIDIn;");
1620 
1621  // custom IO
1622  for (int i = 0; i < geometry_->getNumInputs(); ++i)
1623  {
1624  QString inputName = geometry_->getInputName(i);
1625  QString outputName = geometry_->getIOMapName(i);
1626 
1627  QString outputAssignCode = outputName + QString(" = ") + inputName + QString("[inIdx];");
1628 
1629  mainCode.push_back(outputAssignCode);
1630  }
1631 
1632  mainCode.push_back("}");
1633  }
1634 
1635 
1636  // interpret loaded shader template:
1637  // import #includes
1638  QString it;
1639  foreach(it,geometryTemplate_)
1640  {
1641  if (!checkForIncludes(it, geometry_, getPathName(geometryShaderFile_)))
1642  {
1643  // str line is no include directive
1644  mainCode.push_back(it);
1645  }
1646  }
1647 
1648  geometry_->buildShaderCode(&mainCode, lightingCode_);
1649 }
1650 
1651 
1652 void ShaderProgGenerator::buildFragmentShader()
1653 {
1654  delete fragment_;
1655 
1656  fragment_ = new ShaderGenerator();
1657  fragment_->setGLSLVersion(desc_.version);
1658 
1659  // find previous shader stage
1660  ShaderGenerator* prevStage = geometry_;
1661 
1662  if (!prevStage)
1663  prevStage = tessEval_;
1664  if (!prevStage)
1665  prevStage = tessControl_;
1666  if (!prevStage)
1667  prevStage = vertex_;
1668 
1669 
1670  fragment_->initFragmentShaderIO(&desc_, prevStage, &ioDesc_);
1671 
1672  if (desc_.texGenDim && (desc_.texGenMode == GL_OBJECT_LINEAR || desc_.texGenMode == GL_EYE_LINEAR) && desc_.texGenPerFragment)
1673  {
1674  // application has to provide texture projection planes
1675  QString uniformDecl = "vec4 g_vTexGenPlane";
1676  if (desc_.texGenDim > 1)
1677  uniformDecl += "[" + QString::number(desc_.texGenDim) + "]";
1678  fragment_->addUniform(uniformDecl, " // texture projection planes");
1679  }
1680 
1681 
1682  fragment_->initDefaultUniforms();
1683 
1684 
1685  // texture sampler id
1686  if (desc_.textured())
1687  {
1688  for (std::map<size_t,ShaderGenDesc::TextureType>::const_iterator iter = desc_.textureTypes().begin();
1689  iter != desc_.textureTypes().end(); ++iter)
1690  {
1691  QString name = QString("g_Texture%1").arg(iter->first);
1692  QString type = "";
1693  switch (iter->second.type)
1694  {
1695  case GL_TEXTURE_1D: type = "sampler1D"; break;
1696  case GL_TEXTURE_2D: type = "sampler2D"; break;
1697  case GL_TEXTURE_3D: type = "sampler3D"; break;
1698  case GL_TEXTURE_CUBE_MAP: type = "samplerCube"; break;
1699 #ifdef GL_ARB_texture_rectangle //ARCH_DARWIN doesn't support all texture defines with all xcode version (xcode 5.0 seems to support all)
1700  case GL_TEXTURE_RECTANGLE_ARB: type = "sampler2DRect"; break;
1701 #endif
1702 #ifdef GL_ARB_texture_buffer_object
1703  case GL_TEXTURE_BUFFER_ARB: type = "samplerBuffer"; break;
1704 #endif
1705 #ifdef GL_EXT_texture_array
1706  case GL_TEXTURE_1D_ARRAY_EXT: type = "sampler1DArray"; break;
1707  case GL_TEXTURE_2D_ARRAY_EXT: type = "sampler2DArray"; break;
1708 #endif
1709 #ifdef GL_ARB_texture_cube_map_array
1710  case GL_TEXTURE_CUBE_MAP_ARRAY_ARB: type = "samplerCubeArray"; break;
1711 #endif
1712 #ifdef GL_ARB_texture_multisample
1713  case GL_TEXTURE_2D_MULTISAMPLE: type = "sampler2DMS"; break;
1714  case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: type = "sampler2DMSArray"; break;
1715 #endif
1716  default: std::cerr << "Texture Type not supported "<< iter->second.type << std::endl; break;
1717  }
1718  // todo: check if texture type supports shadowtype
1719  if (iter->second.shadow)
1720  type += "Shadow";
1721  fragment_->addUniform(type + " " + name);
1722  }
1723  }
1724 
1725  // apply i/o modifiers
1726  for (size_t i = 0; i < activeMods_.size(); ++i)
1727  activeMods_[i]->modifyFragmentIO(fragment_);
1728 
1729 
1730  initGenDefines(fragment_);
1731 
1732 
1733 
1734  // io
1735 
1736  // when to use fragment lights
1737  if (desc_.shadeMode == SG_SHADE_PHONG)
1738  {
1739  for (int i = 0; i < desc_.numLights; ++i)
1740  fragment_->addLight(i, desc_.lightTypes[i]);
1741  }
1742 
1743  // assemble main function
1744  QStringList mainCode;
1745 
1746  if (!fragmentTemplate_.size())
1747  {
1748  mainCode.push_back("void main()");
1749  mainCode.push_back("{");
1750 
1751  addFragmentBeginCode(&mainCode);
1752  addFragmentEndCode(&mainCode);
1753 
1754  mainCode.push_back("}");
1755  }
1756  else
1757  {
1758  // interpret loaded shader template:
1759  // import #includes and replace SG_VERTEX_BEGIN/END markers
1760  QString it;
1761  foreach(it,fragmentTemplate_)
1762  {
1763  if (!checkForIncludes(it, fragment_, getPathName(fragmentShaderFile_)))
1764  {
1765  // str line is no include directive
1766  // check for SG_ markers
1767 
1768  if (it.contains("SG_FRAGMENT_BEGIN"))
1769  addFragmentBeginCode(&mainCode);
1770  else if (it.contains("SG_FRAGMENT_END"))
1771  addFragmentEndCode(&mainCode);
1772  else if (it.contains("SG_FRAGMENT_LIGHTING"))
1773  addLightingCode(&mainCode);
1774  else // no SG marker
1775  mainCode.push_back(it);
1776 
1777  }
1778 
1779 
1780  }
1781 
1782  }
1783 
1784 
1785 
1786  fragment_->buildShaderCode(&mainCode, lightingCode_);
1787 }
1788 
1789 
1790 void ShaderProgGenerator::addFragmentBeginCode(QStringList* _code)
1791 {
1792  // support for projective texture mapping
1793  _code->push_back(QString("vec4 sg_vPosCS = ") + ShaderGenerator::keywords.macro_inputPosCS + QString(";"));
1794  _code->push_back("vec2 sg_vScreenPos = sg_vPosCS.xy / sg_vPosCS.w * 0.5 + vec2(0.5, 0.5);");
1795 
1796  _code->push_back(QString("#ifdef ") + ShaderGenerator::keywords.macro_inputPosVS);
1797  _code->push_back(QString("vec4 sg_vPosVS = ") + ShaderGenerator::keywords.macro_inputPosVS + QString(";"));
1798  _code->push_back("#endif");
1799 
1800  _code->push_back(QString("#ifdef ") + ShaderGenerator::keywords.macro_inputNormalVS);
1801  _code->push_back(QString("vec3 sg_vNormalVS = ") + ShaderGenerator::keywords.macro_inputNormalVS + QString(";"));
1803  _code->push_back("sg_vNormalVS = normalize(sg_vNormalVS);");
1804  _code->push_back("#endif");
1805 
1806 
1807  if (desc_.vertexColors && (desc_.colorMaterialMode == GL_AMBIENT || desc_.colorMaterialMode == GL_AMBIENT_AND_DIFFUSE))
1808  _code->push_back(QString("vec4 sg_cColor = vec4(g_cEmissive + g_cLightModelAmbient * ") + ShaderGenerator::keywords.macro_inputVertexColor + QString(".xyz, SG_ALPHA);"));
1809  else
1810  _code->push_back("vec4 sg_cColor = vec4(g_cEmissive + g_cLightModelAmbient * g_cAmbient, SG_ALPHA);");
1811 
1812  if (desc_.shadeMode == SG_SHADE_GOURAUD ||
1813  desc_.shadeMode == SG_SHADE_FLAT ||
1814  (ioDesc_.passColor_ && (desc_.shadeMode == SG_SHADE_UNLIT || desc_.colorMaterialMode == GL_EMISSION)))
1815  _code->push_back(QString("sg_cColor = ") + ShaderGenerator::keywords.macro_inputVertexColor + QString(";"));
1816 
1817  if (desc_.shadeMode == SG_SHADE_PHONG)
1818  addLightingCode(_code);
1819 
1820 
1821  addTexGenCode(_code, true);
1822 
1823  if (desc_.textured())
1824  {
1825  std::map<size_t,ShaderGenDesc::TextureType>::const_iterator iter = desc_.textureTypes().begin();
1826  _code->push_back("vec4 sg_cTex = texture(g_Texture"+QString::number(iter->first)+", sg_vTexCoord);");
1827 
1828  for (++iter; iter != desc_.textureTypes().end(); ++iter)
1829  _code->push_back("sg_cTex += texture(g_Texture"+QString::number(iter->first)+", sg_vTexCoord);");
1830 
1831  if (desc_.textureTypes().size() > 1 && desc_.normalizeTexColors)
1832  _code->push_back("sg_cTex = sg_cTex * 1.0/" + QString::number(desc_.textureTypes().size()) +".0 ;");
1833 
1834  if (desc_.shadeMode == SG_SHADE_UNLIT)
1835  _code->push_back("sg_cColor += sg_cTex;");
1836  else
1837  _code->push_back("sg_cColor *= sg_cTex;");
1838  }
1839 
1840 
1841  // apply modifiers
1842  for (size_t i = 0; i < activeMods_.size(); ++i)
1843  activeMods_[i]->modifyFragmentBeginCode(_code);
1844 }
1845 
1846 void ShaderProgGenerator::addFragmentEndCode(QStringList* _code)
1847 {
1848  _code->push_back(ShaderGenerator::keywords.fs_outputFragmentColor + QString(" = sg_cColor;"));
1849 
1850  // apply modifiers
1851  for (size_t i = 0; i < activeMods_.size(); ++i)
1852  activeMods_[i]->modifyFragmentEndCode(_code);
1853 }
1854 
1855 
1856 
1858 {
1859 
1860  ShaderModifier* lightingModifier = 0;
1861 
1862  // check if any modifier replaces the default lighting function
1863  for (size_t i = 0; i < activeMods_.size() && !lightingModifier; ++i)
1864  {
1865  if (activeMods_[i]->replaceDefaultLightingCode())
1866  lightingModifier = activeMods_[i];
1867  }
1868 
1869  if (!lightingModifier)
1870  {
1871  // default lighting code:
1872 
1873  QString buf;
1874 
1875  QString vertexColorString = (ioDesc_.inputColor_ && ioDesc_.passColor_) ? (ShaderGenerator::keywords.macro_inputVertexColor + QString(".xyz * ")) : "";
1876  QString diffuseVertexColor = (desc_.colorMaterialMode == GL_DIFFUSE || desc_.colorMaterialMode == GL_AMBIENT_AND_DIFFUSE) ? vertexColorString : "";
1877  QString ambientVertexColor = (desc_.colorMaterialMode == GL_AMBIENT || desc_.colorMaterialMode == GL_AMBIENT_AND_DIFFUSE) ? vertexColorString : "";
1878  QString specularVertexColor = (desc_.colorMaterialMode == GL_SPECULAR) ? vertexColorString : "";
1879 
1880  for (int i = 0; i < desc_.numLights; ++i)
1881  {
1882  ShaderGenLightType lgt = desc_.lightTypes[i];
1883 
1884  switch (lgt)
1885  {
1886  case SG_LIGHT_DIRECTIONAL:
1887 // buf.sprintf("sg_cColor.xyz += LitDirLight(sg_vPosVS.xyz, sg_vNormalVS, g_vLightDir_%d, %s g_cLightAmbient_%d, %s g_cLightDiffuse_%d, %s g_cLightSpecular_%d);", i, ambientVertexColor, i, diffuseVertexColor, i, specularVertexColor, i);
1888  buf = QString("sg_cColor.xyz += LitDirLight(sg_vPosVS.xyz, sg_vNormalVS, g_vLightDir_%1, %2 g_cLightAmbient_%1, %3 g_cLightDiffuse_%1, %4 g_cLightSpecular_%1);").arg(QString::number(i), ambientVertexColor, diffuseVertexColor, specularVertexColor);
1889  break;
1890 
1891  case SG_LIGHT_POINT:
1892 // buf.sprintf("sg_cColor.xyz += LitPointLight(sg_vPosVS.xyz, sg_vNormalVS, g_vLightPos_%d, %s g_cLightAmbient_%d, %s g_cLightDiffuse_%d, %s g_cLightSpecular_%d, g_vLightAtten_%d);", i, ambientVertexColor, i, diffuseVertexColor, i, specularVertexColor, i, i);
1893  buf = QString("sg_cColor.xyz += LitPointLight(sg_vPosVS.xyz, sg_vNormalVS, g_vLightPos_%1, %2 g_cLightAmbient_%1, %3 g_cLightDiffuse_%1, %4 g_cLightSpecular_%1, g_vLightAtten_%1);").arg(QString::number(i), ambientVertexColor, diffuseVertexColor, specularVertexColor);
1894  break;
1895 
1896  case SG_LIGHT_SPOT:
1897 // buf.sprintf("sg_cColor.xyz += LitSpotLight(sg_vPosVS.xyz, sg_vNormalVS, g_vLightPos_%d, g_vLightDir_%d, %s g_cLightAmbient_%d, %s g_cLightDiffuse_%d, %s g_cLightSpecular_%d, g_vLightAtten_%d, g_vLightAngleExp_%d);", i, i, ambientVertexColor, i, diffuseVertexColor, i, specularVertexColor, i, i, i);
1898  buf = QString("sg_cColor.xyz += LitSpotLight(sg_vPosVS.xyz, sg_vNormalVS, g_vLightPos_%1, g_vLightDir_%1, %2 g_cLightAmbient_%1, %3 g_cLightDiffuse_%1, %4 g_cLightSpecular_%1, g_vLightAtten_%1, g_vLightAngleExp_%1);").arg(QString::number(i), ambientVertexColor, diffuseVertexColor, specularVertexColor);
1899  break;
1900 
1901  default: break;
1902  }
1903 
1904  _code->push_back(buf);
1905  }
1906 
1907  // modify lighting color afterwards
1908 
1909  for (size_t i = 0; i < activeMods_.size(); ++i)
1910  modifyLightingCode(_code, activeMods_[i]);
1911  }
1912  else
1913  {
1914  // there exists a lighting modifier that completely replaces the default lighting shader
1915  modifyLightingCode(_code, lightingModifier);
1916 
1917 
1918  // apply remaining modifiers that do not replace the complete lighting code
1919 
1920  for (size_t i = 0; i < activeMods_.size(); ++i)
1921  {
1922  if (lightingModifier != activeMods_[i])
1923  modifyLightingCode(_code, activeMods_[i]);
1924  }
1925  }
1926 
1927 }
1928 
1929 void ShaderProgGenerator::modifyLightingCode( QStringList* _code, ShaderModifier* _modifier )
1930 {
1931  if (!_modifier) return;
1932 
1933  for (int i = 0; i < desc_.numLights; ++i)
1934  {
1935  ShaderGenLightType lgt = desc_.lightTypes[i];
1936 
1937  _modifier->modifyLightingCode(_code, i, lgt);
1938  }
1939 }
1940 
1941 
1943 {
1944  QString it;
1945  foreach(it,lightingCode_)
1946  _code->push_back(it);
1947 }
1948 
1949 
1950 void ShaderProgGenerator::addTexGenCode( QStringList* _code, bool _fragmentShader )
1951 {
1952  // declare local texcoord variable name as "sg_vTexCoord"
1953  int texcoordVarDim = 2;
1954  if (ioDesc_.inputTexCoord_ &&
1955  !desc_.textureTypes().empty() &&
1956  desc_.textureTypes().begin()->second.type == GL_TEXTURE_3D)
1957  texcoordVarDim = 3;
1958 
1959  bool generateTexCoord = desc_.texGenDim && desc_.texGenMode && (_fragmentShader == desc_.texGenPerFragment);
1960  if (generateTexCoord)
1961  texcoordVarDim = desc_.texGenDim;
1962 
1963  QString texcoordVarInit;
1964  if (texcoordVarDim == 1)
1965  texcoordVarInit = "float sg_vTexCoord";
1966  else
1967  texcoordVarInit.sprintf("vec%i sg_vTexCoord", texcoordVarDim);
1968 
1969  // init with default value: input or zero
1970  if (ioDesc_.inputTexCoord_ && !generateTexCoord)
1971  texcoordVarInit += QString("= ") + ShaderGenerator::keywords.macro_inputTexcoord + QString(";");
1972  else if (0 <= texcoordVarDim && texcoordVarDim <= 4)
1973  {
1974  QString zeroVecDefs[] =
1975  {
1976  ";",
1977  "= 0;",
1978  "= vec2(0,0);",
1979  "= vec3(0,0,0);",
1980  "= vec4(0,0,0,0);"
1981  };
1982  texcoordVarInit += zeroVecDefs[texcoordVarDim];
1983  }
1984 
1985  _code->push_back(texcoordVarInit);
1986 
1987 
1988  // texcoord generation
1989  // https://www.opengl.org/wiki/Mathematics_of_glTexGen
1990  if (generateTexCoord)
1991  {
1992 
1993  const char* texGenCoordString[] = { "x", "y", "z", "w" };
1994 
1995  switch (desc_.texGenMode)
1996  {
1997  case GL_OBJECT_LINEAR:
1998  {
1999  for (int i = 0; i < desc_.texGenDim; ++i)
2000  {
2001  QString assignmentInstrString;
2002  assignmentInstrString = "sg_vTexCoord";
2003  if (desc_.texGenDim > 1)
2004  {
2005  assignmentInstrString +=".";
2006  assignmentInstrString += texGenCoordString[i];
2007  }
2008  assignmentInstrString += " = dot(";
2009  assignmentInstrString += ShaderGenerator::keywords.macro_inputPosOS;
2010  assignmentInstrString += ", g_vTexGenPlane";
2011  if (desc_.texGenDim > 1)
2012  {
2013  assignmentInstrString += "[";
2014  assignmentInstrString += QString::number(i);
2015  assignmentInstrString += "]";
2016  }
2017  assignmentInstrString += ");";
2018  _code->push_back(assignmentInstrString);
2019  }
2020  } break;
2021 
2022  case GL_EYE_LINEAR:
2023  {
2024  for (int i = 0; i < desc_.texGenDim; ++i)
2025  {
2026  QString assignmentInstrString;
2027  assignmentInstrString = "sg_vTexCoord";
2028  if (desc_.texGenDim > 1)
2029  {
2030  assignmentInstrString += ".";
2031  assignmentInstrString += texGenCoordString[i];
2032  }
2033  assignmentInstrString += " = dot(sg_vPosVS, g_vTexGenPlane";
2034  if (desc_.texGenDim > 1)
2035  {
2036  assignmentInstrString += "[";
2037  assignmentInstrString += QString::number(i);
2038  assignmentInstrString += "]";
2039  }
2040  assignmentInstrString += ");";
2041  _code->push_back(assignmentInstrString);
2042  }
2043 
2044  } break;
2045 
2046  case GL_SPHERE_MAP:
2047  {
2048  _code->push_back("vec3 sg_vPosVS_unit = normalize(sg_vPosVS.xyz);");
2049  _code->push_back("vec3 sg_TexGenRefl = reflect(sg_vPosVS_unit, sg_vNormalVS);");
2050  _code->push_back("vec3 sg_TexGenRefl2 = sg_TexGenRefl; sg_TexGenRefl2.z += 1.0;");
2051  _code->push_back("float sg_TexGenMRcp = 0.5 * inversesqrt(dot(sg_TexGenRefl2, sg_TexGenRefl2));");
2052  for (int i = 0; i < desc_.texGenDim; ++i)
2053  {
2054  QString assignmentInstrString;
2055  assignmentInstrString.sprintf("sg_vTexCoord.%s = sg_TexGenRefl.%s * sg_TexGenMRcp + 0.5;", texGenCoordString[i], texGenCoordString[i]);
2056  _code->push_back(assignmentInstrString);
2057  }
2058  } break;
2059 
2060  case GL_NORMAL_MAP:
2061  {
2062  for (int i = 0; i < desc_.texGenDim; ++i)
2063  {
2064  QString assignmentInstrString;
2065  assignmentInstrString.sprintf("sg_vTexCoord.%s = sg_vNormalVS.%s;", texGenCoordString[i], texGenCoordString[i]);
2066  _code->push_back(assignmentInstrString);
2067  }
2068  } break;
2069 
2070  case GL_REFLECTION_MAP:
2071  {
2072  _code->push_back("vec3 sg_vPosVS_unit = normalize(sg_vPosVS.xyz);");
2073  _code->push_back("vec3 sg_TexGenRefl = reflect(sg_vPosVS_unit, sg_vNormalVS);");
2074  for (int i = 0; i < desc_.texGenDim; ++i)
2075  {
2076  QString assignmentInstrString;
2077  assignmentInstrString.sprintf("sg_vTexCoord.%s = sg_TexGenRefl.%s;", texGenCoordString[i], texGenCoordString[i]);
2078  _code->push_back(assignmentInstrString);
2079  }
2080  } break;
2081 
2082  default: break;
2083  }
2084  }
2085 }
2086 
2087 
2089 {
2090  // import template source from files
2092 
2093  // check what needs to be passed down from vertex shader
2094 
2095  if (desc_.shadeMode != SG_SHADE_UNLIT)
2096  ioDesc_.inputNormal_ = true;
2097 
2098  if (desc_.textured())
2099  {
2100  ioDesc_.inputTexCoord_ = true;
2101  ioDesc_.passTexCoord_ = true;
2102  }
2103 
2104  // clamp generated texcoord dimension
2105  int maxTexGenDim = 4;
2106 
2107  switch (desc_.texGenMode)
2108  {
2109  case GL_EYE_LINEAR:
2110  case GL_OBJECT_LINEAR: maxTexGenDim = 4; break;
2111 
2112  case GL_SPHERE_MAP: maxTexGenDim = 2; break;
2113 
2114  case GL_NORMAL_MAP:
2115  case GL_REFLECTION_MAP: maxTexGenDim = 3; break;
2116 
2117  default: maxTexGenDim = 0; break;
2118  }
2119 
2120  desc_.texGenDim = std::max(std::min(desc_.texGenDim, maxTexGenDim), 0);
2121 
2122 
2123  if (desc_.texGenDim && desc_.texGenMode)
2124  {
2125  // pass generated texcoord from vertex to fragment shader
2126  if (!desc_.texGenPerFragment)
2127  ioDesc_.passTexCoord_ = true;
2128 
2129  // some modes require normal vectors
2130  if (desc_.texGenMode == GL_REFLECTION_MAP || desc_.texGenMode == GL_SPHERE_MAP || desc_.texGenMode == GL_NORMAL_MAP)
2131  ioDesc_.inputNormal_ = true;
2132 
2133  // pass data to the fragment shader as required for the generation
2134  if (desc_.texGenPerFragment)
2135  {
2136  switch (desc_.texGenMode)
2137  {
2138  case GL_OBJECT_LINEAR: ioDesc_.passPosOS_ = true; break;
2139  case GL_EYE_LINEAR: ioDesc_.passPosVS_ = true; break;
2140  case GL_SPHERE_MAP: ioDesc_.passPosVS_ = ioDesc_.passNormalVS_ = true; break;
2141  case GL_NORMAL_MAP: ioDesc_.passNormalVS_ = true; break;
2142  case GL_REFLECTION_MAP: ioDesc_.passPosVS_ = ioDesc_.passNormalVS_ = true; break;
2143  default: break;
2144  }
2145  }
2146  }
2147 
2148 
2149  if (desc_.vertexColors)
2150  ioDesc_.inputColor_ = true;
2151 
2152  if (desc_.shadeMode == SG_SHADE_PHONG)
2153  {
2154  ioDesc_.passNormalVS_ = true;
2155  ioDesc_.passPosVS_ = true;
2156  }
2157 
2158  if (desc_.shadeMode == SG_SHADE_FLAT || desc_.shadeMode == SG_SHADE_GOURAUD || desc_.vertexColors)
2159  ioDesc_.passColor_ = true;
2160 
2161 
2162  // scan macros of modifiers for attribute requests,
2163  // done by adding modifier io to an empty dummy
2164  ShaderGenerator dummy;
2165 
2166  for (size_t i = 0; i < activeMods_.size(); ++i)
2167  {
2168  ShaderModifier* mod = activeMods_[i];
2169 
2170  mod->modifyVertexIO(&dummy);
2171  mod->modifyTessControlIO(&dummy);
2172  mod->modifyTessEvalIO(&dummy);
2173  mod->modifyGeometryIO(&dummy);
2174  mod->modifyFragmentIO(&dummy);
2175  }
2176  // scan requested inputs from modifiers
2177 
2178  if (dummy.hasDefine(ShaderGenerator::keywords.macro_requestPosVS))
2179  ioDesc_.passPosVS_ = true;
2180  if (dummy.hasDefine(ShaderGenerator::keywords.macro_requestTexcoord))
2181  {
2182  ioDesc_.inputTexCoord_ = true;
2183  ioDesc_.passTexCoord_ = true;
2184  }
2185  if (dummy.hasDefine(ShaderGenerator::keywords.macro_requestVertexColor))
2186  {
2187  ioDesc_.inputColor_ = true;
2188  ioDesc_.passColor_ = true;
2189  }
2190  if (dummy.hasDefine(ShaderGenerator::keywords.macro_requestNormalVS))
2191  {
2192  ioDesc_.inputNormal_ = true;
2193  ioDesc_.passNormalVS_ = true;
2194  }
2195  if (dummy.hasDefine(ShaderGenerator::keywords.macro_requestNormalOS))
2196  {
2197  ioDesc_.inputNormal_ = true;
2198  ioDesc_.passNormalOS_ = true;
2199  }
2200  if (dummy.hasDefine(ShaderGenerator::keywords.macro_requestPosOS))
2201  ioDesc_.passPosOS_ = true;
2202 
2203  if (dummy.hasDefine(ShaderGenerator::keywords.macro_requestRenormalize))
2204  renormalizeLighting_ = true;
2205 
2206 
2207 
2208 
2209  // assemble shader codes
2210 
2211  buildVertexShader();
2212  buildTessControlShader();
2213  buildTessEvalShader();
2214  buildGeometryShader();
2215  buildFragmentShader();
2216 }
2217 
2218 
2220 {
2221  return vertex_->getShaderCode();
2222 }
2223 
2225 {
2226  return tessControl_->getShaderCode();
2227 }
2228 
2230 {
2231  return tessEval_->getShaderCode();
2232 }
2233 
2235 {
2236  return geometry_->getShaderCode();
2237 }
2238 
2240 {
2241  return fragment_->getShaderCode();
2242 }
2243 
2244 
2245 void ShaderProgGenerator::saveVertexShToFile(const char* _fileName)
2246 {
2247  vertex_->saveToFile(_fileName);
2248 }
2249 
2250 void ShaderProgGenerator::saveGeometryShToFile(const char* _fileName)
2251 {
2252  geometry_->saveToFile(_fileName);
2253 }
2254 
2255 void ShaderProgGenerator::saveFragmentShToFile(const char* _fileName)
2256 {
2257  fragment_->saveToFile(_fileName);
2258 }
2259 
2260 
2262 {
2263  if (!desc_.vertexTemplateFile.isEmpty())
2264  {
2265  loadStringListFromFile(desc_.vertexTemplateFile, &vertexTemplate_);
2266  scanShaderTemplate(vertexTemplate_, desc_.vertexTemplateFile);
2267  }
2268  if (!desc_.fragmentTemplateFile.isEmpty())
2269  {
2270  loadStringListFromFile(desc_.fragmentTemplateFile, &fragmentTemplate_);
2271  scanShaderTemplate(fragmentTemplate_, desc_.fragmentTemplateFile);
2272  }
2273  if (!desc_.geometryTemplateFile.isEmpty())
2274  {
2275  loadStringListFromFile(desc_.geometryTemplateFile, &geometryTemplate_);
2276  scanShaderTemplate(geometryTemplate_, desc_.geometryTemplateFile);
2277  }
2278  if (!desc_.tessControlTemplateFile.isEmpty())
2279  {
2280  loadStringListFromFile(desc_.tessControlTemplateFile, &tessControlTemplate_);
2281  scanShaderTemplate(tessControlTemplate_, desc_.tessControlTemplateFile, &tessControlLayout_);
2282  }
2283  if (!desc_.tessEvaluationTemplateFile.isEmpty())
2284  {
2285  loadStringListFromFile(desc_.tessEvaluationTemplateFile, &tessEvalTemplate_);
2286  scanShaderTemplate(tessEvalTemplate_, desc_.tessEvaluationTemplateFile, &tessEvalLayout_);
2287  }
2288 
2289 
2290  vertexShaderFile_ = desc_.vertexTemplateFile;
2291  tessControlShaderFile_ = desc_.tessControlTemplateFile;
2292  tessEvalShaderFile_ = desc_.tessEvaluationTemplateFile;
2293  geometryShaderFile_ = desc_.geometryTemplateFile;
2294  fragmentShaderFile_ = desc_.fragmentTemplateFile;
2295 }
2296 
2297 void ShaderProgGenerator::scanShaderTemplate(QStringList& _templateSrc, QString _templateFilename, QStringList* _outLayoutDirectives)
2298 {
2299  // interpret loaded shader template:
2300  // import #includes
2301 
2302  QString filePath = getPathName(_templateFilename);
2303 
2304  QStringList::iterator it;
2305  for (it = _templateSrc.begin(); it != _templateSrc.end(); ++it)
2306  {
2307  QStringList import;
2308 
2309  if (checkForIncludes(*it, &import, filePath))
2310  {
2311  // line is include directive
2312 
2313  // remove line from source
2314  it = _templateSrc.erase(it);
2315 
2316  int offset = it - _templateSrc.begin();
2317 
2318  // insert imported file
2319 
2320  QString importLine;
2321  foreach(importLine, import)
2322  {
2323  it = _templateSrc.insert(it, importLine);
2324  ++it;
2325  }
2326 
2327  // included file might recursively include something again
2328  // -> scan included file
2329  it = _templateSrc.begin() + offset;
2330  }
2331  else
2332  {
2333  QString trimmedLine = it->trimmed();
2334 
2335  // scan and adjust glsl version
2336  QByteArray lineBytes = trimmedLine.toUtf8();
2337 
2338  int templateVersion = 0;
2339  if (sscanf(lineBytes.constData(), "#version %d", &templateVersion) == 1)
2340  {
2341  desc_.version = std::max(templateVersion, desc_.version);
2342 
2343  // remove version line from template since this is added later in the build functions
2344  it = _templateSrc.erase(it);
2345  }
2346  // scan layout() directive
2347  else if (trimmedLine.startsWith("layout(") || trimmedLine.startsWith("layout ("))
2348  {
2349  if (_outLayoutDirectives)
2350  {
2351  _outLayoutDirectives->push_back(trimmedLine);
2352  // layout() will be inserted later at the correct position in the build functions
2353  // - must be placed before shader IO declaration to make tess-control shaders compilable on ati
2354  it = _templateSrc.erase(it);
2355  }
2356  }
2357  else
2358  {
2359  // scan requested inputs
2360 
2361  if (trimmedLine.startsWith(ShaderGenerator::keywords.macro_requestPosVS))
2362  ioDesc_.passPosVS_ = true;
2363  else if (trimmedLine.startsWith(ShaderGenerator::keywords.macro_requestTexcoord))
2364  {
2365  ioDesc_.inputTexCoord_ = true;
2366  ioDesc_.passTexCoord_ = true;
2367  }
2368  else if (trimmedLine.startsWith(ShaderGenerator::keywords.macro_requestVertexColor))
2369  {
2370  ioDesc_.inputColor_ = true;
2371  ioDesc_.passColor_ = true;
2372  }
2373  else if (trimmedLine.startsWith(ShaderGenerator::keywords.macro_requestNormalVS))
2374  {
2375  ioDesc_.inputNormal_ = true;
2376  ioDesc_.passNormalVS_ = true;
2377  }
2378  else if (trimmedLine.startsWith(ShaderGenerator::keywords.macro_requestNormalOS))
2379  {
2380  ioDesc_.inputNormal_ = true;
2381  ioDesc_.passNormalOS_ = true;
2382  }
2383  else if (trimmedLine.startsWith(ShaderGenerator::keywords.macro_requestPosOS))
2384  ioDesc_.passPosOS_ = true;
2385  else if (trimmedLine.startsWith(ShaderGenerator::keywords.macro_requestRenormalize))
2386  renormalizeLighting_ = true;
2387  else if (trimmedLine.startsWith("SG_FRAGMENT_LIGHTING"))
2388  {
2389  // shader template performs lighting in fragment shader
2390  // -> forced phong shading
2391  desc_.shadeMode = SG_SHADE_PHONG;
2392  }
2393  }
2394 
2395  }
2396  }
2397 
2398 }
2399 
2400 QString ShaderProgGenerator::getPathName(QString _strFileName)
2401 {
2402  QFileInfo fileInfo(getAbsFilePath(_strFileName));
2403  return fileInfo.absolutePath();
2404 }
2405 
2406 QString ShaderProgGenerator::getAbsFilePath(QString _strFileName)
2407 {
2408  QString absFilename;
2409  if ( QDir(_strFileName).isRelative() )
2410  absFilename = getShaderDir() + QDir::separator() + _strFileName;
2411  else
2412  absFilename = _strFileName;
2413 
2414  return QDir::cleanPath(absFilename);
2415 }
2416 
2418 {
2419  shaderDir_ = _dir;
2420 }
2421 
2423 {
2424  return shaderDir_ + QString("/");
2425 }
2426 
2427 
2429 {
2430  if (!_modifier) return 0;
2431 
2432  // redundancy check
2433  for (int i = 0; i < numRegisteredModifiers_; ++i)
2434  {
2435  if (registeredModifiers_[i] == _modifier)
2436  {
2437 // std::cout << "warning: trying to re-register shader modifier " << _modifier->getID() << std::endl;
2438  return registeredModifiers_[i]->getID();
2439  }
2440  }
2441 
2442  _modifier->modifierID_ = (unsigned int)(numRegisteredModifiers_++);
2443 
2444  registeredModifiers_.push_back(_modifier);
2445  return _modifier->modifierID_;
2446 }
2447 
2449 {
2450  if (_i >= 0 && _i <= int(activeMods_.size()))
2451  return activeMods_[_i];
2452 
2453  // invalid _i
2454  return 0;
2455 }
2456 
2458 {
2459  return int(activeMods_.size());
2460 }
2461 
2462 
2464 {
2465  return !desc_.geometryTemplateFile.isEmpty();
2466 }
2467 
2469 {
2470  return !desc_.tessControlTemplateFile.isEmpty();
2471 }
2472 
2474 {
2475  return !desc_.tessEvaluationTemplateFile.isEmpty();
2476 }
2477 
2478 
2479 //=============================================================================
2480 
2481 ShaderModifier::ShaderModifier( void )
2482 : modifierID_(0)
2483 {}
2484 
2485 ShaderModifier::~ShaderModifier( void )
2486 {}
2487 
2488 
2490 {
2491 public:
2492 
2494  : version_(0)
2495  {}
2496 
2497  virtual ~ShaderModifierFile()
2498  {}
2499 
2500  void modifyVertexIO(ShaderGenerator* _shader) { modifyIO(0, _shader); }
2501  void modifyTessControlIO(ShaderGenerator* _shader) { modifyIO(1, _shader); }
2502  void modifyTessEvalIO(ShaderGenerator* _shader) { modifyIO(2, _shader); }
2503  void modifyGeometryIO(ShaderGenerator* _shader) { modifyIO(3, _shader); }
2504  void modifyFragmentIO(ShaderGenerator* _shader) { modifyIO(4, _shader); }
2505 
2506 
2507  void modifyVertexBeginCode(QStringList* _code) { _code->append(vertexBeginCode_); }
2508  void modifyVertexEndCode(QStringList* _code) { _code->append(vertexEndCode_); }
2509  void modifyFragmentBeginCode(QStringList* _code) { _code->append(fragmentBeginCode_); }
2510  void modifyFragmentEndCode(QStringList* _code) { _code->append(fragmentEndCode_); }
2511 
2512  const QString& filename() const {return filename_;}
2513  const QDateTime& filetime() const {return filetime_;}
2514  void filetime(const QDateTime& _newtime) {filetime_ = _newtime;}
2515 
2516  void clear()
2517  {
2518  version_ = 0;
2519 
2520  for (int i = 0; i < 5; ++i)
2521  io_[i].clear();
2522 
2523  vertexBeginCode_.clear();
2524  vertexEndCode_.clear();
2525  fragmentBeginCode_.clear();
2526  fragmentEndCode_.clear();
2527  }
2528 
2529  static ShaderModifierFile* loadFromFile(QString _filename)
2530  {
2531  ShaderModifierFile* res = 0;
2532 
2533  // get timestamp
2534  QString absFilename = ShaderProgGenerator::getAbsFilePath(_filename);
2535  QDateTime lastmod = QFileInfo(absFilename).lastModified();
2536 
2537  // check cache
2538  QHash<QString, ShaderModifierFile>::iterator cacheEntry = fileCache_.find(_filename);
2539 
2540  bool reload = false;
2541  bool firstLoad = false;
2542 
2543  if (cacheEntry != fileCache_.end())
2544  {
2545  // fetch from cache
2546  res = &cacheEntry.value();
2547 
2548  if (lastmod != res->filetime())
2549  {
2550  res->clear();
2551  reload = true;
2552  }
2553  }
2554  else
2555  {
2556  // load new modifier
2557  reload = true;
2558  firstLoad = true;
2559  }
2560 
2561  if (reload)
2562  {
2563  QStringList lines;
2564  if (ShaderProgGenerator::loadStringListFromFile(_filename, &lines))
2565  {
2566  // new cache entry
2567  if (firstLoad)
2568  res = &fileCache_[_filename];
2569 
2570  res->loadBlocks(lines);
2571  res->filetime(lastmod);
2572 
2573  // also register to generator
2574  if (firstLoad)
2576  }
2577  }
2578 
2579  return res;
2580  }
2581 
2582 private:
2583 
2584 
2585  void loadBlocks(const QStringList& _lines)
2586  {
2587  static const char* markers [] =
2588  {
2589  "VertexIO:",
2590  "TessControlIO:",
2591  "TessEvalIO:",
2592  "GeometryIO:",
2593  "FragmentIO:",
2594  "VertexBeginCode:",
2595  "VertexEndCode:",
2596  "FragmentBeginCode:",
2597  "FragmentEndCode:"
2598  };
2599  const int numMarkers = sizeof(markers) / sizeof(markers[0]);
2600 
2601  QStringList* blockTargets [] =
2602  {
2603  io_ + 0,
2604  io_ + 1,
2605  io_ + 2,
2606  io_ + 3,
2607  io_ + 4,
2608  &vertexBeginCode_,
2609  &vertexEndCode_,
2610  &fragmentBeginCode_,
2611  &fragmentEndCode_
2612  };
2613 
2614  assert(sizeof(blockTargets) / sizeof(blockTargets[0]) == numMarkers);
2615 
2616 
2617  // current block in file, points to one of io_[idx], vertexBeginCode_, ...
2618  QStringList* curBlock_ = 0;
2619 
2620 
2621  int curLine = 0;
2622 
2623  for (QStringList::const_iterator it = _lines.begin(); it != _lines.end(); ++it, ++curLine)
2624  {
2625  if (it->isEmpty())
2626  continue;
2627 
2628  // read glsl version
2629  if (version_ <= 0 && it->startsWith("#version "))
2630  {
2631  const int offset = strlen("#version ");
2632  version_ = atoi(it->toLatin1().data() + offset);
2633  }
2634  else
2635  {
2636  // read code blocks
2637 
2638  bool blockMarker = false;
2639 
2640  for (int i = 0; i < numMarkers && !blockMarker; ++i)
2641  {
2642  if ( it->startsWith(markers[i]) )
2643  {
2644  // new block start
2645  curBlock_ = blockTargets[i];
2646  blockMarker = true;
2647  }
2648  }
2649 
2650  if (!blockMarker)
2651  {
2652  if (curBlock_) // code belongs to some block
2653  curBlock_->push_back(*it);
2654  else // wrong file structure
2655  std::cerr << "ShaderModifierFile::loadBlocks - line belongs to unknown block in file " << filename_.toLatin1().data() << " at line " << curLine << std::endl;
2656  }
2657  }
2658  }
2659  }
2660 
2661  void modifyIO(int _stage, ShaderGenerator* _shader)
2662  {
2663  if (version_ > 0)
2664  _shader->setGLSLVersion(version_);
2665 
2666  _shader->addRawIOBlock(io_[_stage]);
2667  }
2668 
2669 private:
2670 
2671  QString filename_;
2672 
2673  QDateTime filetime_;
2674 
2675  // glsl version
2676  int version_;
2677 
2678  // io mods
2679  QStringList io_[5];
2680 
2681  // code mods
2682  QStringList vertexBeginCode_,
2683  vertexEndCode_,
2684  fragmentBeginCode_,
2685  fragmentEndCode_;
2686 
2687 
2688  // loaded modifiers
2689  static QHash<QString, ShaderModifierFile> fileCache_;
2690 };
2691 
2692 QHash<QString, ShaderModifierFile> ShaderModifierFile::fileCache_;
2693 
2694 
2696 {
2697  return ShaderModifierFile::loadFromFile(_filename);
2698 }
2699 
2700 
2701 //=============================================================================
2702 
2703 
2705 {
2706  // mapping (int)ShaderGenMode -> string
2707  const char* shadeModeString[] =
2708  {
2709  "SG_SHADE_UNLIT",
2710  "SG_SHADE_FLAT",
2711  "SG_SHADE_GOURAUD",
2712  "SG_SHADE_PHONG"
2713  };
2714 
2715  QString res;
2716  QTextStream resStrm(&res);
2717 
2718  resStrm << "version: " << version;
2719 
2720  resStrm << "shaderDesc.numLights: " << numLights;
2721 
2722  if (numLights)
2723  {
2724  resStrm << "\nshaderDesc.lightTypes[]: {";
2725 
2726  for (int i = 0; i < numLights; ++i)
2727  {
2728  switch (lightTypes[i])
2729  {
2730  case SG_LIGHT_DIRECTIONAL: resStrm << "DIRECTIONAL"; break;
2731  case SG_LIGHT_POINT: resStrm << "POINT"; break;
2732  case SG_LIGHT_SPOT: resStrm << "SPOT"; break;
2733  default: resStrm << "UNDEFINED"; break;
2734  }
2735 
2736  if (i + 1 < numLights)
2737  resStrm << ", ";
2738  else
2739  resStrm << "}";
2740  }
2741  }
2742 
2743 
2744  resStrm << "\nshaderDesc.shadeMode: " << shadeModeString[shadeMode];
2745  resStrm << "\nshaderDesc.vertexColors: " << vertexColors;
2746  resStrm << "\nshaderDesc.textured(): " << textured();
2747  for (std::map<size_t,TextureType>::const_iterator iter = textureTypes_.begin(); iter != textureTypes_.end();++iter)
2748  {
2749  resStrm << "\nTexture stage: " << iter->first;
2750  resStrm << "\nTexture Type: ";
2751  switch (iter->second.type)
2752  {
2753  case GL_TEXTURE_1D: resStrm << "GL_TEXTURE_1D"; break;
2754  case GL_TEXTURE_2D: resStrm << "GL_TEXTURE_2D"; break;
2755  case GL_TEXTURE_3D: resStrm << "GL_TEXTURE_3D"; break;
2756  case GL_TEXTURE_CUBE_MAP: resStrm << "GL_TEXTURE_CUBE_MAP"; break;
2757 #ifdef GL_ARB_texture_rectangle //ARCH_DARWIN doesn't support all texture defines with all xcode version (xcode 5.0 seems to support all)
2758  case GL_TEXTURE_RECTANGLE_ARB: resStrm << "GL_TEXTURE_RECTANGLE"; break;
2759 #endif
2760 #ifdef GL_ARB_texture_buffer_object
2761  case GL_TEXTURE_BUFFER_ARB: resStrm << "GL_TEXTURE_BUFFER"; break;
2762 #endif
2763 #ifdef GL_EXT_texture_array
2764  case GL_TEXTURE_1D_ARRAY_EXT: resStrm << "GL_TEXTURE_1D_ARRAY"; break;
2765  case GL_TEXTURE_2D_ARRAY_EXT: resStrm << "GL_TEXTURE_2D_ARRAY"; break;
2766 #endif
2767 #ifdef GL_ARB_texture_cube_map_array
2768  case GL_TEXTURE_CUBE_MAP_ARRAY_ARB: resStrm << "GL_TEXTURE_CUBE_MAP_ARRAY"; break;
2769 #endif
2770 #ifdef GL_ARB_texture_multisample
2771  case GL_TEXTURE_2D_MULTISAMPLE: resStrm << "GL_TEXTURE_2D_MULTISAMPLE"; break;
2772  case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: resStrm << "GL_TEXTURE_2D_MULTISAMPLE_ARRAY"; break;
2773 #endif
2774  default: std::cerr << "Texture Type with number "<< iter->second.type << " on stage "<< iter->first << " is not supported " << std::endl; break;
2775  }
2776 
2777  resStrm << "\nShadowTexture: " << iter->second.shadow;
2778  }
2779 
2780  resStrm << "\nshaderDesc.texGenDim: " << texGenDim;
2781 
2782  switch (texGenMode)
2783  {
2784  case GL_OBJECT_LINEAR: resStrm << "\nshaderDesc.texGenMode: GL_OBJECT_LINEAR"; break;
2785  case GL_EYE_LINEAR: resStrm << "\nshaderDesc.texGenMode: GL_EYE_LINEAR"; break;
2786  case GL_SPHERE_MAP: resStrm << "\nshaderDesc.texGenMode: GL_SPHERE_MAP"; break;
2787  case GL_NORMAL_MAP: resStrm << "\nshaderDesc.texGenMode: GL_NORMAL_MAP"; break;
2788  case GL_REFLECTION_MAP: resStrm << "\nshaderDesc.texGenMode: GL_REFLECTION_MAP"; break;
2789  default: resStrm << "\nshaderDesc.texGenMode: unknown"; break;
2790  }
2791 
2792  resStrm << "\nshaderDesc.texGenPerFragment: " << texGenPerFragment;
2793 
2794  if (!vertexTemplateFile.isEmpty())
2795  resStrm << "\nshaderDesc.vertexTemplateFile: " << vertexTemplateFile;
2796 
2797  if (!tessControlTemplateFile.isEmpty())
2798  resStrm << "\nshaderDesc.tessControlTemplateFile: " << tessControlTemplateFile;
2799 
2800  if (!tessEvaluationTemplateFile.isEmpty())
2801  resStrm << "\nshaderDesc.tessEvaluationTemplateFile: " << tessEvaluationTemplateFile;
2802 
2803  if (!geometryTemplateFile.isEmpty())
2804  resStrm << "\nshaderDesc.geometryTemplateFile: " << geometryTemplateFile;
2805 
2806  if (!fragmentTemplateFile.isEmpty())
2807  resStrm << "\nshaderDesc.fragmentTemplateFile: " << fragmentTemplateFile;
2808 
2809  return res;
2810 }
2811 
2812 
2813 
2814 } // namespace ACG
2815 //=============================================================================
QString getInputName(int _id) const
get variable name of input
int getNumActiveModifiers() const
Get the number of active modifiers.
QString vertexColorsInterpolator
interpolation qualifier for input vertex colors: "flat", "smooth", "noperspective" ...
static void setShaderDir(QString _dir)
bool inputTexCoord_
default attributes that should be imported in vertex shader
QString vertexShaderFile_
path + filename to shader templates
bool hasDefine(QString _define) const
Check for define.
void modifyLightingCode(QStringList *_code, ShaderModifier *_modifier)
Calls lighting modifier for each light.
QString toString() const
convert ShaderGenDesc to string format for debugging
static QString getPathName(QString _strFileName)
returns path to _strFileName without last slash
virtual void modifyLightingCode(QStringList *_code, int _lightId, ShaderGenLightType _lightType)
Modify the default lighting code of the shader generator.
const QStringList & getShaderCode()
Get result of buildShaderCode.
void modifyFragmentIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the fragment shader.
void matchInputs(const ShaderGenerator *_previousShaderStage, bool _passToNextStage, QString _inputPrefix="outVertex", QString _outputPrefix="outGeometry")
Perform name matching of outputs and inputs between two shader stages.
void modifyGeometryIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the geometry shader.
void generateShaders()
Generates the shader code.
void loadShaderTemplateFromFile()
Loads external shader templates.
void addTexGenCode(QStringList *_code, bool _fragmentShader)
Add texture coordinate generation code.
void modifyFragmentBeginCode(QStringList *_code)
Append code the the fragment shader.
void modifyVertexIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the vertex shader.
void defineIOAbstraction(const DefaultIODesc *_iodesc, bool _vs, bool _fs)
Define abstract IO names via shader defines.
void addIODefine(const QString &_macroName, const QString &_resolvedName)
Assign an opaque name to the abstract macro.
const QStringList & getFragmentShaderCode()
Returns generated fragment shader code.
virtual void modifyTessEvalIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the tessellation evaluation shader.
void addLightingCode(QStringList *_code)
Adds lighting function calls to code.
void addLight(int lightIndex_, ShaderGenLightType _light)
Add a light description to shader:
bool renormalizeLighting_
renormalize before lighting, default false, can be activated via "#define SG_REQUEST_RENORMALIZE" ...
QString getOutputName(int _id) const
get variable name of output
void addRawIOBlock(QStringList _codeBlock)
Add a raw glsl IO code block.
void modifyTessControlIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the tessellation control shader.
bool hasGeometryShader() const
check whether there is a geometry shader present
static QString getShaderDir()
static QString getAbsFilePath(QString _fileName)
Convert a filename to an absolute filename.
void addIncludeFile(QString _fileName)
Imports another shader, same as #include.
static ShaderModifier * loadFromFile(QString _filename)
Load a modifier from file.
bool openGLVersion(const int _major, const int _minor)
Definition: gl.cc:95
static bool loadStringListFromFile(QString _fileName, QStringList *_out)
Load a text file as string list.
void addLayout(QString _layout)
Add a layout directive.
QString getIOMapName(int _inId) const
get corresponding output name of an input id
int getNumInputs() const
get number of inputs
ShaderProgGenerator(const ShaderGenDesc *_desc)
void initVertexShaderIO(const ShaderGenDesc *_desc, const DefaultIODesc *_iodesc)
Adds fitting vertex shader io for a given description.
void modifyVertexBeginCode(QStringList *_code)
Append code the the vertex shader.
ShaderModifier * getActiveModifier(int _i)
Get active modfiers for this program.
QString outputPrefix_
prefix of outputs of this shader
void initGenDefines(ShaderGenerator *_gen)
provide generated defines to shader
const QStringList & getGeometryShaderCode()
Returns generated tessellation evaluation shader code.
bool normalizeTexColors
Defines if the textureVariable is normalized or not, if multiple textures are used.
const QStringList & getVertexShaderCode()
Returns generated vertex shader code.
bool hasTessControlShader() const
check whether there is a tess-control shader present
void addInput(const QString &_input)
Add one GLSL input specifier.
int checkForIncludes(QString _str, ShaderGenerator *_gen, QString _includePath)
static int numRegisteredModifiers_
registered shader modifier
Namespace providing different geometric functions concerning angles.
Definition: DBSCANT.cc:51
bool passPosVS_
default attributes that should be passed down from vertex shader
void addLightingFunctions(QStringList *_code)
Adds lighting definition functions.
void initDefaultUniforms()
Adds frequently used uniform parameters.
const QStringList & getTessEvaluationShaderCode()
Returns generated tessellation control shader code.
virtual void modifyTessControlIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the tessellation control shader.
void addUniform(QString _uniform, QString _comment="")
Add one GLSL uniform specifier.
virtual void modifyVertexIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the vertex shader.
int getNumOutputs() const
get number of outputs
void addMacros(const QStringList &_macros)
Add a list of preprocessor macros.
virtual void modifyFragmentIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the fragment shader.
void initGeometryShaderIO(const ShaderGenDesc *_desc, ShaderGenerator *_prevStage, const DefaultIODesc *_iodesc)
Adds fitting geometry shader io for a given description.
void buildShaderCode(QStringList *_pMainCode, const QStringList &_defaultLightingFunctions)
Shader assembly function.
void setGLSLVersion(int _version)
Set glsl version.
void modifyTessEvalIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the tessellation evaluation shader.
const QStringList & getTessControlShaderCode()
Returns generated vertex shader code.
void addDefine(const QString &_define)
Add one define.
void saveToFile(const char *_fileName)
Save generated shader code to text file.
bool hasTessEvaluationShader() const
check whether there is a tess-evaluation shader present
bool outputArrays_
outputs of shader are arrays (tess-control)
void modifyFragmentEndCode(QStringList *_code)
Append code the the fragment shader.
void initTessControlShaderIO(const ShaderGenDesc *_desc, ShaderGenerator *_prevStage, const DefaultIODesc *_iodesc)
Adds fitting tess-control shader io for a given description.
void initFragmentShaderIO(const ShaderGenDesc *_desc, ShaderGenerator *_prevStage, const DefaultIODesc *_iodesc)
Adds fitting fragment shader io for a given description.
void init(const ShaderGenDesc *_desc, ShaderModifier *const *_modifiers, unsigned int _numActiveMods)
Called in constructor.
void addStringToList(QString _str, QStringList *_list, QString _prefix="", QString _postfix="")
virtual void modifyGeometryIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the geometry shader.
void modifyVertexEndCode(QStringList *_code)
Append code the the vertex shader.
static unsigned int registerModifier(ShaderModifier *_modifier)
Shader modifiers have to be registered before they can be used. They also must remain allocated for t...
ShaderGenerator::DefaultIODesc ioDesc_
default IO descriptor for the vertex shader
void initTessEvalShaderIO(const ShaderGenDesc *_desc, ShaderGenerator *_prevStage, const DefaultIODesc *_iodesc)
Adds fitting tess-evaluation shader io for a given description.
void scanShaderTemplate(QStringList &_templateSrc, QString _templateFilename, QStringList *_outLayoutDirectives=0)
Scans loaded shader template for requested inputs, glsl version or includes.
void addIOToCode(const QStringList &_cmds)
void addOutput(const QString &_output)
Add one GLSL output specifier.
QStringList tessControlLayout_
layout() directives scanned from loaded templates