Shader.cc 10.3 KB
Newer Older
1
2
3
4
5
/***********************************************************************
 * Copyright 2011-2012 Computer Graphics Group RWTH Aachen University. *
 * All rights reserved.                                                *
 * Distributed under the terms of the MIT License (see LICENSE.TXT).   *
 **********************************************************************/
Robert Menzel's avatar
Robert Menzel committed
6

Robert Menzel's avatar
Robert Menzel committed
7
8
#include <ACGL/OpenGL/Objects/Shader.hh>
#include <ACGL/OpenGL/Tools.hh>
9
#include <ACGL/Utils/StringHelpers.hh>
Robert Menzel's avatar
Robert Menzel committed
10
11
12

#include <iostream>
#include <fstream>
13
#include <algorithm>
Robert Menzel's avatar
Robert Menzel committed
14
15

using namespace ACGL::Utils;
Robert Menzel's avatar
Robert Menzel committed
16
using namespace ACGL::OpenGL;
Robert Menzel's avatar
Robert Menzel committed
17

18
19

bool Shader::setFromFileNoImportParsing(const std::string& _filename)
Robert Menzel's avatar
Robert Menzel committed
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
    std::string line = "";
    std::string fileContent = "";
    
    std::ifstream fileStream(_filename.c_str(), std::ifstream::in);
    
    if(fileStream.is_open())
    {
        while (fileStream.good())
        {
            std::getline(fileStream,line);
            fileContent += line + "\n";
        }
        fileStream.close();
    }
    else
    {
        error() << "Failed to open file: " << _filename << std::endl;
        return false;
    }
    
41
42
43
44
45
46
47
48
49
50
51
52
53
    bool compileErrors = true;
    if ( setSource(fileContent, false) ) { // don't check for errors, we will do that on our own:
        std::string compileLog;
        getCompileLog( compileLog, compileErrors );
        if (compileLog.size() > 0) {
            if (compileErrors) {
                error() << "\nIn file: " << _filename << ": \n" << compileLog << "\n" << std::endl;
            } else {
                warning() << "\nIn file: " << _filename << ": \n" << compileLog << "\n" << std::endl;
            }
        }
    }
    return !compileErrors; // return true iff there were no errors
Robert Menzel's avatar
Robert Menzel committed
54
55
}

56
bool Shader::setFromFile(SharedShaderParser const& _sp)
57
58
{
    bool compileErrors = true;
59
    if ( setSources( _sp->getSources(), false) ) { // don't check for errors, we will do that on our own:
60
61
62
63
        std::string compileLog;
        getCompileLog( compileLog, compileErrors );
        if (compileLog.size() > 0) {
            if (compileErrors) {
64
                error() << "\nIn files: \n" << _sp->getFileNamesPrintable() << compileLog << "\n" << std::endl;
65
            } else {
66
                warning() << "\nIn files: \n" << _sp->getFileNamesPrintable() << compileLog << "\n" << std::endl;
67
68
69
70
71
72
            }
        }
    }
    return !compileErrors; // return true iff there were no errors
}

73
bool Shader::setSource(const std::string& _source, bool _checkForCompileErrors)
Robert Menzel's avatar
Robert Menzel committed
74
{
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
    const char *pProgramString = _source.c_str();
    glShaderSource(mObjectName, 1, &pProgramString, NULL); // can't create OpenGL errors
    if (!compile()) {
        return false;
    }
    // the compile call should work even if there are compile errors itself:
    bool compileErrors = false;
    if (_checkForCompileErrors) {
        std::string compileLog;
        getCompileLog( compileLog, compileErrors );
        if (compileLog.size() > 0) {
            if (compileErrors) {
                error() << compileLog << std::endl;
            } else {
                warning() << compileLog << std::endl;
            }
        }
    }
    return !compileErrors; // return true iff there were NO errors
Robert Menzel's avatar
Robert Menzel committed
94
95
}

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
bool Shader::setSources(const std::vector<std::string> &_sources, bool _checkForCompileErrors )
{
    unsigned int numberOfStrings = _sources.size();
    const char **pProgramStrings = new const char*[ numberOfStrings ];

    for (unsigned int i = 0; i < numberOfStrings; ++i) {
        pProgramStrings[i] = _sources[i].c_str();
    }

    glShaderSource(mObjectName, numberOfStrings, pProgramStrings, NULL); // can't create OpenGL errors
    delete[] pProgramStrings;

    if (!compile()) {
        return false;
    }
    // the compile call should work even if there are compile errors itself:
    bool compileErrors = false;
    if (_checkForCompileErrors) {
        std::string compileLog;
        getCompileLog( compileLog, compileErrors );
        if (compileLog.size() > 0) {
            if (compileErrors) {
                error() << compileLog << std::endl;
            } else {
                warning() << compileLog << std::endl;
            }
        }
    }
    return !compileErrors; // return true iff there were NO errors
}

bool Shader::compile() const
Robert Menzel's avatar
Robert Menzel committed
128
{
129
    glCompileShader(mObjectName);
Robert Menzel's avatar
Robert Menzel committed
130
    
131
132
133
134
135
#ifdef ACGL_CHECK_CRITICAL_GL_ERRORS
    // problems with the shader creation are always a bad thing...
    // from here on only error checks are performed:
    //
    if ( openGLRareErrorOccured() )
Robert Menzel's avatar
Robert Menzel committed
136
    {
137
        error() << "glCompileShader failed, that can only mean, that the object name used is not a valid shader object!" << std::endl;
Robert Menzel's avatar
Robert Menzel committed
138
139
        return false;
    }
140
141
142
#endif
    return true;
}
Robert Menzel's avatar
Robert Menzel committed
143

144
145
void Shader::getCompileLog( std::string &_log, bool &_wasErrorLog ) const
{
Robert Menzel's avatar
Robert Menzel committed
146
147
    // check for shader compile errors:
    GLint shaderError;
148
    glGetShaderiv(mObjectName, GL_COMPILE_STATUS, &shaderError);
Robert Menzel's avatar
Robert Menzel committed
149
150
151
    if(shaderError != GL_TRUE)
    {
        // yes, errors
152
153
154
        _wasErrorLog = true;
    } else {
        _wasErrorLog = false;
Robert Menzel's avatar
Robert Menzel committed
155
156
    }

157
    // the log could be warnings:
Robert Menzel's avatar
Robert Menzel committed
158
    GLsizei length = 0;
159
    glGetShaderiv(mObjectName, GL_INFO_LOG_LENGTH, &length);
160
    if(length > 1) // null terminated, so 1 could also be empty
Robert Menzel's avatar
Robert Menzel committed
161
162
    {
        // a compile log can get produced even if there were no errors, but warnings!
163
        GLchar* pInfo = new char[length];
164
        glGetShaderInfoLog(mObjectName,  length, &length, pInfo);
165
166
        //error() << "Compile log: " << std::string(pInfo) << std::endl;
        _log = std::string( pInfo );
Robert Menzel's avatar
Robert Menzel committed
167
        delete[] pInfo;
168
169
    } else {
        _log = "";
Robert Menzel's avatar
Robert Menzel committed
170
171
    }
}
172
173
174
175
176
177
178
179
180
181
182
183


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ShaderParser::ShaderParser( const std::string &_filename )
{
    mMaxVersion = 110;

    mSources.push_back( "#version 330\n" );
    std::string path, file;
184
    StringHelpers::splitLastFileOrFolder( _filename, path, file );
185
186
187
188
    //readin( "./"+path+"/"+file );
    readin( _filename );

    if (mMaxVersion > 110) {
189
        mSources[0] = "#version "+StringHelpers::toString(mMaxVersion)+"\n";
190
191
192
193
194
195
    }
}


bool ShaderParser::lineContainsVersion( const std::string &line, unsigned int &version)
{
196
    std::vector< std::string > token = StringHelpers::split( line, ' ' );
197
198

    if (token.size() >= 2 && token[0] == "#version") {
199
        version = StringHelpers::to<unsigned int>(token[1]);
200
201
202
203
204
205
206
207
        //debug() << "string " << line << " -> version " << version << std::endl;
        return true;
    }
    return false;
}

bool ShaderParser::lineContainsImport( const std::string &line, std::string &filename)
{
208
    std::vector< std::string > token = StringHelpers::split( line, ' ' );
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226

    if (token.size() >= 3 && token[0] == "#pragma" && token[1] == "ACGLimport") {
        if (token[2].size() >= 2) {

            size_t startName = line.find('"',0);
            if (startName == std::string::npos) return false;
            size_t endName = line.find('"',startName+1);
            if (endName == std::string::npos) return false;

            filename = line.substr(startName+1, endName-startName-1); // remove the ""

            //debug() << "string " << line << " -> import " << filename << std::endl;
            return true;
        }
    }
    return false;
}

227
std::string ShaderParser::getFileNamesPrintable() const
228
229
230
{
    std::string rString;
    for (size_t i = 0; i < mSourceFileNames.size(); ++i) {
231
        rString += " " + StringHelpers::toString<unsigned int>(i+1) + " " + mSourceFileNames[i] + "\n";
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
    }
    return rString;
}

void ShaderParser::readin( const std::string &_filename )
{
    std::string line = "";

    std::ifstream fileStream(_filename.c_str(), std::ifstream::in);

    unsigned int lineNumber = 1;
    mSourceFileNames.push_back( _filename );
    unsigned int fileNumber = mSourceFileNames.size();

    // define the file and line number to get correct errors from the shader compiler:
247
    std::string fileContent = "#line "+StringHelpers::toString(lineNumber)+" "+StringHelpers::toString(fileNumber)+"\n";
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272

    //debug() << "parse file " << _filename << std::endl;

    if(fileStream.is_open())
    {
        while (fileStream.good())
        {
            std::getline(fileStream,line);

            unsigned int version;
            std::string fileToImport;
            if ( lineContainsVersion(line, version) ) {
                mMaxVersion = std::max( version, mMaxVersion );
                fileContent += "\n"; // remove the #version but keep a newline
                lineNumber++;
            } else if ( lineContainsImport(line, fileToImport ) ) {
                // handle import
                if (fileToImport[0] != '/') {
                    // it's a relative path:

                    // remove "./" in case this was given explicitly:
                    if(fileToImport.size() > 2 && fileToImport[0] == '.' && fileToImport[1] == '/') {
                        fileToImport = fileToImport.substr(2, fileToImport.size()-2);
                    }
                    std::string path, file;
273
                    StringHelpers::splitLastFileOrFolder( _filename, path, file );
274
275
276
277
278
279
280
281
282
                    fileToImport = path+"/"+fileToImport;
                }

                if ( std::find(mSourceFileNames.begin(), mSourceFileNames.end(), fileToImport) == mSourceFileNames.end()) {
                    // not imported yet:
                    // push what we have till now:
                    mSources.push_back( fileContent );
                    readin( fileToImport );
                    lineNumber++;
Robert Menzel's avatar
Robert Menzel committed
283
                    fileContent = "#line "+StringHelpers::toString(lineNumber)+" "+StringHelpers::toString(fileNumber)+"\n";
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
                } else {
                    // ignore:
                    lineNumber++;
                    fileContent += "\n";
                }
            } else {
                fileContent += line + "\n";
                lineNumber++;
            }
        }
        fileStream.close();
    }
    else
    {
        error() << "Failed to open file: " << _filename << std::endl;
        return;
    }

    //debug() << "file: " << fileContent << std::endl;

    mSources.push_back( fileContent );
}