1 /* Copyright (c) 2015 Gerald Knizia
2 *
3 * This file is part of the IboView program (see: http://www.iboview.org)
4 *
5 * IboView is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * IboView is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
16 *
17 * Please see IboView documentation in README.txt for:
18 * -- A list of included external software and their licenses. The included
19 * external software's copyright is not touched by this agreement.
20 * -- Notes on re-distribution and contributions to/further development of
21 * the IboView software
22 */
23
24 #include "IvGl.h"
25
26 #include <boost/format.hpp>
27 #include <map>
28 #include <iostream>
29 using boost::format;
30 #include <QRect>
31 #include <QImage>
32 #include <QApplication>
33 #include <QClipboard>
34 #include <QFile>
35 #include <QTextStream>
36 #include <QDir>
37
38 #include <stdio.h>
39 #include <stdexcept>
40 #include <sstream>
41 #include <cstdlib> // for rand
42
43 #include "CxColor.h"
44 // #include "CxOsInt.h"
45 #include "CxVec3.h"
46 using namespace ct;
47
48 int g_GlVersion = 30;
49 #ifdef __APPLE__
50 // well, that may be overkill... it's probably not *all* apples, just some, which have this particular
51 // driver bug. should be controllable via command line argument, once we introduce sensible handling of those.
52 // (hm.. there is a QCommandLineParser. That might be useful).
53 bool g_WorkAroundGlFrontFacingBug = true;
54 #else
55 bool g_WorkAroundGlFrontFacingBug = false;
56 #endif
57 bool g_WorkAroundAlphaCompositing = false;
58
59
hStack(FVec3f const & Row0,FVec3f const & Row1,FVec3f const & Row2,FVec3f const & Row3)60 FMat4f hStack(FVec3f const &Row0, FVec3f const &Row1, FVec3f const &Row2, FVec3f const &Row3)
61 {
62 FMat4f r;
63 r(0,0) = Row0[0]; r(1,0) = Row0[1]; r(2,0) = Row0[2]; r(3,0) = 0.;
64 r(0,1) = Row1[0]; r(1,1) = Row1[1]; r(2,1) = Row1[2]; r(3,1) = 0.;
65 r(0,2) = Row2[0]; r(1,2) = Row2[1]; r(2,2) = Row2[2]; r(3,2) = 0.;
66 r(0,3) = Row3[0]; r(1,3) = Row3[1]; r(2,3) = Row3[2]; r(3,3) = 1.;
67 return r;
68 }
69
70 // vec4f vertices of a full screen quad given as two triangles.
71 static const GLfloat s_QuadPoints[] = {
72 -1.0f, -1.0f, 0.0f, 1.0f,
73 1.0f, -1.0f, 0.0f, 1.0f,
74 -1.0f, 1.0f, 0.0f, 1.0f,
75 1.0f, -1.0f, 0.0f, 1.0f,
76 1.0f, 1.0f, 0.0f, 1.0f,
77 -1.0f, 1.0f, 0.0f, 1.0f
78 };
79
Init()80 void FFullScreenQuad::Init()
81 {
82 // make a vertex descriptor-- we have just positions.
83 CALL_GL( glGenVertexArrays(1, &hVertexDesc) );
84 CALL_GL( glBindVertexArray(hVertexDesc) );
85
86 CALL_GL( glGenBuffers(1, &IdVertexBuffer) );
87 CALL_GL( glBindBuffer(GL_ARRAY_BUFFER, IdVertexBuffer) )
88 CALL_GL( glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, 0) ); // must be done after GL_ARRAY_BUFFER is bound.
89 CALL_GL( glBufferData(GL_ARRAY_BUFFER, sizeof(s_QuadPoints), s_QuadPoints, GL_STATIC_DRAW) )
90 CheckGlError("FullScreenQuadInit");
91 }
92
~FFullScreenQuad()93 FFullScreenQuad::~FFullScreenQuad()
94 {
95 CALL_GL( glDeleteVertexArrays(1, &hVertexDesc) );
96 CALL_GL( glDeleteBuffers(1, &IdVertexBuffer) );
97 hVertexDesc = 0;
98 IdVertexBuffer = 0;
99 }
100
Draw(GLenum Program)101 void FFullScreenQuad::Draw(GLenum Program)
102 {
103 CALL_GL( glUseProgram(Program) );
104 CALL_GL( glBindVertexArray(hVertexDesc) );
105 GLuint
106 IdPos = glGetAttribLocation(Program, "in_Position");
107 CALL_GL( glEnableVertexAttribArray(IdPos) );
108 CALL_GL( glBindBuffer(GL_ARRAY_BUFFER, IdVertexBuffer) );
109 CALL_GL( glVertexAttribPointer(IdPos, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*4, 0) );
110 CALL_GL( glDrawArrays(GL_TRIANGLES, 0, sizeof(s_QuadPoints)/sizeof(s_QuadPoints[0])) );
111 CheckGlError("FFullScreenQuad::Draw");
112 }
113
FFrameBufferAttachment(char const * pDesc_,QSize Size_,GLenum Attachment_,uint nSamples_,GLuint InternalFormat,GLuint Format,GLenum Type)114 FFrameBufferAttachment::FFrameBufferAttachment(char const *pDesc_, QSize Size_, GLenum Attachment_, uint nSamples_, GLuint InternalFormat, GLuint Format, GLenum Type)
115 {
116 CheckGlError(pDesc, "FFrameBufferAttachment/c'tor (enter)");
117 nSamples = nSamples_;
118 GlAttachment = Attachment_;
119 pDesc = pDesc_;
120 Size = Size_;
121
122 if (nSamples == 0)
123 GlTarget = GL_TEXTURE_2D;
124 else
125 GlTarget = GL_TEXTURE_2D_MULTISAMPLE;
126
127 CALL_GL( glGenTextures(1, &this->hTexture) );
128 CALL_GL( glBindTexture(GlTarget, hTexture) );
129 if (nSamples == 0) {
130 // glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
131 // CheckGlError(pDesc, "glTexParameteri/GL_GENERATE_MIPMAP");
132 // ^- not present in core profile, apparently, and anyway disabled by default (use glGenerateMipmap.)
133 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) );
134 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) );
135 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) );
136 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) );
137 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0) ); // <- otherwise lots of "Waste of memory: Texture 0 has mipmaps, while its min filter is inconsistent with mipmaps."-warnings.
138 CALL_GL( glTexImage2D(GlTarget, 0, InternalFormat, width(), height(), 0, Format, Type, 0) );
139 } else {
140 CALL_GL( glTexImage2DMultisample(GlTarget, nSamples, InternalFormat, width(), height(), GL_FALSE) );
141 }
142 CALL_GL( glBindTexture(GlTarget, 0) ); // unbind.
143 CheckGlError(pDesc, "FFrameBufferAttachment/c'tor (leave)");
144 // note: this makes the attachment. It is, however, not yet attached to anything.
145 // that only happens in FFrameBuffer::Attach.
146 }
147
~FFrameBufferAttachment()148 FFrameBufferAttachment::~FFrameBufferAttachment()
149 {
150 CALL_GL( glBindTexture(GL_TEXTURE_2D, 0) );
151 // ^- on Macs the glDeleteTextures gives "INVALID OPERATION". I don't think it is supposed to.
152 // but I read online that some other people had luck with unbinding the attachments first.
153 CALL_GL( glDeleteTextures(1, &hTexture) );
154 }
155
BindAsTexture(GLenum GlTexture)156 void FFrameBufferAttachment::BindAsTexture(GLenum GlTexture)
157 {
158 CheckGlError(pDesc, "FFrameBufferAttachment::BindAsTexture (enter)");
159 CALL_GL( glActiveTexture(GlTexture) );
160 CALL_GL( glBindTexture(GlTarget, hTexture) );
161 }
162
UnbindAsTexture(GLenum GlTexture)163 void FFrameBufferAttachment::UnbindAsTexture(GLenum GlTexture)
164 {
165 CheckGlError(pDesc, "FFrameBufferAttachment::Unbind (enter)");
166 CALL_GL( glActiveTexture(GlTexture) );
167 CALL_GL( glBindTexture(GlTarget, 0) );
168 }
169
FFrameBuffer(char const * pDesc_,QSize Size_,bool ReferCurrent)170 FFrameBuffer::FFrameBuffer(char const *pDesc_, QSize Size_, bool ReferCurrent)
171 : pDesc(pDesc_), Size(Size_)
172 {
173 CheckGlError("FFrameBuffer::c'tor (enter)");
174 if (!ReferCurrent) {
175 // make a new frame buffer
176 CALL_GL( glGenFramebuffers(1, &hFbo) );
177 Bind(GL_FRAMEBUFFER, false);
178 NeedCleanup = true;
179 } else {
180 // make an empty object referring to the current frame buffer.
181 CALL_GL( glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&hFbo) );
182 if (hFbo != 0)
183 IvNotify(NOTIFY_Warning, "Default frame buffer does not have id 0. I think it should have!");
184 NeedCleanup = false;
185 }
186 CheckGlError("FFrameBuffer::c'tor (leave)");
187 }
188
~FFrameBuffer()189 FFrameBuffer::~FFrameBuffer()
190 {
191 if (NeedCleanup && hFbo != 0) {
192 CALL_GL( glDeleteFramebuffers(1, &hFbo) );
193 hFbo = 0;
194 }
195 }
196
Bind(GLenum Target,bool AssertCompleteness)197 void FFrameBuffer::Bind(GLenum Target, bool AssertCompleteness)
198 {
199 CheckGlError("bind frame buffer (enter)");
200 CALL_GL( glBindFramebuffer(Target, hFbo) );
201 CALL_GL( glViewport(0, 0, width(), height()) );
202
203 if (AssertCompleteness && glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
204 IvNotify(NOTIFY_Error, QString("FFrameBuffer::Bind(%1): Bind succeeded, but frame buffer is incomplete.").arg(pDesc));
205 CheckGlError("bind frame buffer (leave)");
206 }
207
Attach(FFrameBufferAttachmentPtr p)208 void FFrameBuffer::Attach(FFrameBufferAttachmentPtr p)
209 {
210 // attach the surface to the current FBO.
211 CheckGlError("attach frame buffer (enter)");
212 CALL_GL( glBindFramebuffer(GL_FRAMEBUFFER, hFbo) );
213 CALL_GL( glFramebufferTexture2D(GL_FRAMEBUFFER, p->GlAttachment, p->GlTarget, p->hTexture, 0) );
214 Attachments.push_back(p);
215 }
216
217
BlitFrom(FFrameBuffer * pSrc,GLbitfield Layers,GLenum Filter)218 void FFrameBuffer::BlitFrom(FFrameBuffer *pSrc, GLbitfield Layers, GLenum Filter)
219 {
220 CheckGlError("blit frame buffer (enter)");
221 CALL_GL( glBindFramebuffer(GL_READ_FRAMEBUFFER, pSrc->hFbo) );
222 CALL_GL( glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->hFbo) );
223 // Layers: e.g., GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
224 // Filter: e.g., GL_NEAREST
225 CALL_GL( glBlitFramebuffer(0, 0, pSrc->width(), pSrc->height(), 0, 0, this->width(), this->height(), Layers, Filter) );
226 CheckGlError("blit frame buffer (leave)");
227 };
228
229
Finalize()230 void FFrameBuffer::Finalize()
231 {
232 CheckGlError("FFrameBuffer::Finalize (enter)");
233 // GLenum DrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
234 GLenum
235 DrawBuffers[16] = {0};
236 uint
237 nDrawBuffers = 0;
238 for (uint i = 0; i < Attachments.size(); ++ i) {
239 GLuint g = Attachments[i]->GlAttachment;
240 // apparently I'm only supposed to register color attachments,
241 // not depth or stencil.
242 if (g >= GL_COLOR_ATTACHMENT0 && g <= GL_COLOR_ATTACHMENT15) {
243 assert(nDrawBuffers < sizeof(DrawBuffers)/sizeof(DrawBuffers[0]));
244 DrawBuffers[nDrawBuffers] = g;
245 nDrawBuffers += 1;
246 }
247 }
248 CALL_GL(glDrawBuffers(nDrawBuffers, &DrawBuffers[0]));
249
250 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
251 IvNotify(NOTIFY_Error, QString("Failed to finalize deferred frame buffer '%1'.").arg(pDesc));
252 CheckGlError("FFrameBuffer::Finalize (leave)");
253 }
254
255
FImageFilter(FShaderSet & ShaderSet,FFilterType Type)256 FImageFilter::FImageFilter(FShaderSet &ShaderSet, FFilterType Type)
257 : Filter5HV(ShaderSet)
258 {
259 CheckGlError("FImageFilter::c'tor (enter)");
260 CALL_GL( glUseProgram(ShaderSet.hProgram) );
261 IdFilterStepH = ShaderSet.GetUniformLocation("FilterStepH");
262 IdFilterStepV = ShaderSet.GetUniformLocation("FilterStepV");
263 IdFilterWeight = ShaderSet.GetUniformLocation("FilterWeight");
264 IdSourceImage = ShaderSet.GetUniformLocation("SourceImage");
265 InitKernel(Type);
266 CheckGlError("FImageFilter::c'tor (leave)");
267 }
268
InitKernel(FImageFilter::FFilterType Type)269 void FImageFilter::InitKernel(FImageFilter::FFilterType Type)
270 {
271 if (Type == FILTER_Blur) {
272 GLfloat
273 // BlurWeights[] = {1., -3., 9., -3., 1.},
274 // BlurWeights[] = {-1., 3., 9., 3., -1.},
275 // BlurWeights[] = {-1., 2, 5., 2, -1.},
276 BlurWeights[5],
277 w = 0;
278 for (uint i = 0; i < nFilterSize; ++ i) {
279 float x = (double)i - 2.;
280 // float sigma = 1.3;
281 float sigma = 0.7;
282 BlurWeights[i] = std::exp(-sqr(x)/(2*sqr(sigma)));
283 }
284 for (uint i = 0; i < nFilterSize; ++ i)
285 w += BlurWeights[i];
286 for (uint i = 0; i < nFilterSize; ++ i)
287 Kernel[i] = BlurWeights[i] / w;
288 }
289 }
290
291
GetGlErrorString(GLenum ErrorCode)292 static char const *GetGlErrorString(GLenum ErrorCode) {
293 switch (ErrorCode) {
294 case GL_NO_ERROR: return "No error";
295 case GL_INVALID_ENUM: return "Invalid enum value.";
296 case GL_INVALID_VALUE: return "Invalid value (numeric argument out of range)";
297 case GL_INVALID_OPERATION: return "Invalid operation (op not allowed in current state)";
298 case GL_STACK_OVERFLOW: return "Command would cause stack overflow";
299 case GL_STACK_UNDERFLOW: return "Command would cause stack underflow";
300 case GL_OUT_OF_MEMORY: return "Out of memory (not enough memory to execute the command)";
301 case GL_TABLE_TOO_LARGE: return "Table size exceed implementation's maximum allowed table size";
302 default: return "(GL Error code not recognized)";
303 }
304 }
305
CheckGlError(char const * pDesc,char const * pDesc2)306 void CheckGlError(char const *pDesc, char const *pDesc2) {
307 bool
308 Brrrk = false;
309 size_t nErrors = 0;
310 for ( ; ; ) { // hm... it's an error stack.
311 GLenum
312 ErrorCode = glGetError();
313 if (ErrorCode == GL_NO_ERROR)
314 break;
315 QString
316 Desc(pDesc);
317 if (pDesc2)
318 Desc = Desc + "/" + QString(pDesc2);
319 // __debugbreak;
320 // *(int*)0 = 0;
321 // throw std::runtime_error("OpenGL broke :(.");
322
323 IvNotify(NOTIFY_Error, "Problem with OpenGL: " + IvFmt("Error in %1: %2", Desc, GetGlErrorString(ErrorCode)));
324
325 Brrrk = true;
326 #ifdef _MSC_VER
327 break; // on windows the errors don't (always?) seem go away if you keep on eating them with glGetError().
328 #else
329 nErrors += 1;
330 if (nErrors > 4)
331 break;
332 #endif
333 }
334 // if (Brrrk) {
335 // // *(int*)0 = 0;
336 // // __builtin_trap();
337 // throw std::runtime_error("OpenGL broke :(.");
338 // }
339 (void)Brrrk;
340 }
341
342
343 // extern "C" {
344 // void glClearTexImage(GLuint texture, GLint level, GLenum format, GLenum type, const void * data);
345 // }
346 // ^- not there :(.
347
LoadTextFromFile(QString FileName)348 QString LoadTextFromFile(QString FileName)
349 {
350 QFile
351 File(FileName);
352 if (!File.open(QFile::ReadOnly | QFile::Text))
353 return "";
354 QTextStream TextStream(&File);
355 return TextStream.readAll();
356 }
357
358
359 enum FGlObjectType {
360 GLOBJECT_Shader,
361 GLOBJECT_Program
362 };
363
GlCheckShaderStatus(GLuint hObject,FGlObjectType ObjectType,QString FileName)364 GLuint GlCheckShaderStatus(GLuint hObject, FGlObjectType ObjectType, QString FileName)
365 {
366 // check shader compilation.
367 uint const nBuf = 0xffff;
368 char infobuffer[nBuf];
369 int infobufferlen = 0;
370 GLint CompileResult = 0;
371 if (ObjectType == GLOBJECT_Shader) {
372 CALL_GL( glGetShaderiv(hObject, GL_COMPILE_STATUS, &CompileResult) );
373 // ^- on the mac implementation glGetShaderInfoLog may return non-empty messages
374 // even if there was no error (including for things like performance warnings).
375 // So we check this here and only ask for the full result if it returns false.
376 // might otherwise confuse users
377 CALL_GL( glGetShaderInfoLog(hObject, nBuf-1, &infobufferlen, infobuffer) );
378 } else if (ObjectType == GLOBJECT_Program) {
379 CALL_GL( glGetProgramiv(hObject, GL_LINK_STATUS, &CompileResult) );
380 CALL_GL( glGetProgramInfoLog(hObject, nBuf-1, &infobufferlen, infobuffer) );
381 }
382 infobuffer[infobufferlen] = 0;
383 QString s(&infobuffer[0]);
384 if (CompileResult != GL_TRUE) {
385 IvNotify(NOTIFY_Error, "Failed to compile GLSL shader: " + QString("OpenGL says for file '%1': %2").arg(FileName, s));
386 return 0;
387 }
388 if (!s.isEmpty()) {
389 IvEmit(QString("INFO: OpenGL successfully compiled '%1', but raised complaints:\n%2").arg(FileName, s));
390 }
391
392 return hObject;
393 }
394
GlLoadShader(QString FileName,QString Prefix,int Type)395 GLuint GlLoadShader(QString FileName, QString Prefix, int Type)
396 {
397 GLenum
398 handle;
399 QString
400 SourceCode = LoadTextFromFile(FileName);
401 if ("" == SourceCode) {
402 IvNotify(NOTIFY_Error, "Failed to compile GLSL shader: " + QString("File '%1' could not be loaded").arg(FileName));
403 return 0;
404 }
405 if (Prefix != "")
406 SourceCode = Prefix + SourceCode;
407
408 if (g_WorkAroundGlFrontFacingBug && Type == GL_FRAGMENT_SHADER) {
409 SourceCode.replace("gl_FrontFacing", "true");
410 }
411
412 handle = glCreateShader(Type);
413 CheckGlError("GlLoadShader","glCreateShader");
414 QByteArray
415 SourceCodeAsAscii = SourceCode.toLocal8Bit();
416 char const *pSrc = SourceCodeAsAscii.data();
417
418 CALL_GL( glShaderSource(handle, 1, &pSrc, 0) );
419 CALL_GL( glCompileShader(handle) );
420
421 // check shader compilation.
422 return GlCheckShaderStatus(handle, GLOBJECT_Shader, FileName);
423 }
424
425
426 // see also: http://www.altdev.co/2011/06/23/improving-opengl-error-messages/
427 // http://virtrev.blogspot.de/2012/12/custom-opengl-context-with-qt.html
428 // however, I could not get the debug context running on QT4.
429 // maybe I can fake it with hijacking WGL_create_context via LD_PRELOAD and putting
430 // in a CONTEXT_DEBUG_BIT?
431 //
432 // Or I could try it with QT5: http://qt-project.org/doc/qt-5/qopengldebuglogger.html
433 // It is also ugly, but maybe does the trick.
434
435 #ifdef OPENGL_DEBUG
436 // it's a callback for OpenGL. Needs debug context support---which QT does not
437 // have by default (but which can be hacked in by hijacking glXCreateContextAttribsARB
438 // via LD_PRELOAD and sneaking in the GLX_CONTEXT_DEBUG_BIT_ARB flag.
439 // See ~/dev/gl_debug_context_preload/)
OutputGlDebugMessage(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,void *)440 void OutputGlDebugMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, void */*userParam*/)
441 {
442 std::string DebugMsg(message, length);
443
444 char const *pSource = "?", *pSeverity = "?", *pType = "?";
445 switch(source) {
446 case GL_DEBUG_SOURCE_API_ARB: pSource = "API"; break;
447 case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: pSource = "WINDOW_SYSTEM"; break;
448 case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: pSource = "SHADER_COMPILER"; break;
449 case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: pSource = "THIRD_PARTY"; break;
450 case GL_DEBUG_SOURCE_APPLICATION_ARB: pSource = "APPLICATION"; break;
451 case GL_DEBUG_SOURCE_OTHER_ARB: pSource = "OTHER"; break;
452 }
453 switch(type) {
454 case GL_DEBUG_TYPE_ERROR_ARB: pType = "ERROR"; break;
455 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: pType = "DEPRECATED_BEHAVIOR"; break;
456 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: pType = "UNDEFINED_BEHAVIOR"; break;
457 case GL_DEBUG_TYPE_PORTABILITY_ARB: pType = "PORTABILITY"; break;
458 case GL_DEBUG_TYPE_PERFORMANCE_ARB: pType = "PERFORMANCE"; break;
459 case GL_DEBUG_TYPE_OTHER_ARB: pType = "OTHER"; break;
460 }
461 switch(severity) {
462 case GL_DEBUG_SEVERITY_HIGH_ARB: pSeverity = "HIGH"; break;
463 case GL_DEBUG_SEVERITY_MEDIUM_ARB: pSeverity = "MEDIUM"; break;
464 case GL_DEBUG_SEVERITY_LOW_ARB: pSeverity = "LOW"; break;
465 }
466 if (severity != GL_DEBUG_SEVERITY_LOW_ARB)
467 IvEmit("GL DEBUG [%2/%3]: %1 (source: %4, id: %5)", DebugMsg, pType, pSeverity, pSource, id);
468 // if (severity != GL_DEBUG_SEVERITY_LOW_ARB)
469 // asm("int $3");
470
471 // __builtin_trap();
472 // ^- hmpf... there is one illegal op in QGLWidget itself. can't break like this.
473 }
474 #endif
475 // #endif // INCLUDE_OPTIONALS
476
477
478
Apply(FFrameBufferPtr pDest,FFrameBufferPtr pSrc,uint Width,uint Height,FFullScreenQuad & FullScreenQuad)479 void FImageFilter::Apply(FFrameBufferPtr pDest, FFrameBufferPtr pSrc, uint Width, uint Height, FFullScreenQuad &FullScreenQuad)
480 {
481 CheckGlError("FImageFilter::Apply (enter)");
482
483 // this thing is only here because GetUniformLocation does not work
484 // in draw mode.
485 CALL_GL( glUseProgram(Filter5HV.hProgram) );
486
487 assert(nFilterSize == sizeof(Kernel)/sizeof(Kernel[0]));
488 CALL_GL( glUniform1fv(IdFilterWeight, nFilterSize, Kernel) );
489 CALL_GL( glUniform1f(IdFilterStepH, 1./Width) );
490 CALL_GL( glUniform1f(IdFilterStepV, 1./Height) );
491
492 pSrc->Attachments[0]->BindAsTexture(GL_TEXTURE0);
493
494 // set input filters to bi-linear (defaults to nearest).
495 GLint MinFilter, MagFilter;
496 CALL_GL( glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, &MinFilter) );
497 CALL_GL( glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, &MagFilter) );
498 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) );
499 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) );
500
501 CALL_GL( glUniform1i(IdSourceImage, 0) );
502 pDest->Bind(GL_DRAW_FRAMEBUFFER);
503
504 CALL_GL( glDisable(GL_DEPTH_TEST) ); // FIXME: move to FFullScreenQuad::Draw()
505 FullScreenQuad.Draw(Filter5HV.hProgram);
506 CALL_GL( glEnable(GL_DEPTH_TEST) ); // FIXME: move to FFullScreenQuad::Draw()
507
508 // reset filters.
509 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, MinFilter) );
510 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, MagFilter) );
511
512 // unbind the frame buffer texture.
513 pSrc->Attachments[0]->UnbindAsTexture(GL_TEXTURE0);
514 CheckGlError("FImageFilter::Apply (leave)");
515 }
516
517
FShaderSet()518 FShaderSet::FShaderSet()
519 {
520 ZeroOutHandles();
521 }
522
FShaderSet(QString const & sVertexFile,QString const & sPixelFile,QString const & BasePath)523 FShaderSet::FShaderSet(QString const &sVertexFile, QString const &sPixelFile, QString const &BasePath)
524 {
525 ZeroOutHandles();
526 Create(sVertexFile, sPixelFile, BasePath);
527 }
528
ZeroOutHandles()529 void FShaderSet::ZeroOutHandles()
530 {
531 hProgram = 0;
532 hVertexShader = 0;
533 hPixelShader = 0;
534 hPixelCommon = 0;
535 }
536
Create(QString const & sVertexFile,QString const & sPixelFile,QString const & sBasePath)537 void FShaderSet::Create(QString const &sVertexFile, QString const &sPixelFile, QString const &sBasePath)
538 {
539 QDir
540 BasePath(sBasePath);
541 CheckGlError("FShaderSet::Create", "(enter)");
542 if (hProgram != 0)
543 Destroy();
544 ZeroOutHandles();
545 // static std::string BasePath = GetExePath() + "/";
546 // QString BasePath = ":/"; // <- embedded resource root.
547
548 hProgram = glCreateProgram();
549 CheckGlError("FShaderSet::Create", "CreateProgram");
550 #ifndef __APPLE__
551 QString
552 ShaderPrefix = "#version 400\n\n";
553 // ShaderPrefix = "#version 300 es\nprecision mediump float;\n\n";
554 // ^- we add this in software now... to make it work with Intel linux
555 // drivers, which can do a number of OpenGL 4.0 things, but only really
556 // accepts "3.0 es" as #version.
557 if (g_GlVersion < 40) {
558 ShaderPrefix = "#version 300 es\nprecision mediump float;\n\n";
559 }
560 #else
561 // Apple. This and #version 150 were the ONLY ones it accepted.
562 // Nothing else (no 140, no 130, no 300, no 330, no 300 es, ...).
563 // ...In a OpenGL 3.2 core context.
564 QString
565 ShaderPrefix = "#version 400\n\n";
566 #endif
567 hVertexShader = GlLoadShader(BasePath.filePath(sVertexFile), ShaderPrefix, GL_VERTEX_SHADER);
568 hPixelShader = GlLoadShader(BasePath.filePath(sPixelFile), ShaderPrefix, GL_FRAGMENT_SHADER);
569 hPixelCommon = GlLoadShader(BasePath.filePath(QString("pixel_common.glsl")), ShaderPrefix, GL_FRAGMENT_SHADER);
570
571 CALL_GL( glAttachShader(hProgram, hVertexShader) );
572 CALL_GL( glAttachShader(hProgram, hPixelShader) );
573 CALL_GL( glAttachShader(hProgram, hPixelCommon) );
574 CALL_GL( glLinkProgram(hProgram) );
575
576 GlCheckShaderStatus(hProgram, GLOBJECT_Program, QString("LINK [%1 // %2]").arg(sVertexFile, sPixelFile));
577
578 // query and store some uniform locations which most shaders have
579 for (uint i = 0; i < sizeof(hMatrix)/sizeof(hMatrix[0]); ++ i)
580 hMatrix[i] = -1;
581 hMatrix[TRANSFORM_ModelView] = glGetUniformLocation(hProgram, "mView");
582 hMatrix[TRANSFORM_Projection] = glGetUniformLocation(hProgram, "mProj");
583 hMatrix[TRANSFORM_Normals] = glGetUniformLocation(hProgram, "mNorm");
584 hDiffuseColor = glGetUniformLocation(hProgram, "DiffuseColor");
585 hObjectId = glGetUniformLocation(hProgram, "ObjectId");
586
587 CheckGlError("FShaderSet::Create", "(exit)");
588 }
589
590
Destroy()591 void FShaderSet::Destroy()
592 {
593 CALL_GL( glUseProgram(0) );
594 CALL_GL( glDeleteProgram(hProgram) );
595 CALL_GL( glDeleteShader(hVertexShader) );
596 CALL_GL( glDeleteShader(hPixelShader) );
597 CALL_GL( glDeleteShader(hPixelCommon) );
598 // note: deleting 0 objects is fine. Result is ignored.
599
600 ZeroOutHandles();
601 }
602
~FShaderSet()603 FShaderSet::~FShaderSet()
604 {
605 Destroy();
606 }
607
SetGlNormalTransformationMatrix(GLint hTrafoNorm,FMat4f const & mView)608 void SetGlNormalTransformationMatrix(GLint hTrafoNorm, FMat4f const &mView)
609 {
610 // make and set the transformation for normal vectors. may be non-trivial if the
611 // main transformation is not unitary (and we use non-unitary trafos to
612 // place atoms and bonds).
613 FMat3f
614 mNorm1;
615 for (uint i = 0; i != 3; ++ i)
616 for (uint j = 0; j != 3; ++ j)
617 mNorm1(i,j) = mView(i,j);
618 mNorm1 = vmath::transpose(vmath::inverse(mNorm1));
619 CALL_GL( glUniformMatrix3fv(hTrafoNorm, 1, GL_FALSE, &mNorm1(0,0)) );
620 }
621
Actualize(FShaderSet & ShaderSet)622 void FGlMatrixStack::Actualize(FShaderSet &ShaderSet)
623 {
624 GLint
625 hMatrix = ShaderSet.hMatrix[this->DefaultRole];
626 if (hMatrix == -1)
627 throw std::runtime_error("FGlMatrixStack::Actualize: Current shader set does not have a matrix of the current role.");
628
629 CALL_GL( glUniformMatrix4fv(hMatrix, 1, GL_FALSE, &Top()(0,0)) );
630 // ^- false: do not transpose (openGL expects column major order, i.e., row stride = 1, column stride = nrows).
631 // that is what I use, too.
632 CheckGlError("FGlMatrixStack::Actualize", "set matrices.");
633 if (DefaultRole == TRANSFORM_ModelView)
634 SetGlNormalTransformationMatrix(ShaderSet.hMatrix[TRANSFORM_Normals], Top());
635 }
636
Translate(FVec3f const & v)637 void FGlMatrixStack::Translate(FVec3f const &v)
638 {
639 // FMat4f
640 // Dummy = vmath::identity4<float>();
641 // for (uint i = 0; i < 3; ++ i)
642 // Dummy(i,3) = v[i];
643 // Multiply(Dummy);
644 float
645 fRowDisp[3] = {0, 0, 0};
646 for (uint iRow = 0; iRow != 3; ++ iRow)
647 for (uint iComp = 0; iComp != 3; ++ iComp)
648 fRowDisp[iRow] += v[iComp] * Top()(iRow, iComp);
649 for (uint iRow = 0; iRow != 3; ++ iRow)
650 Top()(iRow,3) += fRowDisp[iRow];
651 }
652
Scale(FVec3f const & s)653 void FGlMatrixStack::Scale(FVec3f const &s)
654 {
655 // Top = Scale * Top
656 // where
657 // ( s[0] )
658 // Scale = ( s[1] )
659 // ( s[2] )
660 // ( 1. )
661 for (uint iDir = 0; iDir < 3; ++ iDir)
662 for (uint iComp = 0; iComp < 4; ++ iComp)
663 Top()(iComp,iDir) *= s[iDir];
664 }
665
SetIdentity()666 void FGlMatrixStack::SetIdentity()
667 {
668 memset(&Top()(0,0), 0, sizeof(FMat4f));
669 for (uint i = 0; i < 4; ++ i)
670 Top()(i,i) = 1.f;
671 }
672
Clear()673 void FGlMatrixStack::Clear()
674 {
675 FBase::resize(1);
676 SetIdentity();
677 }
678
Set(FMat4f const & m)679 void FGlMatrixStack::Set(FMat4f const &m)
680 {
681 Top() = m;
682 }
683
Multiply(FMat4f const & m)684 void FGlMatrixStack::Multiply(FMat4f const &m)
685 {
686 Top() = m * Top();
687 }
688
689
690
691
692 #include "fn_LiberationSans.h"
693
694 template<class FVertex>
TGlMesh(TIndexedTriangleList<FVertex> const & List,GLenum UsageType_)695 TGlMesh<FVertex>::TGlMesh(TIndexedTriangleList<FVertex> const &List, GLenum UsageType_)
696 {
697 m_UsageType = UsageType_;
698 Vertices = List.Vertices;
699 Triangles = List.Triangles;
700 CreateGlBindings();
701 }
702
703 template<class FVertex>
TGlMesh(GLenum UsageType_)704 TGlMesh<FVertex>::TGlMesh(GLenum UsageType_)
705 {
706 m_UsageType = UsageType_;
707 }
708
709
710 template<class FVertex>
~TGlMesh()711 TGlMesh<FVertex>::~TGlMesh()
712 {
713 DestroyGlBindings();
714 }
715
716 template<class FVertex>
Invalidate()717 void TGlMesh<FVertex>::Invalidate()
718 {
719 DestroyGlBindings();
720 CreateGlBindings(true); // rebuild interfaces and upload data
721 }
722
723
AssignVertexAttributes()724 void FBaseVertex::AssignVertexAttributes()
725 {
726 FBaseVertex v;
727
728 if (1) {
729 // GL4? required?
730 for (uint i = 0; i < 4; ++ i ) {
731 CALL_GL( glEnableVertexAttribArray(i) );
732 }
733 CALL_GL( glDisableVertexAttribArray(1) );
734
735 // position
736 CALL_GL( glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(v), (void*)((char*)&v.vPos - (char*)&v)) );
737 // normal
738 CALL_GL( glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(v), (void*)((char*)&v.vNorm - (char*)&v)) );
739 // color (normalize: convert 0..255 -> 0...1)
740 CALL_GL( glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(v), (void*)((char*)&v.dwColor - (char*)&v)) );
741 } else {
742 // this worked in GL3
743 glVertexPointer(3, GL_FLOAT, sizeof(v), (void*)((char*)&v.vPos - (char*)&v));
744 glNormalPointer(GL_FLOAT, sizeof(v), (void*)((char*)&v.vNorm - (char*)&v));
745 // glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(v), (void*)((char*)&v.vNorm - (char*)&v));
746 glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(v), (void*)((char*)&v.dwColor - (char*)&v));
747 glEnableClientState(GL_VERTEX_ARRAY);
748 glEnableClientState(GL_NORMAL_ARRAY);
749 glEnableClientState(GL_COLOR_ARRAY);
750 }
751 }
752
AssignVertexAttributes()753 void FTextVertex::AssignVertexAttributes()
754 {
755 FTextVertex v;
756
757 // GL4? required?
758 for (uint i = 0; i < 4; ++ i ) {
759 CALL_GL( glEnableVertexAttribArray(i) );
760 }
761 // glDisableVertexAttribArray(2);
762
763 // position (3d)
764 CALL_GL( glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(v), (void*)((char*)&v.vPos - (char*)&v)) );
765 // position (2d displacement)... not sure if stupid to do it like this.
766 CALL_GL( glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(v), (void*)((char*)&v.vCoord2d - (char*)&v)) );
767 // texture coordinate
768 CALL_GL( glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(v), (void*)((char*)&v.vTex - (char*)&v)) );
769 // color (normalize: convert 0..255 -> 0...1)
770 CALL_GL( glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(v), (void*)((char*)&v.dwColor - (char*)&v)) );
771 }
772
773
774 template<class FVertex>
CreateGlBindings(bool UploadData)775 void TGlMesh<FVertex>::CreateGlBindings(bool UploadData)
776 {
777 CALL_GL( glGenVertexArrays(1, &m_hVertexArrayObject) ); // <- that's a descriptor of vertex data.
778 CALL_GL( glBindVertexArray(m_hVertexArrayObject) );
779 // ^- defines layout of vertex buffers, effectively. also stores
780 // which buffers were bound to what.
781 CheckGlError("FGlMesh::CreateGlBindings", "create vertex descriptor");
782
783 FVertex
784 *pVertices = 0;
785 vec3ui
786 *pTriangles = 0;
787 if (UploadData) {
788 m_MaxTriangles = Triangles.size();
789 m_MaxVertices = Vertices.size();
790 pTriangles = &Triangles[0];
791 pVertices = &Vertices[0];
792 }
793
794 CALL_GL( glGenBuffers(1, &m_hVertexBuffer) );
795 CALL_GL( glBindBuffer(GL_ARRAY_BUFFER, m_hVertexBuffer) );
796 CALL_GL( glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices[0])*m_MaxVertices, pVertices, m_UsageType) );
797 CheckGlError("FGlMesh::CreateGlBindings", "create vertex buffer");
798
799 // setup the vertex descriptor (didn't work on some machines if done before the GL_ARRAY_BUFFER stuff)
800 // note: only allowed if a "vertex array object" is bound (glBindVertexArray)
801 FVertex::AssignVertexAttributes();
802 CheckGlError("FGlMesh::CreateGlBindings", "setup vertex descriptor");
803
804 CALL_GL( glBindVertexArray(0) );
805 // ^- hm.. why was this here? doesn't this un-associate the vertex descriptor with the vertex array object?
806 CALL_GL( glGenBuffers(1, &m_hIndexBuffer) );
807 CALL_GL( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_hIndexBuffer) );
808 CALL_GL( glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Triangles[0]) * m_MaxTriangles, pTriangles, m_UsageType) );
809
810 CheckGlError("FGlMesh::CreateGlBindings", "create index buffer");
811 }
812
813 template<class FVertex>
UploadData()814 void TGlMesh<FVertex>::UploadData()
815 {
816 if (Triangles.size() > m_MaxTriangles || Vertices.size() > m_MaxVertices) {
817 IvNotify(NOTIFY_Error, "Ran out of buffer space in TGlMesh<FVertex>::UploadData().");
818 return;
819 // alternatively I could invalidate and re-upload. But I think this cannot
820 // be done in all render states, can it?
821 }
822 CALL_GL( glBindVertexArray(m_hVertexArrayObject) );
823 // ^- I think this binds the other arrays automagically (hm.. or maybe it doesn't. crashes if I remove the
824 // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,...) in the Draw() routine.
825 CALL_GL( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_hIndexBuffer) );
826 CALL_GL( glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(Triangles[0]) * Triangles.size(), &Triangles[0]) );
827 CALL_GL( glBindBuffer(GL_ARRAY_BUFFER, m_hVertexBuffer) );
828 CALL_GL( glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertices[0])*Vertices.size(), &Vertices[0]) );
829 }
830
831 template<class FVertex>
DestroyGlBindings()832 void TGlMesh<FVertex>::DestroyGlBindings()
833 {
834 CheckGlError("TGlMesh<FVertex>::DestroyGlBindings() (enter)");
835 CALL_GL( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) );
836 CALL_GL( glDeleteBuffers(1, &m_hIndexBuffer) );
837 m_hIndexBuffer = 0;
838
839 CALL_GL( glBindBuffer(GL_ARRAY_BUFFER, 0) );
840 CALL_GL( glDeleteBuffers(1, &m_hVertexBuffer) );
841 m_hVertexBuffer = 0;
842
843 // for (uint i = 0; i < 4; ++ i )
844 // glDisableVertexAttribArray(i);
845 // ^- may not be allowed unless the vertex array object is bound. also, doesn't actually do anything here.
846
847 CALL_GL( glBindVertexArray(0) );
848 CALL_GL( glDeleteVertexArrays(1, &m_hVertexArrayObject) );
849 m_hVertexArrayObject = 0;
850 CheckGlError("TGlMesh<FVertex>::DestroyGlBindings() (leave)");
851 }
852
853 template<class FVertex>
Draw()854 void TGlMesh<FVertex>::Draw()
855 {
856 CheckGlError("FGlMesh::Draw", "(enter)");
857 // glBindVertexArray(hVertexArrayObject); // defines layout of vertex buffers, effectively.
858 // glBindBuffer(GL_ARRAY_BUFFER, hVertexBuffer);
859 // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, hIndexBuffer);
860 // for (uint i = 0; i < 3; ++ i )
861 // glEnableVertexAttribArray(i);
862
863 CALL_GL( glBindVertexArray(m_hVertexArrayObject) );
864 CALL_GL( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_hIndexBuffer) );
865
866 // std::cout << "Triangles.size: " << Triangles.size() << std::endl;
867 CALL_GL( glDrawElements(GL_TRIANGLES, 3*Triangles.size(), GL_UNSIGNED_INT, (GLvoid*)0) );
868 // ^- why * 3? Do I need to specify the number of indices instead
869 // of the number of primitives? The way the docs are phrased suggests 'no'.
870
871 CALL_GL( glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0) );
872 CALL_GL( glBindVertexArray(0) );
873 // glBindBuffer(GL_ARRAY_BUFFER, 0);
874 CheckGlError("FGlMesh::Draw", "(leave)");
875 }
876
877 template struct TGlMesh<FBaseVertex>;
878
879
FGlTextBuffer(size_t MaxCharacters)880 FGlTextBuffer::FGlTextBuffer(size_t MaxCharacters)
881 // : FBase(GL_STREAM_DRAW)
882 : FBase(GL_DYNAMIC_DRAW)
883 {
884 m_pFont = &embedded_fonts::efont_sans;
885 m_Dirty = true;
886 m_MaxVertices = 4 * MaxCharacters;
887 m_MaxTriangles = 2 * MaxCharacters;
888 Triangles.reserve(m_MaxTriangles);
889 Vertices.reserve(m_MaxVertices);
890 CreateGlBindings(false); // make buffers, but don't upload any data.
891 }
892
~FGlTextBuffer()893 FGlTextBuffer::~FGlTextBuffer()
894 {
895 }
896
Clear()897 void FGlTextBuffer::Clear()
898 {
899 Triangles.clear();
900 Vertices.clear();
901 m_Dirty = true;
902 }
903
904
Print(vec3f vPos,float Scale,uint32_t dwColor,wchar_t const * pText,uint Flags)905 void FGlTextBuffer::Print(vec3f vPos, float Scale, uint32_t dwColor, wchar_t const *pText, uint Flags)
906 {
907 size_t
908 iFirstVertex = Vertices.size();
909 wchar_t
910 // last emitted character (for building kerning pairs)
911 LastChar = 0;
912 vec3f
913 // position of text. x,y in units of pixels. Will be fixed up and moved
914 // to vPos later.
915 vCur(0.f,0.f,0.f);
916 // float
917 // fMaxHeight = 0.;
918 size_t
919 nChars = wcslen(pText);
920 if (2*nChars + Triangles.size() > m_MaxTriangles) {
921 IvNotify(NOTIFY_Warning, "Text buffer too small. Some text will not be rendered (FGlTextBuffer::Print).");
922 return;
923 }
924 for (size_t i = 0; i < nChars; ++ i)
925 {
926 embedded_fonts::texture_glyph_t
927 *pGlyph = 0;
928 for (size_t j = 0; j < m_pFont->glyphs_count; ++ j)
929 if (m_pFont->glyphs[j].charcode == pText[i]) {
930 pGlyph = &m_pFont->glyphs[j];
931 break;
932 }
933 if (!pGlyph)
934 continue;
935
936 float
937 kerning = 0.f;
938 for (size_t ikern = 0; ikern < pGlyph->kerning_count; ++ ikern)
939 if (pGlyph->kerning[ikern].charcode == LastChar)
940 kerning = pGlyph->kerning[ikern].kerning;
941 vCur[0] += kerning;
942
943 float
944 x = vCur[0] + (float)pGlyph->offset_x,
945 y = vCur[1] + (float)pGlyph->offset_y,
946 w = (float)pGlyph->width,
947 h = (float)pGlyph->height;
948 size_t iVert = Vertices.size();
949 // Vertices.push_back(FTextVertex(vec3f(x, y, vPos.z), vec2f(pGlyph->s0, pGlyph->t0), dwColor));
950 // Vertices.push_back(FTextVertex(vec3f(x, y-h,vPos.z), vec2f(pGlyph->s0, pGlyph->t1), dwColor));
951 // Vertices.push_back(FTextVertex(vec3f(x+w,y-h,vPos.z), vec2f(pGlyph->s1, pGlyph->t1), dwColor));
952 // Vertices.push_back(FTextVertex(vec3f(x+w,y, vPos.z), vec2f(pGlyph->s1, pGlyph->t0), dwColor));
953
954 Vertices.push_back(FTextVertex(vPos, vec2f(x, y), vec2f(pGlyph->s0, pGlyph->t0), dwColor));
955 Vertices.push_back(FTextVertex(vPos, vec2f(x, y-h), vec2f(pGlyph->s0, pGlyph->t1), dwColor));
956 Vertices.push_back(FTextVertex(vPos, vec2f(x+w, y-h), vec2f(pGlyph->s1, pGlyph->t1), dwColor));
957 Vertices.push_back(FTextVertex(vPos, vec2f(x+w, y), vec2f(pGlyph->s1, pGlyph->t0), dwColor));
958
959 // Vertices.push_back(FTextVertex(vPos, vec2f(x, y), vec2f(0., 0.), dwColor));
960 // Vertices.push_back(FTextVertex(vPos, vec2f(x, y-h), vec2f(0., 1.), dwColor));
961 // Vertices.push_back(FTextVertex(vPos, vec2f(x+w, y-h), vec2f(1., 1.), dwColor));
962 // Vertices.push_back(FTextVertex(vPos, vec2f(x+w, y), vec2f(1., 0.), dwColor));
963 Triangles.push_back(vec3ui(iVert+0, iVert+1, iVert+2));
964 Triangles.push_back(vec3ui(iVert+0, iVert+2, iVert+3));
965 // fMaxHeight = std::max(fMaxHeight, h);
966
967 vCur[0] += pGlyph->advance_x;
968 vCur[1] += pGlyph->advance_y;
969
970 LastChar = pText[i];
971 }
972
973 // apply scaling and transformation to x and y positions of glyphs.
974 vec2f
975 vOff(0., 0.);
976 if ((Flags & HALIGN_Mask) == HALIGN_Center)
977 vOff[0] -= vCur[0]/2;
978 if ((Flags & HALIGN_Mask) == HALIGN_Right)
979 vOff[0] -= vCur[0];
980 if ((Flags & VALIGN_Mask) == VALIGN_Center)
981 vOff[1] -= (m_pFont->ascender + m_pFont->descender)/2;
982 if ((Flags & VALIGN_Mask) == VALIGN_Top)
983 vOff[1] -= (m_pFont->ascender + m_pFont->descender);
984
985 for (size_t iVert = iFirstVertex; iVert != Vertices.size(); ++ iVert) {
986 // vec3f &v = Vertices[iVert].vPos;
987 // v = (v + vOff)*(Scale/m_pFont->size);
988 vec2f &v = Vertices[iVert].vCoord2d;
989 v = (v + vOff)*(Scale/m_pFont->size);
990 // std::cout << format("text v2f: (%8.3f, %8.3f)\n") % v[0] % v[1];
991 }
992
993 m_Dirty = true;
994 }
995
DrawText1(FGlMatrixStack * pView,FGlMatrixStack * pProj)996 void FGlTextBuffer::DrawText1(FGlMatrixStack *pView, FGlMatrixStack *pProj)
997 {
998 if (m_Dirty) {
999 UploadData();
1000 m_Dirty = false;
1001 }
1002 CALL_GL( glUseProgram(pShader->hProgram) );
1003 CALL_GL( glUniform1i(pShader->GetUniformLocation("FontTexture" ), 0) );
1004 CALL_GL( glActiveTexture(GL_TEXTURE0) );
1005 CALL_GL( glBindTexture(GL_TEXTURE_2D, m_hFontTexture) );
1006
1007 if (pView)
1008 pView->Actualize(*pShader);
1009 if (pProj)
1010 pProj->Actualize(*pShader);
1011
1012 // {
1013 // FGlMatrixStack mIdProj(TRANSFORM_Projection);
1014 // FGlMatrixStack mIdView(TRANSFORM_ModelView);
1015 // mIdProj.SetIdentity();
1016 // mIdView.SetIdentity();
1017 // CALL_GL( glUniformMatrix4fv(pShader->hMatrix[TRANSFORM_ModelView], 1, GL_FALSE, &mIdView.Top()(0,0)) );
1018 // CALL_GL( glUniformMatrix4fv(pShader->hMatrix[TRANSFORM_Projection], 1, GL_FALSE, &mIdProj.Top()(0,0)) );
1019 // }
1020 CALL_GL( glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST) );
1021
1022 // UploadData(); // FIXME: should this be here?
1023 // std::cout << format("drawing %i triangles and %i vertices via text buffer.\n") % Triangles.size() % Vertices.size();
1024 FBase::Draw();
1025
1026 CALL_GL( glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_DONT_CARE) );
1027 }
1028
Invalidate()1029 void FGlTextBuffer::Invalidate()
1030 {
1031 FBase::Invalidate();
1032 m_Dirty = false;
1033 }
1034
CreateGlBindings(bool UploadData)1035 void FGlTextBuffer::CreateGlBindings(bool UploadData)
1036 {
1037 FBase::CreateGlBindings(UploadData);
1038 // create texture object.
1039
1040 CALL_GL( glGenTextures(1, &m_hFontTexture) );
1041 CALL_GL( glActiveTexture(GL_TEXTURE0) );
1042 CALL_GL( glBindTexture(GL_TEXTURE_2D, m_hFontTexture) );
1043 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) );
1044 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) );
1045 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) );
1046 // CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST) );
1047 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) );
1048 CALL_GL( glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 9) );
1049
1050 CALL_GL( glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, m_pFont->tex_width, m_pFont->tex_height,
1051 0, GL_RED, GL_UNSIGNED_BYTE, &m_pFont->tex_data[0] ) );
1052 // CALL_GL( glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST) );
1053 // ^- apparently not allowed in the core profile. There doesn't seem to be another way
1054 // of controlling this, either.
1055 CALL_GL( glGenerateMipmap(GL_TEXTURE_2D) );
1056
1057
1058 pShader = new FShaderSet("vertex_text.glsl", "pixel_text.glsl");
1059 }
1060
DestroyGlBindings()1061 void FGlTextBuffer::DestroyGlBindings()
1062 {
1063 pShader = 0; // should destroy objects automatically.
1064 FBase::DestroyGlBindings();
1065 }
1066
UploadData()1067 void FGlTextBuffer::UploadData()
1068 {
1069 FBase::UploadData();
1070 }
1071