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