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