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