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) { modifyIO(0, _shader); }
2530  void modifyTessControlIO(ShaderGenerator* _shader) { modifyIO(1, _shader); }
2531  void modifyTessEvalIO(ShaderGenerator* _shader) { modifyIO(2, _shader); }
2532  void modifyGeometryIO(ShaderGenerator* _shader) { modifyIO(3, _shader); }
2533  void modifyFragmentIO(ShaderGenerator* _shader) { modifyIO(4, _shader); }
2534 
2535 
2536  void modifyVertexBeginCode(QStringList* _code) { _code->append(vertexBeginCode_); }
2537  void modifyVertexEndCode(QStringList* _code) { _code->append(vertexEndCode_); }
2538  void modifyFragmentBeginCode(QStringList* _code) { _code->append(fragmentBeginCode_); }
2539  void modifyFragmentEndCode(QStringList* _code) { _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 //=============================================================================
static void setShaderDir(QString _dir)
bool hasTessControlShader() const
check whether there is a tess-control shader present
void addInput(const QString &_input)
Add one GLSL input specifier.
Namespace providing different geometric functions concerning angles.
static unsigned int registerModifier(ShaderModifier *_modifier)
Shader modifiers have to be registered before they can be used. They also must remain allocated for t...
bool normalizeTexColors
Defines if the textureVariable is normalized or not, if multiple textures are used.
void addOutput(const QString &_output)
Add one GLSL output specifier.
QString getInputName(int _id) const
get variable name of input
int getNumActiveModifiers() const
Get the number of active modifiers.
void initTessControlShaderIO(const ShaderGenDesc *_desc, ShaderGenerator *_prevStage, const DefaultIODesc *_iodesc)
Adds fitting tess-control shader io for a given description.
void modifyFragmentIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the fragment shader.
void init(const ShaderGenDesc *_desc, ShaderModifier *const *_modifiers, unsigned int _numActiveMods)
Called in constructor.
void addStringToList(QString _str, QStringList *_list, QString _prefix="", QString _postfix="")
bool hasGeometryShader() const
check whether there is a geometry shader present
virtual void modifyLightingCode(QStringList *_code, int _lightId, ShaderGenLightType _lightType)
Modify the default lighting code of the shader generator.
void saveToFile(const char *_fileName)
Save generated shader code to text file.
virtual void modifyTessEvalIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the tessellation evaluation shader.
void addUniform(QString _uniform, QString _comment="")
Add one GLSL uniform specifier.
void addRawIOBlock(QStringList _codeBlock)
Add a raw glsl IO code block.
ShaderProgGenerator(const ShaderGenDesc *_desc)
void modifyVertexEndCode(QStringList *_code)
Append code the the vertex shader.
bool passPosVS_
default attributes that should be passed down from vertex shader
void addMacros(const QStringList &_macros)
Add a list of preprocessor macros.
void modifyFragmentBeginCode(QStringList *_code)
Append code the the fragment shader.
QString vertexShaderFile_
path + filename to shader templates
void scanShaderTemplate(QStringList &_templateSrc, QString _templateFilename, QStringList *_outLayoutDirectives=0)
Scans loaded shader template for requested inputs, glsl version or includes.
void addTexGenCode(QStringList *_code, bool _fragmentShader)
Add texture coordinate generation code.
bool hasDefine(QString _define) const
Check for define.
virtual void modifyFragmentIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the fragment shader.
static ShaderModifier * loadFromFile(QString _filename)
Load a modifier from file.
void addDefine(const QString &_define)
Add one define.
void buildShaderCode(QStringList *_pMainCode, const QStringList &_defaultLightingFunctions)
Shader assembly function.
void addIncludeFile(QString _fileName)
Imports another shader, same as #include.
void modifyGeometryIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the geometry shader.
void addLightingCode(QStringList *_code)
Adds lighting function calls to code.
void modifyTessEvalIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the tessellation evaluation shader.
virtual void modifyTessControlIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the tessellation control shader.
void addIODefine(const QString &_macroName, const QString &_resolvedName)
Assign an opaque name to the abstract macro.
static int numRegisteredModifiers_
registered shader modifier
bool hasTessEvaluationShader() const
check whether there is a tess-evaluation shader present
int getNumOutputs() const
get number of outputs
void modifyFragmentEndCode(QStringList *_code)
Append code the the fragment shader.
QString getOutputName(int _id) const
get variable name of output
QString getIOMapName(int _inId) const
get corresponding output name of an input id
void modifyVertexBeginCode(QStringList *_code)
Append code the the vertex shader.
void loadShaderTemplateFromFile()
Loads external shader templates.
void setGLSLVersion(int _version)
Set glsl version.
void defineIOAbstraction(const DefaultIODesc *_iodesc, bool _vs, bool _fs)
Define abstract IO names via shader defines.
const QStringList & getTessEvaluationShaderCode()
Returns generated tessellation control shader code.
const QStringList & getTessControlShaderCode()
Returns generated vertex shader code.
bool ACGDLLEXPORT openGLVersionTest(const int _major, const int _minor)
Definition: gl.hh:265
int getNumInputs() const
get number of inputs
void initTessEvalShaderIO(const ShaderGenDesc *_desc, ShaderGenerator *_prevStage, const DefaultIODesc *_iodesc)
Adds fitting tess-evaluation shader io for a given description.
void initGeometryShaderIO(const ShaderGenDesc *_desc, ShaderGenerator *_prevStage, const DefaultIODesc *_iodesc)
Adds fitting geometry shader io for a given description.
static QString getShaderDir()
void initFragmentShaderIO(const ShaderGenDesc *_desc, ShaderGenerator *_prevStage, const DefaultIODesc *_iodesc)
Adds fitting fragment shader io for a given description.
int checkForIncludes(QString _str, ShaderGenerator *_gen, QString _includePath)
void initGenDefines(ShaderGenerator *_gen)
provide generated defines to shader
void modifyLightingCode(QStringList *_code, ShaderModifier *_modifier)
Calls lighting modifier for each light.
virtual void modifyVertexIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the vertex shader.
const QStringList & getVertexShaderCode()
Returns generated vertex shader code.
static QString getPathName(QString _strFileName)
returns path to _strFileName without last slash
void addLight(int lightIndex_, ShaderGenLightType _light)
Add a light description to shader:
const QStringList & getFragmentShaderCode()
Returns generated fragment shader code.
ShaderModifier * getActiveModifier(int _i)
Get active modfiers for this program.
void modifyVertexIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the vertex shader.
bool outputArrays_
outputs of shader are arrays (tess-control)
void addLayout(QString _layout)
Add a layout directive.
const QStringList & getShaderCode()
Get result of buildShaderCode.
void addIOToCode(const QStringList &_cmds)
void matchInputs(const ShaderGenerator *_previousShaderStage, bool _passToNextStage, QString _inputPrefix="outVertex", QString _outputPrefix="outGeometry")
Perform name matching of outputs and inputs between two shader stages.
static bool loadStringListFromFile(QString _fileName, QStringList *_out)
Load a text file as string list.
QString toString() const
convert ShaderGenDesc to string format for debugging
QString vertexColorsInterpolator
interpolation qualifier for input vertex colors: "flat", "smooth", "noperspective" ...
bool inputTexCoord_
default attributes that should be imported in vertex shader
QString vertexNormalInterpolator
interpolation qualifier for vertex shader normal outputs: "flat", "smooth", "noperspective" ...
virtual void modifyGeometryIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the geometry shader.
const QStringList & getGeometryShaderCode()
Returns generated tessellation evaluation shader code.
void initVertexShaderIO(const ShaderGenDesc *_desc, const DefaultIODesc *_iodesc)
Adds fitting vertex shader io for a given description.
void addLightingFunctions(QStringList *_code)
Adds lighting definition functions.
QStringList tessControlLayout_
layout() directives scanned from loaded templates
void generateShaders()
Generates the shader code.
static QString getAbsFilePath(QString _fileName)
Convert a filename to an absolute filename.
void initDefaultUniforms()
Adds frequently used uniform parameters.
QString outputPrefix_
prefix of outputs of this shader
void modifyTessControlIO(ShaderGenerator *_shader)
Add your own inputs/outputs to the tessellation control shader.
ShaderGenerator::DefaultIODesc ioDesc_
default IO descriptor for the vertex shader