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