ShaderGenerator.cc 76.9 KB
Newer Older
1
2
3
/*===========================================================================*\
 *                                                                           *
 *                              OpenFlipper                                  *
Jan Möbius's avatar
Jan Möbius committed
4
5
6
7
 *           Copyright (c) 2001-2015, RWTH-Aachen University                 *
 *           Department of Computer Graphics and Multimedia                  *
 *                          All rights reserved.                             *
 *                            www.openflipper.org                            *
8
9
 *                                                                           *
 *---------------------------------------------------------------------------*
Jan Möbius's avatar
Jan Möbius committed
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 * This file is part of OpenFlipper.                                         *
 *---------------------------------------------------------------------------*
 *                                                                           *
 * Redistribution and use in source and binary forms, with or without        *
 * modification, are permitted provided that the following conditions        *
 * are met:                                                                  *
 *                                                                           *
 * 1. Redistributions of source code must retain the above copyright notice, *
 *    this list of conditions and the following disclaimer.                  *
 *                                                                           *
 * 2. Redistributions in binary form must reproduce the above copyright      *
 *    notice, this list of conditions and the following disclaimer in the    *
 *    documentation and/or other materials provided with the distribution.   *
 *                                                                           *
 * 3. Neither the name of the copyright holder nor the names of its          *
 *    contributors may be used to endorse or promote products derived from   *
 *    this software without specific prior written permission.               *
 *                                                                           *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       *
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A           *
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  *
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,       *
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR        *
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    *
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      *
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        *
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              *
39
40
41
42
43
44
45
46
47
48
49
 *                                                                           *
\*===========================================================================*/

/*===========================================================================*\
 *                                                                           *
 *   $Revision$                                                       *
 *   $Author$                                                      *
 *   $Date$                   *
 *                                                                           *
\*===========================================================================*/

Christopher Tenter's avatar
Christopher Tenter committed
50
#include "ShaderGenerator.hh"
51
52
#include <cstdio>
#include <cstring>
Christopher Tenter's avatar
Christopher Tenter committed
53
54
55
#include <iostream>
#include <algorithm>

56
#include <QFile>
57
58
#include <QFileInfo>
#include <QDir>
59
#include <QTextStream>
60
61
#include <QDateTime>
#include <QHash>
62

Christopher Tenter's avatar
Christopher Tenter committed
63
64
65
namespace ACG
{

66
#define LIGHTING_CODE_FILE "ShaderGen/SG_LIGHTING.GLSL"
Christopher Tenter's avatar
Christopher Tenter committed
67

68
69
70
71
72
73
74


// space naming
// OS : object space
// VS : view space
// CS : clip space

75
76
77
78
79
// attribute request keywords
#define SG_REQUEST_POSVS "#define SG_REQUEST_POSVS"
#define SG_REQUEST_POSOS "#define SG_REQUEST_POSOS"
#define SG_REQUEST_TEXCOORD "#define SG_REQUEST_TEXCOORD"
#define SG_REQUEST_VERTEXCOLOR "#define SG_REQUEST_VERTEXCOLOR"
80
81
82
#define SG_REQUEST_NORMALVS "#define SG_REQUEST_NORMALVS"
#define SG_REQUEST_NORMALOS "#define SG_REQUEST_NORMALOS"

83
84
85
// renormalize normal-vec before lighting in fragment shader
#define SG_REQUEST_RENOMARLIZE "#define SG_REQUEST_RENORMARLIZE"

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// generic default attribute input keywords
//  these are extended by the correct input name by the generator for each stage
#define SG_INPUT_POSVS "SG_INPUT_POSVS"
#define SG_INPUT_POSOS "SG_INPUT_POSOS"
#define SG_INPUT_POSCS "SG_INPUT_POSCS"
#define SG_INPUT_NORMALVS "SG_INPUT_NORMALVS"
#define SG_INPUT_NORMALOS "SG_INPUT_NORMALOS"
#define SG_INPUT_TEXCOORD "SG_INPUT_TEXCOORD"
#define SG_INPUT_VERTEXCOLOR "SG_INPUT_VERTEXCOLOR"

#define SG_OUTPUT_POSVS "SG_OUTPUT_POSVS"
#define SG_OUTPUT_POSOS "SG_OUTPUT_POSOS"
#define SG_OUTPUT_POSCS "SG_OUTPUT_POSCS"
#define SG_OUTPUT_NORMALVS "SG_OUTPUT_NORMALVS"
#define SG_OUTPUT_NORMALOS "SG_OUTPUT_NORMALOS"
#define SG_OUTPUT_TEXCOORD "SG_OUTPUT_TEXCOORD"
#define SG_OUTPUT_VERTEXCOLOR "SG_OUTPUT_VERTEXCOLOR"
Christopher Tenter's avatar
Christopher Tenter committed
103

104

105
106
int ShaderProgGenerator::numRegisteredModifiers_ = 0;
std::vector<ShaderModifier*> ShaderProgGenerator::registeredModifiers_;
107

Christopher Tenter's avatar
Christopher Tenter committed
108
109
110


ShaderGenerator::ShaderGenerator()
111
  : version_(150), inputArrays_(false), outputArrays_(false)
Christopher Tenter's avatar
Christopher Tenter committed
112
113
114
115
116
117
118
119
120
{
}

ShaderGenerator::~ShaderGenerator()
{

}


121
void ShaderGenerator::initVertexShaderIO(const ShaderGenDesc* _desc, const DefaultIODesc* _iodesc)
Christopher Tenter's avatar
Christopher Tenter committed
122
{
123
124
125
126
127
  // set type of IO
  inputArrays_ = false;
  outputArrays_ = false;
  inputPrefix_ = "in";         // inputs: inPosition, inTexCoord...
  outputPrefix_ = "outVertex"; // outputs: outVertexPosition, outVertexTexCoord..
128

Christopher Tenter's avatar
Christopher Tenter committed
129
  addInput("vec4 inPosition");
130
  addOutput("vec4 outVertexPosCS");
131

Christopher Tenter's avatar
Christopher Tenter committed
132

133
  if (_iodesc->inputNormal_)
Christopher Tenter's avatar
Christopher Tenter committed
134
135
    addInput("vec3 inNormal");

136
  if (_desc->textured())
Christopher Tenter's avatar
Christopher Tenter committed
137
  {
138
139
140
141
142
143
144
145
146
147
148

    std::map<size_t,ShaderGenDesc::TextureType>::const_iterator iter = _desc->textureTypes().begin();

    /// TODO Setup for multiple texture coordinates as input
    if (iter->second.type == GL_TEXTURE_3D) {
      addInput("vec3 inTexCoord");
      addOutput("vec3 outVertexTexCoord");
    } else {
      addInput("vec2 inTexCoord");
      addOutput("vec2 outVertexTexCoord");
    }
Christopher Tenter's avatar
Christopher Tenter committed
149
  }
150
151


152
  if (_iodesc->inputColor_)
Christopher Tenter's avatar
Christopher Tenter committed
153
154
155
    addInput("vec4 inColor");


156
  if (_iodesc->passNormalVS_)
157
    addOutput("vec3 outVertexNormal");
158
159
160
161

  if (_iodesc->passNormalOS_)
    addOutput("vec3 outVertexNormalOS");

Christopher Tenter's avatar
Christopher Tenter committed
162
163


164
  // vertex color output
Christopher Tenter's avatar
Christopher Tenter committed
165

166
167
168
169
170
171
172
173
174
175
  if (_desc->vertexColorsInterpolator.isEmpty())
  {
    std::string strColorOut = "";
    if (_desc->shadeMode == SG_SHADE_FLAT)
      if (!_desc->geometryTemplateFile.isEmpty())
        strColorOut = "vec4 outVertexColor";
      else {
        // Bypass the output setter, as we have to set that directly with the flat.
        addStringToList("vec4 outVertexColor", &outputs_, "flat out ", ";");
      }
Jan Möbius's avatar
Jan Möbius committed
176
    else {
177
178
      if (_desc->shadeMode == SG_SHADE_GOURAUD || _desc->vertexColors || _iodesc->inputColor_)
        strColorOut = "vec4 outVertexColor";
Jan Möbius's avatar
Jan Möbius committed
179
    }
180
181
182

    if (strColorOut.size())
      addOutput(strColorOut.c_str());
Christopher Tenter's avatar
Christopher Tenter committed
183
  }
184
185
  else
    addStringToList("vec4 outVertexColor", &outputs_, _desc->vertexColorsInterpolator + " out ", ";");
Christopher Tenter's avatar
Christopher Tenter committed
186

187
188
189
190


  // handle other requests: normals, positions, texcoords

191
  if (_iodesc->passPosVS_)
192
193
    addOutput("vec4 outVertexPosVS");

194
  if (_iodesc->passPosOS_)
195
196
    addOutput("vec4 outVertexPosOS");

197
  if (_iodesc->passTexCoord_ && !_desc->textured())
198
  {
199
200
201
    // assume 2d texcoords as default
    int texdim = 2;

202
    if (_desc->texGenMode && _desc->texGenDim > 0 && _desc->texGenDim <= 4 && !_desc->texGenPerFragment)
203
204
205
206
207
208
209
210
211
      texdim = _desc->texGenDim;


    QString inTexCoordString, outTexCoordString;
    inTexCoordString.sprintf("vec%i inTexCoord", texdim);
    outTexCoordString.sprintf("vec%i outVertexTexCoord", texdim);

    addInput(inTexCoordString);
    addOutput(outTexCoordString);
212
213
  }

214

215
  defineIOAbstraction(_iodesc, true, false);
216
217
}

218
void ShaderGenerator::initTessControlShaderIO(const ShaderGenDesc* _desc, ShaderGenerator* _prevStage, const DefaultIODesc* _iodesc) 
219
220
221
222
223
224
225
226
{
  // set type of IO
  inputArrays_ = true;
  outputArrays_ = true;
  inputPrefix_ = _prevStage->outputPrefix_;
  outputPrefix_ = "outTc"; // outputs: outTcPosition, outTcTexCoord..

  matchInputs(_prevStage, true, inputPrefix_, outputPrefix_);
227
228

  defineIOAbstraction(_iodesc, false, false);
Christopher Tenter's avatar
Christopher Tenter committed
229
230
}

231
void ShaderGenerator::initTessEvalShaderIO(const ShaderGenDesc* _desc, ShaderGenerator* _prevStage, const DefaultIODesc* _iodesc) 
232
233
234
235
236
237
238
239
{
  // set type of IO
  inputArrays_ = true;
  outputArrays_ = false;
  inputPrefix_ = _prevStage->outputPrefix_;
  outputPrefix_ = "outTe"; // outputs: outTePosition, outTeTexCoord..

  matchInputs(_prevStage, true, inputPrefix_, outputPrefix_);
240
241

  defineIOAbstraction(_iodesc, false, false);
242
243
}

244
void ShaderGenerator::initGeometryShaderIO(const ShaderGenDesc* _desc, ShaderGenerator* _prevStage, const DefaultIODesc* _iodesc) 
245
246
247
248
249
250
251
252
253
254
{
  // set type of IO
  inputArrays_ = true;
  outputArrays_ = false;
  inputPrefix_ = _prevStage->outputPrefix_;
  outputPrefix_ = "outGeometry"; // outputs: outGeometryPosition, outGeometryTexCoord..

  matchInputs(_prevStage, true, inputPrefix_, outputPrefix_);

  return;
255

256
257
258
  addInput("vec4 outVertexPosCS[]");
  addOutput("vec4 outGeometryPosCS");

259
  if (_desc->textured()) {
260
261
262
263
264
265
266
267
268
269
270
271
272

    std::map<size_t,ShaderGenDesc::TextureType>::const_iterator iter = _desc->textureTypes().begin();

    /// TODO Setup for multiple texture coordinates as input
    if (iter->second.type == GL_TEXTURE_3D)
    {
      addInput("vec3 outVertexTexCoord[]");
      addOutput("vec3 outGeometryTexCoord");
    } else {
      addInput("vec2 outVertexTexCoord[]");
      addOutput("vec2 outGeometryTexCoord");
    }

273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  }


  if (_desc->shadeMode == SG_SHADE_PHONG)
  {
    addInput("vec3 outVertexNormal[]");
    addInput("vec4 outVertexPosVS[]");

    addOutput("vec3 outGeometryNormal");
    addOutput("vec4 outGeometryPosVS");
  }

  QString strColorOut = "";

  if (_desc->shadeMode == SG_SHADE_FLAT || _desc->shadeMode == SG_SHADE_GOURAUD || _desc->vertexColors) {
    addInput("vec4 outVertexColor[]");


    if (_desc->shadeMode == SG_SHADE_FLAT)
Jan Möbius's avatar
Jan Möbius committed
292
      addStringToList("vec4 outGeometryColor", &outputs_, "flat out ", ";");
293
294
295
296
297
    else {
      if (_desc->shadeMode == SG_SHADE_GOURAUD || _desc->vertexColors)
        strColorOut = "vec4 outGeometryColor";
    }

Jan Möbius's avatar
Jan Möbius committed
298
299
    if ( !strColorOut.isEmpty() )
      addOutput(strColorOut);
300
301
  }

302
  defineIOAbstraction(_iodesc, false, false);
303
304
}

Christopher Tenter's avatar
Christopher Tenter committed
305
306


307
void ShaderGenerator::initFragmentShaderIO(const ShaderGenDesc* _desc, ShaderGenerator* _prevStage, const DefaultIODesc* _iodesc)
Christopher Tenter's avatar
Christopher Tenter committed
308
{
309
310
311
312
313
  // set type of IO
  inputArrays_ = false;
  outputArrays_ = false;
  inputPrefix_ = _prevStage->outputPrefix_;
  outputPrefix_ = "outFragment";
314

315
  matchInputs(_prevStage, false);
Christopher Tenter's avatar
Christopher Tenter committed
316
  addOutput("vec4 outFragment");
317
318

  defineIOAbstraction(_iodesc, false, true);
Christopher Tenter's avatar
Christopher Tenter committed
319
320
321
}


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
void ShaderGenerator::defineIOAbstraction( const DefaultIODesc* _iodesc, bool _vs, bool _fs )
{
  if (_vs)
  {
    // input name abstraction

    addDefine(SG_INPUT_POSOS " inPosition");

    if (_iodesc->inputTexCoord_)
      addDefine(SG_INPUT_TEXCOORD " inTexCoord");

    if (_iodesc->inputNormal_)
      addDefine(SG_INPUT_NORMALOS " inNormal");

    if (_iodesc->inputColor_)
337
      addDefine(SG_INPUT_VERTEXCOLOR " inColor");
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368



    // output name abstraction

    addDefine(SG_OUTPUT_POSCS " outVertexPosCS");

    if (_iodesc->passPosVS_)
      addDefine(SG_OUTPUT_POSVS " outVertexPosVS");

    if (_iodesc->passPosOS_)
      addDefine(SG_OUTPUT_POSOS " outVertexPosOS");

    if (_iodesc->passTexCoord_)
      addDefine(SG_OUTPUT_TEXCOORD " outVertexTexCoord");

    if (_iodesc->passNormalVS_)
      addDefine(SG_OUTPUT_NORMALVS " outVertexNormal");

    if (_iodesc->passNormalOS_)
      addDefine(SG_OUTPUT_NORMALOS " outVertexNormalOS");

    if (_iodesc->passColor_)
      addDefine(SG_OUTPUT_VERTEXCOLOR " outVertexColor");
  }
  else
  {
    if (_iodesc->passPosVS_)
    {
      addDefine(QString(SG_INPUT_POSVS) + QString(" ") + inputPrefix_ + QString("PosVS"));
      if (!_fs)
369
        addDefine(QString(SG_OUTPUT_POSVS) + QString(" ") + outputPrefix_ + QString("PosVS"));
370
371
372
373
374
375
    }

    if (_iodesc->passPosOS_)
    {
      addDefine(QString(SG_INPUT_POSOS) + QString(" ") + inputPrefix_ + QString("PosOS"));
      if (!_fs)
376
        addDefine(QString(SG_OUTPUT_POSOS) + QString(" ") + outputPrefix_ + QString("PosOS"));
377
378
379
380
    }

    addDefine(QString(SG_INPUT_POSCS) + QString(" ") + inputPrefix_ + QString("PosCS"));
    if (!_fs)
381
      addDefine(QString(SG_OUTPUT_POSCS) + QString(" ") + outputPrefix_ + QString("PosCS"));
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414

    if (_iodesc->passNormalVS_)
    {
      addDefine(QString(SG_INPUT_NORMALVS) + QString(" ") + inputPrefix_ + QString("Normal"));
      if (!_fs)
        addDefine(QString(SG_OUTPUT_NORMALVS) + QString(" ") + outputPrefix_ + QString("Normal"));
    }

    if (_iodesc->passNormalOS_)
    {
      addDefine(QString(SG_INPUT_NORMALOS) + QString(" ") + inputPrefix_ + QString("NormalOS"));
      if (!_fs)
        addDefine(QString(SG_OUTPUT_NORMALOS) + QString(" ") + outputPrefix_ + QString("NormalOS"));
    }

    if (_iodesc->passTexCoord_)
    {
      addDefine(QString(SG_INPUT_TEXCOORD) + QString(" ") + inputPrefix_ + QString("TexCoord"));
      if (!_fs)
        addDefine(QString(SG_OUTPUT_TEXCOORD) + QString(" ") + outputPrefix_ + QString("TexCoord"));
    }

    if (_iodesc->passColor_)
    {
      addDefine(QString(SG_INPUT_VERTEXCOLOR) + QString(" ") + inputPrefix_ + QString("Color"));
      if (!_fs)
        addDefine(QString(SG_INPUT_VERTEXCOLOR) + QString(" ") + outputPrefix_ + QString("Color"));
    }
  }

  
}

Christopher Tenter's avatar
Christopher Tenter committed
415
416
417
418


void ShaderGenerator::initDefaultUniforms()
{
Jan Möbius's avatar
Jan Möbius committed
419
420
421
422
  addUniform("mat4 g_mWVP" , "  // Projection * Modelview");       // Transforms directly from Object space to NDC
  addUniform("mat4 g_mWV" , "   // Modelview matrix");             // Modelview transforms from Object to World to View coordinates
  addUniform("mat3 g_mWVIT" , " // Modelview inverse transposed"); // Modelview inverse transposed, transforms from view across World into Object coordinates
  addUniform("mat4 g_mP", "     // Projection matrix");            // Projection Matrix
Christopher Tenter's avatar
Christopher Tenter committed
423
424
425
426
427
428
429
430
431

  addUniform("vec3 g_vCamPos");
  addUniform("vec3 g_vCamDir");

  addUniform("vec3 g_cDiffuse");
  addUniform("vec3 g_cAmbient");
  addUniform("vec3 g_cEmissive");
  addUniform("vec3 g_cSpecular");
  addUniform("vec4 g_vMaterial");
432
  addUniform("vec3 g_cLightModelAmbient");
Christopher Tenter's avatar
Christopher Tenter committed
433
434
435
}


436
#define ADDLIGHT(x) (sz.sprintf(x"_%d", lightIndex_), addUniform(sz))
Christopher Tenter's avatar
Christopher Tenter committed
437
438
439

void ShaderGenerator::addLight(int lightIndex_, ShaderGenLightType _light)
{
440
  QString sz;
Christopher Tenter's avatar
Christopher Tenter committed
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463

  ADDLIGHT("vec3 g_cLightDiffuse");
  ADDLIGHT("vec3 g_cLightAmbient");
  ADDLIGHT("vec3 g_cLightSpecular");

  if (_light == SG_LIGHT_POINT ||
    _light == SG_LIGHT_SPOT)
  {
    ADDLIGHT("vec3 g_vLightPos");
    ADDLIGHT("vec3 g_vLightAtten");
  }

  if (_light == SG_LIGHT_DIRECTIONAL ||
    _light == SG_LIGHT_SPOT)
    ADDLIGHT("vec3 g_vLightDir");


  if (_light == SG_LIGHT_SPOT)
    ADDLIGHT("vec2 g_vLightAngleExp");
}



464
void ShaderGenerator::addStringToList(QString _str, 
465
                                      QStringList* _arr,
Jan Möbius's avatar
Jan Möbius committed
466
467
                                      QString _prefix,
                                      QString _postfix)
Christopher Tenter's avatar
Christopher Tenter committed
468
{
Jan Möbius's avatar
Jan Möbius committed
469
  // Construct the whole string
Christopher Tenter's avatar
Christopher Tenter committed
470
471
472
473
474
475
476
  QString tmp = _str;

  if (!_str.startsWith(_prefix))
    tmp = _prefix + tmp;

  if (!_str.endsWith(_postfix))
     tmp += _postfix;
Christopher Tenter's avatar
Christopher Tenter committed
477
478
479

  // normalize string
  //  remove tabs, double whitespace
480
  tmp = tmp.simplified();
Christopher Tenter's avatar
Christopher Tenter committed
481
482

  // avoid duplicates
483
484
  if (!_arr->contains(tmp))
    _arr->push_back(tmp);
Christopher Tenter's avatar
Christopher Tenter committed
485
486
487
488

}


489
void ShaderGenerator::addInput(QString _input)
Christopher Tenter's avatar
Christopher Tenter committed
490
491
492
493
494
495
{
  addStringToList(_input, &inputs_, "in ", ";");
}



496
void ShaderGenerator::addOutput(QString _output)
Christopher Tenter's avatar
Christopher Tenter committed
497
498
499
500
501
{
  addStringToList(_output, &outputs_, "out ", ";");
}


502
void ShaderGenerator::addDefine(QString _def)
Christopher Tenter's avatar
Christopher Tenter committed
503
504
505
506
{
  addStringToList(_def, &genDefines_, "#define ");
}

507
508
509
510
511
512
513
514
515
516
517
518
void ShaderGenerator::addMacros(const QStringList& _macros)
{
  // prepend macros to the "defines" list

  // QStringList reverse_iterator:
  typedef std::reverse_iterator<QStringList::const_iterator> QStringListReverseIterator;
  QStringListReverseIterator rbegin( _macros.end() ), rend( _macros.begin() );

  for (QStringListReverseIterator it = rbegin; it != rend; ++it)
    genDefines_.push_front(*it);
}

519
520
521
522
bool ShaderGenerator::hasDefine(QString _define) const
{
  if (genDefines_.contains(_define))
    return true;
Christopher Tenter's avatar
Christopher Tenter committed
523

524
525
526
527
528
529
530
531
532
533
534
535
  // check trimmed strings and with startsWith()

  QString trimmedDef = _define.trimmed();

  for (QStringList::const_iterator it = genDefines_.constBegin(); it != genDefines_.constEnd(); ++it)
  {
    QString trimmedRef = it->trimmed();

    if (trimmedRef.startsWith(trimmedDef))
      return true;
  }

536
537
538
539
540
541
542
543
544
  // also check raw io blocks
  for (QStringList::const_iterator it = rawIO_.constBegin(); it != rawIO_.constEnd(); ++it)
  {
    QString trimmedRef = it->trimmed();

    if (trimmedRef.startsWith(trimmedDef))
      return true;
  }

545
546
547
548
549
550
551
  return false;
}

void ShaderGenerator::addLayout(QString _def)
{
  addStringToList(_def, &layouts_);
}
Christopher Tenter's avatar
Christopher Tenter committed
552
553


Jan Möbius's avatar
Jan Möbius committed
554
void ShaderGenerator::addUniform(QString _uniform, QString _comment)
Christopher Tenter's avatar
Christopher Tenter committed
555
{
556
557
558
559
560
  QString prefix = "";
  if (!_uniform.startsWith("uniform ") && !_uniform.contains(" uniform "))
    prefix = "uniform ";

  addStringToList(_uniform, &uniforms_, prefix, "; " + _comment );
Christopher Tenter's avatar
Christopher Tenter committed
561
562
563
564
}



565
void ShaderGenerator::addIOToCode(const QStringList& _cmds)
Christopher Tenter's avatar
Christopher Tenter committed
566
{
567
568
  QString it;
  foreach(it, _cmds)
Christopher Tenter's avatar
Christopher Tenter committed
569
  {
570
    code_.push_back(it);
Christopher Tenter's avatar
Christopher Tenter committed
571
572
    // append ; eventually

573
    if (!it.contains(";"))
Christopher Tenter's avatar
Christopher Tenter committed
574
575
576
577
578
579
      code_.back().append(";");
  }
}



580
void ShaderGenerator::buildShaderCode(QStringList* _pMainCode, const QStringList& _defaultLightingFunctions)
Christopher Tenter's avatar
Christopher Tenter committed
581
{
582
583
584
585
  QString glslversion;
  glslversion.sprintf("#version %d", version_);

  code_.push_back(glslversion);
Christopher Tenter's avatar
Christopher Tenter committed
586
587

  // provide defines
588
  QString it;
Christopher Tenter's avatar
Christopher Tenter committed
589

590
591
  foreach(it, genDefines_)
    code_.push_back(it);
Christopher Tenter's avatar
Christopher Tenter committed
592

593
594
595
596
  // layouts
  foreach(it, layouts_)
    code_.push_back(it);

597
  // IO
Christopher Tenter's avatar
Christopher Tenter committed
598
599
600
601
  addIOToCode(inputs_);
  addIOToCode(outputs_);
  addIOToCode(uniforms_);

602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
  // eventually attach lighting functions if required
  bool requiresLightingCode = false;

  // search for references in imports
  foreach(it, imports_)
  {
    if (it.contains("LitDirLight") || it.contains("LitPointLight") || it.contains("LitSpotLight"))
      requiresLightingCode = true;
  }

  if (requiresLightingCode)
  {
    foreach(it, _defaultLightingFunctions)
      code_.push_back(it);
  }

618
619
620
621
  // provide imports
  foreach(it, imports_)
    code_.push_back(it);

622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640

  // search for lighting references in main code

  if (!requiresLightingCode)
  {
    foreach(it, (*_pMainCode))
    {
      if (it.contains("LitDirLight") || it.contains("LitPointLight") || it.contains("LitSpotLight"))
        requiresLightingCode = true;
    }

    if (requiresLightingCode)
    {
      foreach(it, _defaultLightingFunctions)
        code_.push_back(it);
    }
  }


641
642
643
644
  // add raw IO code block
  code_.append(rawIO_);


Christopher Tenter's avatar
Christopher Tenter committed
645
  // main function
646
647
  foreach(it, (*_pMainCode))
    code_.push_back(it);
Christopher Tenter's avatar
Christopher Tenter committed
648
649
650
651
}



652
void ShaderGenerator::addIncludeFile(QString _fileName)
Christopher Tenter's avatar
Christopher Tenter committed
653
{
654
  QFile file(_fileName);
Christopher Tenter's avatar
Christopher Tenter committed
655

Christopher Tenter's avatar
Christopher Tenter committed
656
  if (file.open(QIODevice::ReadOnly | QIODevice::Text))
Christopher Tenter's avatar
Christopher Tenter committed
657
  {
Christopher Tenter's avatar
Christopher Tenter committed
658
    QTextStream fileStream(&file);
659
660
661
662
663
664
    
    // track source of include files in shader comment
    
    imports_.push_back("// ==============================================================================");
    imports_.push_back(QString("// ShaderGenerator - begin of imported file: ") + _fileName);
    
Christopher Tenter's avatar
Christopher Tenter committed
665
666
667
668

    while (!fileStream.atEnd())
    {
      QString tmpLine = fileStream.readLine();
Christopher Tenter's avatar
Christopher Tenter committed
669

Christopher Tenter's avatar
Christopher Tenter committed
670
671
      imports_.push_back(tmpLine.simplified());
    }
672
673
674
675
676
677
678
    
    
    // mark end of include file in comment
    
    imports_.push_back(QString("// ShaderGenerator - end of imported file #include \"") + _fileName);
    imports_.push_back("// ==============================================================================");
    
Christopher Tenter's avatar
Christopher Tenter committed
679
680
681
682
683
684
685
686
  }

}



void ShaderGenerator::saveToFile(const char* _fileName)
{
687
  QFile file(_fileName);
Christopher Tenter's avatar
Christopher Tenter committed
688
689
690
  if (file.open(QIODevice::WriteOnly | QIODevice::Text))
  {
    QTextStream fileStream(&file);
Christopher Tenter's avatar
Christopher Tenter committed
691

Christopher Tenter's avatar
Christopher Tenter committed
692
693
694
695
    QString it;
    foreach(it, code_)
      fileStream << it << '\n';
  }
Christopher Tenter's avatar
Christopher Tenter committed
696
697
698
699
}



700
const QStringList& ShaderGenerator::getShaderCode()
Christopher Tenter's avatar
Christopher Tenter committed
701
702
703
704
{
  return code_;
}

705
706
707
708
709
void ShaderGenerator::setGLSLVersion( int _version )
{
  version_ = _version;
}

710
void ShaderGenerator::matchInputs(const ShaderGenerator* _previousShaderStage,
711
712
713
714
  bool _passToNextStage,
  QString _inputPrefix, 
  QString _outputPrefix)
{
715
716
717
718
719
720
  if (!_previousShaderStage)
  {
    std::cout << "error: ShaderGenerator::matchInputs called without providing input stage" << std::endl;
    return;
  }

721
  QString it;
722
  foreach(it, _previousShaderStage->outputs_)
723
724
725
726
727
728
729
730
731
  {
    QString input = it;

    QString outKeyword = "out ";
    QString inKeyword = "in  ";

    // replace first occurrence of "out" with "in"
    input.replace(input.indexOf(outKeyword), outKeyword.size(), inKeyword);

732
733
734
735
736
737
738
739
740
741
742
    // special case for array IO

    if (inputArrays_ && !_previousShaderStage->outputArrays_)
    {
      QRegExp alphaNum("[a-zA-Z0-9]");
      int lastNameChar = input.lastIndexOf(alphaNum);
      input.insert(lastNameChar+1, "[]");
//      input.insert(lastNameChar+1, "[gl_in.length()]");
    }


743
744
745
746
747
748
749
750
751
    // add to input list with duplicate check
    addStringToList(input, &inputs_);

    if (_passToNextStage)
    {
      // replace prefixes of in/outputs to avoid name collision

      QString output = input;
      output.replace(output.indexOf(_inputPrefix), _inputPrefix.size(), _outputPrefix);
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
      output.replace(output.indexOf(inKeyword), inKeyword.size(), outKeyword);

      // take care of arrays
      if (inputArrays_ && !outputArrays_)
      {
        int bracketStart = output.indexOf("[");
        int bracketEnd = output.indexOf("]");
        output.remove(bracketStart, bracketEnd-bracketStart+1);
      }
      else if (!inputArrays_ && outputArrays_)
      {
        QRegExp alphaNum("[a-zA-Z0-9]");
        int lastNameChar = output.lastIndexOf(alphaNum);
        output.insert(lastNameChar+1, "[]");
//        output.insert(lastNameChar+1, "[gl_in.length()]");
      }

769
770
771
772
773
774

      // add to output list with duplicate check
      addStringToList(output, &outputs_);
    }
  }
}
Christopher Tenter's avatar
Christopher Tenter committed
775

776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
int ShaderGenerator::getNumOutputs() const
{
  return outputs_.size();
}

QString ShaderGenerator::getOutputName(int _id) const
{
  QString output = outputs_.at(_id);

  output.remove(";");
  output.remove("out ");

  int bracketStart = output.indexOf("[");
  int bracketEnd = output.lastIndexOf("]");

  if (bracketStart >= 0)
    output.remove(bracketStart, bracketEnd-bracketStart+1);

  // decompose output declaration
  QStringList decompOutput = output.split(" ");
  return decompOutput.last();
}

int ShaderGenerator::getNumInputs() const
{
  return inputs_.size();
}

QString ShaderGenerator::getInputName(int _id) const
{
  QString input = inputs_.at(_id);

  input.remove(";");
  input.remove("out ");
  
  int bracketStart = input.indexOf("[");
  int bracketEnd = input.lastIndexOf("]");

  if (bracketStart >= 0)
    input.remove(bracketStart, bracketEnd-bracketStart+1);

  // decompose output declaration
  QStringList decompInput = input.split(" ");
  return decompInput.last();
}

QString ShaderGenerator::getIOMapName(int _inId) const
{
  QString inputName = getInputName(_inId);
Christopher Tenter's avatar
Christopher Tenter committed
825

826
827
828
829
830
831
  // output name = input name with swapped prefix
  QString outputName = inputName;
  outputName.replace(outputName.indexOf(inputPrefix_), inputPrefix_.size(), outputPrefix_);

  return outputName;
}
Christopher Tenter's avatar
Christopher Tenter committed
832

833
834
835
836
837
838
839
840
841
842
843
844
845
846
847

ShaderGenerator::DefaultIODesc::DefaultIODesc()
  : inputTexCoord_(false),
  inputColor_(false),
  inputNormal_(false),
  passPosVS_(false), passPosOS_(false), 
  passTexCoord_(false), 
  passColor_(false),
  passNormalVS_(false), passNormalOS_(false)
{
}




848
QString ShaderProgGenerator::shaderDir_;
849
QStringList ShaderProgGenerator::lightingCode_;
Christopher Tenter's avatar
Christopher Tenter committed
850

851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871

ShaderProgGenerator::ShaderProgGenerator( const ShaderGenDesc* _desc )
  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
{
  init(_desc, (ShaderModifier**)0, 0);
}

ShaderProgGenerator::ShaderProgGenerator( const ShaderGenDesc* _desc, const unsigned int* _modifierIDs, unsigned int _numActiveMods )
  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
{
  init(_desc, _modifierIDs, _numActiveMods);
}

ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, const std::vector<unsigned int>& _modifierIDs)
  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
{
  init(_desc, _modifierIDs.empty() ? 0 : &_modifierIDs[0], (unsigned int)_modifierIDs.size());
}

ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, const std::vector<unsigned int>* _modifierIDs)
  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
Christopher Tenter's avatar
Christopher Tenter committed
872
{
Christopher Tenter's avatar
Christopher Tenter committed
873
874
  unsigned int numMods = !_modifierIDs || _modifierIDs->empty() ? 0 : (unsigned int)_modifierIDs->size();
  init(_desc, numMods ? &((*_modifierIDs)[0]) : 0, numMods);
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
}

ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, ShaderModifier* const* _modifiers, unsigned int _numActiveMods)
  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
{
  init(_desc, _modifiers, _numActiveMods);
}

ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, const std::vector<ShaderModifier*>& _modifierIDs)
  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
{
  init(_desc, _modifierIDs.empty() ? 0 : &(_modifierIDs[0]), (unsigned int)_modifierIDs.size());
}

ShaderProgGenerator::ShaderProgGenerator(const ShaderGenDesc* _desc, const std::vector<ShaderModifier*>* _modifierIDs)
  : vertex_(0), tessControl_(0), tessEval_(0), geometry_(0), fragment_(0), renormalizeLighting_(false)
{
Christopher Tenter's avatar
Christopher Tenter committed
892
893
  unsigned int numMods = !_modifierIDs || _modifierIDs->empty() ? 0 : (unsigned int)_modifierIDs->size();
  init(_desc, numMods ? &((*_modifierIDs)[0]) : 0, numMods);
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
}


void ShaderProgGenerator::init( const ShaderGenDesc* _desc, const unsigned int* _modifierIDs, unsigned int _numActiveMods )
{
  if (_modifierIDs && _numActiveMods)
  {
    activeMods_.resize(_numActiveMods);

    for (unsigned int i = 0; i < _numActiveMods; ++i)
      activeMods_[i] = registeredModifiers_[ _modifierIDs[i] ];
  }

  init(_desc, (ShaderModifier**)0, 0);
}

void ShaderProgGenerator::init( const ShaderGenDesc* _desc, ShaderModifier* const* _modifiers, unsigned int _numActiveMods )
{
  if (_modifiers && _numActiveMods)
  {
    activeMods_.resize(_numActiveMods);

    for (unsigned int i = 0; i < _numActiveMods; ++i)
      activeMods_[i] = _modifiers[i];
  }


921
922
923
924
  if (shaderDir_.isEmpty())
    std::cout << "error: call ShaderProgGenerator::setShaderDir() first!" << std::endl;
  else
  {
925
    desc_ = *_desc;
Christopher Tenter's avatar
Christopher Tenter committed
926

927
    // We need at least version 3.2 or higher to support geometry shaders
928
    if ( !ACG::openGLVersion(3,2) )
929
930
931
932
    {
      if (!desc_.geometryTemplateFile.isEmpty())
        std::cerr << "Warning: removing geometry shader from ShaderDesc" << std::endl;

933
      desc_.geometryTemplateFile.clear();
934
    }
935

936
937
938
    // We need at least version 4.0 or higher to support tessellation
    if ( !ACG::openGLVersion(4, 0) )
    {
939
940
941
      if (!desc_.tessControlTemplateFile.isEmpty() || !desc_.tessEvaluationTemplateFile.isEmpty())
        std::cerr << "Warning: removing tessellation shader from ShaderDesc" << std::endl;

942
943
944
945
946
947
948
949
950
951
952
953
954
      desc_.tessControlTemplateFile.clear();
      desc_.tessEvaluationTemplateFile.clear();
    }

    // adjust glsl version to requirement

    if (!desc_.geometryTemplateFile.isEmpty())
      desc_.version = std::max(desc_.version, 150);

    if (!desc_.tessControlTemplateFile.isEmpty() || !desc_.tessEvaluationTemplateFile.isEmpty())
      desc_.version = std::max(desc_.version, 400);


955
    loadLightingFunctions();
Christopher Tenter's avatar
Christopher Tenter committed
956

957
958
    generateShaders();
  }
Christopher Tenter's avatar
Christopher Tenter committed
959
960
}

961

Christopher Tenter's avatar
Christopher Tenter committed
962
963
964
965
ShaderProgGenerator::~ShaderProgGenerator(void)
{
  delete vertex_;
  delete fragment_;
966
967
968
  delete geometry_;
  delete tessControl_;
  delete tessEval_;
Christopher Tenter's avatar
Christopher Tenter committed
969
970
971
972
}



973
bool ShaderProgGenerator::loadStringListFromFile(QString _fileName, QStringList* _out)
Christopher Tenter's avatar
Christopher Tenter committed
974
{
975
976
  bool success = false;

977
  QString absFilename = getAbsFilePath(_fileName);
Christopher Tenter's avatar
Christopher Tenter committed
978

979

980
  QFile file(absFilename);
Christopher Tenter's avatar
Christopher Tenter committed
981

982
  if (file.open(QIODevice::ReadOnly | QIODevice::Text))
Christopher Tenter's avatar
Christopher Tenter committed
983
  {
984
985
986
    if (!file.isReadable())
      std::cout << "error: unreadable file -> \"" << absFilename.toStdString() << "\"" << std::endl;
    else
Christopher Tenter's avatar
Christopher Tenter committed
987
    {
988
989
990
991
992
993
994
      QTextStream filestream(&file);

      while (!filestream.atEnd())
      {
        QString szLine = filestream.readLine();
        _out->push_back(szLine.trimmed());
      }
995
996

      success = true;
Christopher Tenter's avatar
Christopher Tenter committed
997
    }
998
999

    file.close();
Christopher Tenter's avatar
Christopher Tenter committed
1000
  }