1/*
2 * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "shaderprogram.h"
24#include "graphics.h"
25
26#include <framework/core/application.h>
27
28uint ShaderProgram::m_currentProgram = 0;
29
30ShaderProgram::ShaderProgram(const std::string& name) : m_name(name)
31{
32 m_linked = false;
33 m_programId = glCreateProgram();
34 m_uniformLocations.fill(-1);
35 if(!m_programId)
36 g_logger.fatal("Unable to create GL shader program");
37}
38
39ShaderProgram::~ShaderProgram()
40{
41 VALIDATE(!g_app.isTerminated());
42 if(g_graphics.ok())
43 glDeleteProgram(m_programId);
44}
45
46PainterShaderProgramPtr ShaderProgram::create(const std::string& name, const std::string& vertexShader, const std::string& fragmentShader, bool colorMatrix)
47{
48 PainterShaderProgramPtr program(new PainterShaderProgram(name));
49 if (!program) {
50 g_logger.error(stdext::format("Cant creatre shader: %s", program->getName()));
51 return nullptr;
52 }
53 if (!program->addShaderFromSourceCode(Shader::Vertex, vertexShader))
54 return nullptr;
55 if(!program->addShaderFromSourceCode(Shader::Fragment, fragmentShader))
56 return nullptr;
57
58 if (colorMatrix) {
59 program->enableColorMatrix();
60 }
61 if (!program->link()) {
62 g_graphics.checkForError(stdext::format("%s (%s)", __FUNCTION__, program->getName()), vertexShader + "\n" + fragmentShader, __LINE__);
63 return nullptr;
64 }
65 g_graphics.checkForError(stdext::format("%s (%s)", __FUNCTION__, program->getName()), vertexShader + "\n" + fragmentShader, __LINE__);
66 return program;
67}
68
69
70bool ShaderProgram::addShader(const ShaderPtr& shader) {
71 glAttachShader(m_programId, shader->getShaderId());
72 m_linked = false;
73 m_shaders.push_back(shader);
74 return true;
75}
76
77bool ShaderProgram::addShaderFromSourceCode(Shader::ShaderType shaderType, const std::string& sourceCode) {
78 ShaderPtr shader(new Shader(shaderType));
79 if(!shader->compileSourceCode(sourceCode)) {
80 g_logger.error(stdext::format("failed to compile shader: %s\n%s", getName(), shader->log()));
81 return false;
82 }
83 return addShader(shader);
84}
85
86bool ShaderProgram::addShaderFromSourceFile(Shader::ShaderType shaderType, const std::string& sourceFile) {
87 ShaderPtr shader(new Shader(shaderType));
88 if(!shader->compileSourceFile(sourceFile)) {
89 g_logger.error(stdext::format("failed to compile shader %s (%s): %s", getName(), sourceFile, shader->log()));
90 return false;
91 }
92 return addShader(shader);
93}
94
95void ShaderProgram::removeShader(const ShaderPtr& shader)
96{
97 auto it = std::find(m_shaders.begin(), m_shaders.end(), shader);
98 if(it == m_shaders.end())
99 return;
100
101 glDetachShader(m_programId, shader->getShaderId());
102 m_shaders.erase(it);
103 m_linked = false;
104}
105
106void ShaderProgram::removeAllShaders()
107{
108 while(!m_shaders.empty())
109 removeShader(m_shaders.front());
110}
111
112bool ShaderProgram::link()
113{
114 if(m_linked)
115 return true;
116
117 glLinkProgram(m_programId);
118
119 int value = GL_FALSE;
120 glGetProgramiv(m_programId, GL_LINK_STATUS, &value);
121 m_linked = (value != GL_FALSE);
122 if (m_linked) {
123 return true;
124 }
125
126 GLint maxLength = 0;
127 glGetProgramiv(m_programId, GL_INFO_LOG_LENGTH, &maxLength);
128 std::vector<GLchar> infoLog(maxLength);
129 glGetProgramInfoLog(m_programId, maxLength, &maxLength, &infoLog[0]);
130 g_logger.error(stdext::format("Program %i, %s linking error (%i): %s - %s - %s %s\nExtensions: %s", m_programId, getName(), infoLog.size(),
131 std::string(infoLog.begin(), infoLog.end()).c_str(), log().c_str(),
132 g_graphics.getRenderer(), g_graphics.getVersion(), g_graphics.getExtensions()));
133 return false;
134}
135
136bool ShaderProgram::bind()
137{
138 if(m_currentProgram != m_programId) {
139 if (!m_linked) {
140 link();
141 }
142 glUseProgram(m_programId);
143 m_currentProgram = m_programId;
144 }
145 return true;
146}
147
148void ShaderProgram::release()
149{
150 if(m_currentProgram != 0) {
151 m_currentProgram = 0;
152 glUseProgram(0);
153 }
154}
155
156std::string ShaderProgram::log()
157{
158 std::string infoLog;
159 int infoLogLength = 0;
160 glGetProgramiv(m_programId, GL_INFO_LOG_LENGTH, &infoLogLength);
161 if(infoLogLength > 1) {
162 std::vector<char> buf(infoLogLength);
163 glGetShaderInfoLog(m_programId, infoLogLength-1, NULL, &buf[0]);
164 infoLog = &buf[0];
165 }
166 return infoLog;
167}
168
169int ShaderProgram::getAttributeLocation(const char* name)
170{
171 return glGetAttribLocation(m_programId, name);
172}
173
174void ShaderProgram::bindAttributeLocation(int location, const char* name)
175{
176 return glBindAttribLocation(m_programId, location, name);
177}
178
179void ShaderProgram::bindUniformLocation(int location, const char* name)
180{
181 VALIDATE(m_linked);
182 VALIDATE(location >= 0 && location < MAX_UNIFORM_LOCATIONS);
183 m_uniformLocations[location] = glGetUniformLocation(m_programId, name);
184}
185