1 // glshader.cpp
2 //
3 // Copyright (C) 2001-2006, Chris Laurel <claurel@shatters.net>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 
10 #include <iostream>
11 #include "glshader.h"
12 #include "gl.h"
13 #include "glext.h"
14 
15 using namespace std;
16 
17 
18 static const string GetInfoLog(GLhandleARB obj);
19 
20 
21 ostream* g_shaderLogFile = NULL;
22 
23 
GLShader(GLhandleARB _id)24 GLShader::GLShader(GLhandleARB _id) :
25     id(_id)
26 {
27 }
28 
29 
30 GLhandleARB
getID() const31 GLShader::getID() const
32 {
33     return id;
34 }
35 
36 
37 GLShaderStatus
compile(const vector<string> & source)38 GLShader::compile(const vector<string>& source)
39 {
40     if (source.empty())
41         return ShaderStatus_EmptyProgram;
42 
43     // Convert vector of shader source strings to an array for OpenGL
44     const char** sourceStrings = new const char*[source.size()];
45     for (unsigned int i = 0; i < source.size(); i++)
46         sourceStrings[i] = source[i].c_str();
47 
48     // Copy shader source to OpenGL
49     glx::glShaderSourceARB(id, source.size(), sourceStrings, NULL);
50     delete[] sourceStrings;
51 
52     // Actually compile the shader
53     glx::glCompileShaderARB(id);
54 
55     GLint compileSuccess;
56     glx::glGetObjectParameterivARB(id, GL_OBJECT_COMPILE_STATUS_ARB,
57                                    &compileSuccess);
58     if (compileSuccess == GL_FALSE)
59         return ShaderStatus_CompileError;
60 
61     return ShaderStatus_OK;
62 }
63 
64 
~GLShader()65 GLShader::~GLShader()
66 {
67     glx::glDeleteObjectARB(id);
68 }
69 
70 
71 
72 //************* GLxxxProperty **********
73 
FloatShaderParameter()74 FloatShaderParameter::FloatShaderParameter() :
75     slot(-1)
76 {
77 }
78 
FloatShaderParameter(GLhandleARB obj,const char * name)79 FloatShaderParameter::FloatShaderParameter(GLhandleARB obj, const char* name)
80 {
81     slot = glx::glGetUniformLocationARB(obj, name);
82 }
83 
84 FloatShaderParameter&
operator =(float f)85 FloatShaderParameter::operator=(float f)
86 {
87     if (slot != -1)
88         glx::glUniform1fARB(slot, f);
89     return *this;
90 }
91 
92 
Vec3ShaderParameter()93 Vec3ShaderParameter::Vec3ShaderParameter() :
94     slot(-1)
95 {
96 }
97 
Vec3ShaderParameter(GLhandleARB obj,const char * name)98 Vec3ShaderParameter::Vec3ShaderParameter(GLhandleARB obj, const char* name)
99 {
100     slot = glx::glGetUniformLocationARB(obj, name);
101 }
102 
103 Vec3ShaderParameter&
operator =(const Vec3f & v)104 Vec3ShaderParameter::operator=(const Vec3f& v)
105 {
106     if (slot != -1)
107         glx::glUniform3fARB(slot, v.x, v.y, v.z);
108     return *this;
109 }
110 
111 Vec3ShaderParameter&
operator =(const Point3f & p)112 Vec3ShaderParameter::operator=(const Point3f& p)
113 {
114     if (slot != -1)
115         glx::glUniform3fARB(slot, p.x, p.y, p.z);
116     return *this;
117 }
118 
119 
Vec4ShaderParameter()120 Vec4ShaderParameter::Vec4ShaderParameter() :
121     slot(-1)
122 {
123 }
124 
Vec4ShaderParameter(GLhandleARB obj,const char * name)125 Vec4ShaderParameter::Vec4ShaderParameter(GLhandleARB obj, const char* name)
126 {
127     slot = glx::glGetUniformLocationARB(obj, name);
128 }
129 
130 Vec4ShaderParameter&
operator =(const Vec4f & v)131 Vec4ShaderParameter::operator=(const Vec4f& v)
132 {
133     if (slot != -1)
134         glx::glUniform4fARB(slot, v.x, v.y, v.z, v.w);
135     return *this;
136 }
137 
138 
139 //************* GLProgram **************
140 
GLProgram(GLhandleARB _id)141 GLProgram::GLProgram(GLhandleARB _id) :
142     id(_id)
143 {
144 }
145 
146 
~GLProgram()147 GLProgram::~GLProgram()
148 {
149     glx::glDeleteObjectARB(id);
150 }
151 
152 
153 void
use() const154 GLProgram::use() const
155 {
156     glx::glUseProgramObjectARB(id);
157 }
158 
159 
160 void
attach(const GLShader & shader)161 GLProgram::attach(const GLShader& shader)
162 {
163     glx::glAttachObjectARB(id, shader.getID());
164 }
165 
166 
167 GLShaderStatus
link()168 GLProgram::link()
169 {
170     glx::glLinkProgramARB(id);
171 
172     GLint linkSuccess;
173     glx::glGetObjectParameterivARB(id, GL_OBJECT_LINK_STATUS_ARB,
174                                    &linkSuccess);
175     if (linkSuccess == GL_FALSE)
176     {
177         if (g_shaderLogFile != NULL)
178         {
179             *g_shaderLogFile << "Error linking shader program:\n";
180             *g_shaderLogFile << GetInfoLog(getID());
181         }
182         return ShaderStatus_LinkError;
183     }
184 
185     return ShaderStatus_OK;
186 }
187 
188 
189 //************* GLShaderLoader ************
190 
191 GLShaderStatus
CreateVertexShader(const vector<string> & source,GLVertexShader ** vs)192 GLShaderLoader::CreateVertexShader(const vector<string>& source,
193                                    GLVertexShader** vs)
194 {
195     GLhandleARB vsid = glx::glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
196 
197     GLVertexShader* shader = new GLVertexShader(vsid);
198     if (!shader)
199         return ShaderStatus_OutOfMemory;
200 
201     GLShaderStatus status = shader->compile(source);
202     if (status != ShaderStatus_OK)
203     {
204         if (g_shaderLogFile != NULL)
205         {
206             *g_shaderLogFile << "Error compiling vertex shader:\n";
207             *g_shaderLogFile << GetInfoLog(shader->getID());
208         }
209         return status;
210     }
211 
212     *vs = shader;
213 
214     return ShaderStatus_OK;
215 }
216 
217 
218 GLShaderStatus
CreateFragmentShader(const vector<string> & source,GLFragmentShader ** fs)219 GLShaderLoader::CreateFragmentShader(const vector<string>& source,
220                                      GLFragmentShader** fs)
221 {
222     GLhandleARB fsid = glx::glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
223 
224     GLFragmentShader* shader = new GLFragmentShader(fsid);
225     if (!shader)
226         return ShaderStatus_OutOfMemory;
227 
228     GLShaderStatus status = shader->compile(source);
229     if (status != ShaderStatus_OK)
230     {
231         if (g_shaderLogFile != NULL)
232         {
233             *g_shaderLogFile << "Error compiling fragment shader:\n";
234             *g_shaderLogFile << GetInfoLog(shader->getID());
235         }
236         return status;
237     }
238 
239     *fs = shader;
240 
241     return ShaderStatus_OK;
242 }
243 
244 
245 GLShaderStatus
CreateVertexShader(const string & source,GLVertexShader ** vs)246 GLShaderLoader::CreateVertexShader(const string& source,
247                                    GLVertexShader** vs)
248 
249 {
250     vector<string> v;
251     v.push_back(source);
252     return CreateVertexShader(v, vs);
253 }
254 
255 
256 GLShaderStatus
CreateFragmentShader(const string & source,GLFragmentShader ** fs)257 GLShaderLoader::CreateFragmentShader(const string& source,
258                                      GLFragmentShader** fs)
259 {
260     vector<string> v;
261     v.push_back(source);
262     return CreateFragmentShader(v, fs);
263 }
264 
265 
266 GLShaderStatus
CreateProgram(const GLVertexShader & vs,const GLFragmentShader & fs,GLProgram ** progOut)267 GLShaderLoader::CreateProgram(const GLVertexShader& vs,
268                               const GLFragmentShader& fs,
269                               GLProgram** progOut)
270 {
271     GLhandleARB progid = glx::glCreateProgramObjectARB();
272 
273     GLProgram* prog = new GLProgram(progid);
274     if (!prog)
275         return ShaderStatus_OutOfMemory;
276 
277     prog->attach(vs);
278     prog->attach(fs);
279 
280     *progOut = prog;
281 
282     return ShaderStatus_OK;
283 }
284 
285 
286 GLShaderStatus
CreateProgram(const vector<string> & vsSource,const vector<string> & fsSource,GLProgram ** progOut)287 GLShaderLoader::CreateProgram(const vector<string>& vsSource,
288                               const vector<string>& fsSource,
289                               GLProgram** progOut)
290 {
291     GLVertexShader* vs = NULL;
292     GLShaderStatus status = CreateVertexShader(vsSource, &vs);
293     if (status != ShaderStatus_OK)
294         return status;
295 
296     GLFragmentShader* fs = NULL;
297     status = CreateFragmentShader(fsSource, &fs);
298     if (status != ShaderStatus_OK)
299     {
300         delete vs;
301         return status;
302     }
303 
304     GLProgram* prog = NULL;
305     status = CreateProgram(*vs, *fs, &prog);
306     if (status != ShaderStatus_OK)
307     {
308         delete vs;
309         delete fs;
310         return status;
311     }
312 
313     *progOut = prog;
314 
315     // No need to keep these around--the program doesn't reference them
316     delete vs;
317     delete fs;
318 
319     return ShaderStatus_OK;
320 }
321 
322 
323 GLShaderStatus
CreateProgram(const string & vsSource,const string & fsSource,GLProgram ** progOut)324 GLShaderLoader::CreateProgram(const string& vsSource,
325                               const string& fsSource,
326                               GLProgram** progOut)
327 {
328     vector<string> vsSourceVec;
329     vsSourceVec.push_back(vsSource);
330     vector<string> fsSourceVec;
331     fsSourceVec.push_back(fsSource);
332 
333     return CreateProgram(vsSourceVec, fsSourceVec, progOut);
334 }
335 
336 
337 const string
GetInfoLog(GLhandleARB obj)338 GetInfoLog(GLhandleARB obj)
339 {
340     GLint logLength = 0;
341     GLsizei charsWritten = 0;
342 
343     glx::glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB,
344                                    &logLength);
345     if (logLength <= 0)
346         return string();
347 
348     char* log = new char[logLength];
349     if (log == NULL)
350         return string();
351 
352     glx::glGetInfoLogARB(obj, logLength, &charsWritten, log);
353 
354     string s(log, charsWritten);
355     delete[] log;
356 
357     return s;
358 }
359