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