1
/***********************************************************************
3
* Copyright (C) 2011 Graeme Gott <graeme@gottcode.org>
5
* This program 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, either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
***********************************************************************/
20
#include "graphics_layer.h"
22
#include "appearance_dialog.h"
25
#include <QGLShaderProgram>
28
//-----------------------------------------------------------------------------
30
GraphicsLayer* graphics_layer = 0;
32
//-----------------------------------------------------------------------------
38
#define APIENTRYP APIENTRY *
41
// Multi-texture extension
42
#ifndef GL_VERSION_1_3
43
#define GL_TEXTURE0 0x84C0
44
#define GL_TEXTURE1 0x84C1
47
#ifndef GL_VERSION_1_3_DEPRECATED
48
#define GL_COMBINE 0x8570
49
#define GL_COMBINE_RGB 0x8571
50
#define GL_ADD_SIGNED 0x8574
53
typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
54
static PFNGLACTIVETEXTUREPROC activeTexture = 0;
55
typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture);
56
static PFNGLCLIENTACTIVETEXTUREPROC clientActiveTexture = 0;
58
// Vertex buffer object extension
59
#ifndef GL_VERSION_1_5
60
#define GL_ARRAY_BUFFER 0x8892
61
#define GL_DYNAMIC_DRAW 0x88E8
62
typedef ptrdiff_t GLintptr;
63
typedef ptrdiff_t GLsizeiptr;
66
static GLuint vbo_id = 0;
68
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
69
static PFNGLBINDBUFFERPROC bindBuffer = 0;
70
typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
71
static PFNGLBUFFERDATAPROC bufferData = 0;
72
typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data);
73
static PFNGLBUFFERSUBDATAPROC bufferSubData = 0;
74
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
75
static PFNGLDELETEBUFFERSPROC deleteBuffers = 0;
76
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
77
static PFNGLGENBUFFERSPROC genBuffers = 0;
79
// Vertex attribute object extension
80
static GLuint vao_id = 0;
82
typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array);
83
static PFNGLBINDVERTEXARRAYPROC bindVertexArray = 0;
84
typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);
85
static PFNGLDELETEVERTEXARRAYSPROC deleteVertexArrays = 0;
86
typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
87
static PFNGLGENVERTEXARRAYSPROC genVertexArrays = 0;
93
MultiTextureFlag = 0x01,
94
VertexBufferObjectFlag = 0x02,
95
FragmentShadersFlag = 0x04,
96
VertexArrayObjectFlag = 0x08
102
Version13 = MultiTextureFlag,
103
Version15 = MultiTextureFlag | VertexBufferObjectFlag,
104
Version21 = FragmentShadersFlag | MultiTextureFlag | VertexBufferObjectFlag,
105
Version30 = VertexArrayObjectFlag | FragmentShadersFlag | MultiTextureFlag | VertexBufferObjectFlag
109
static void* getProcAddress(const QString& name)
112
QString names[] = { name, name + "ARB", name + "EXT" };
113
for (int i = 0; i < 3; ++i) {
114
result = QGLContext::currentContext()->getProcAddress(names[i]);
122
static inline void convertMatrix(const qreal* in, GLfloat* out)
124
for (int i = 0; i < 16; ++i) {
125
out[i] = static_cast<GLfloat>(in[i]);
129
//-----------------------------------------------------------------------------
131
void GraphicsLayer::init()
133
unsigned int state = 0;
136
// Try to load multi-texture functions
137
activeTexture = (PFNGLACTIVETEXTUREPROC) getProcAddress("glActiveTexture");
138
clientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC) getProcAddress("glClientActiveTexture");
139
if ((activeTexture != 0) && (clientActiveTexture != 0)) {
140
state |= MultiTextureFlag;
144
// Try to load vertex buffer object functions
145
bindBuffer = (PFNGLBINDBUFFERPROC) getProcAddress("glBindBuffer");
146
bufferData = (PFNGLBUFFERDATAPROC) getProcAddress("glBufferData");
147
bufferSubData = (PFNGLBUFFERSUBDATAPROC) getProcAddress("glBufferSubData");
148
deleteBuffers = (PFNGLDELETEBUFFERSPROC) getProcAddress("glDeleteBuffers");
149
genBuffers = (PFNGLGENBUFFERSPROC) getProcAddress("glGenBuffers");
150
if ((bindBuffer != 0) && (bufferData != 0) && (bufferSubData != 0) && (deleteBuffers != 0) && (genBuffers != 0)) {
151
state |= VertexBufferObjectFlag;
152
if (state == Version15) {
157
// Check for minimum supported programmable pipeline
158
if (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_1) {
159
state |= FragmentShadersFlag;
160
if (activeTexture != 0) {
161
state |= MultiTextureFlag;
165
// Try to load vertex array object functions
166
if (state == Version21) {
168
bindVertexArray = (PFNGLBINDVERTEXARRAYPROC) getProcAddress("glBindVertexArray");
169
deleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) getProcAddress("glDeleteVertexArrays");
170
genVertexArrays = (PFNGLGENVERTEXARRAYSPROC) getProcAddress("glGenVertexArrays");
171
if ((bindVertexArray != 0) && (deleteVertexArrays != 0) && (genVertexArrays != 0)) {
172
state |= VertexArrayObjectFlag;
177
// Check for a requested state
178
unsigned int new_state = 0;
179
int requested = QSettings().value("GraphicsLayer", detected).toInt();
180
if (requested == 30) {
181
new_state = Version30;
182
} else if (requested == 21) {
183
new_state = Version21;
184
} else if (requested == 15) {
185
new_state = Version15;
186
} else if (requested == 13) {
187
new_state = Version13;
188
} else if (requested == 11) {
189
new_state = Version11;
192
qWarning("Requested GraphicsLayer%d is invalid; using detected GraphicsLayer%d instead.", requested, detected);
194
if (state >= new_state) {
197
qWarning("Unable to use requested GraphicsLayer%d; using detected GraphicsLayer%d instead.", requested, detected);
200
// Create graphics layer instance
201
if (state == Version30) {
202
genVertexArrays(1, &vao_id);
203
bindVertexArray(vao_id);
204
graphics_layer = new GraphicsLayer21;
205
} else if (state == Version21) {
206
graphics_layer = new GraphicsLayer21;
207
} else if (state == Version15) {
208
graphics_layer = new GraphicsLayer15;
209
} else if (state == Version13) {
210
graphics_layer = new GraphicsLayer13;
212
graphics_layer = new GraphicsLayer11;
214
graphics_layer->setTextureUnits(1);
217
//-----------------------------------------------------------------------------
219
GraphicsLayer::GraphicsLayer()
224
// Enable OpenGL features
225
glEnable(GL_CULL_FACE);
226
glEnable(GL_DEPTH_TEST);
228
// Set OpenGL parameters
229
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
230
glDepthFunc(GL_LEQUAL);
233
// Start with a 1MB vertex buffer
234
m_data.resize(0x100000 / sizeof(Vertex));
235
VertexArray free_region;
236
free_region.end = m_data.count();
237
m_free_regions.append(free_region);
240
//-----------------------------------------------------------------------------
242
GraphicsLayer::~GraphicsLayer()
246
bindBuffer(GL_ARRAY_BUFFER, 0);
247
deleteBuffers(1, &vbo_id);
254
deleteVertexArrays(1, &vao_id);
259
//-----------------------------------------------------------------------------
261
void GraphicsLayer::updateArray(VertexArray& array, const QVector<Vertex>& data)
263
int length = data.count();
264
if (array.length() != length) {
267
for (int i = 0; i < m_free_regions.count(); ++i) {
268
VertexArray& free_region = m_free_regions[i];
269
if (free_region.length() == length) {
270
array.start = free_region.start;
271
array.end = array.start + length;
272
m_free_regions.removeAt(i);
274
} else if (free_region.length() > length) {
275
array.start = free_region.start;
276
array.end = array.start + length;
277
free_region.start += length;
283
if (array.start != -1) {
284
qCopy(data.begin(), data.end(), m_data.begin() + array.start);
286
m_changed_regions.append(array);
289
array.start = m_data.count();
290
array.end = array.start + length;
293
m_changed_regions.clear();
297
//-----------------------------------------------------------------------------
299
void GraphicsLayer::removeArray(VertexArray& array)
301
if (array.end == 0) {
306
int count = m_free_regions.count();
308
for (int i = 0; i < count; ++i) {
309
VertexArray& free_region = m_free_regions[i];
310
if (free_region.end == array.start) {
312
free_region.end = array.end;
313
if ((i+1) < count && free_region.end == m_free_regions[i+1].start) {
314
free_region.end = m_free_regions[i+1].end;
315
m_free_regions.removeAt(i+1);
318
} else if (free_region.start == array.end) {
320
free_region.start = array.start;
321
if (i > 0 && free_region.start == m_free_regions[i-1].end) {
322
free_region.start = m_free_regions[i-1].start;
323
m_free_regions.removeAt(i-1);
326
} else if (free_region.start > array.start) {
332
m_free_regions.insert(pos, array);
335
array.start = array.end = 0;
338
//-----------------------------------------------------------------------------
340
void GraphicsLayer::clearChanged()
342
m_changed_regions.clear();
346
//-----------------------------------------------------------------------------
348
void GraphicsLayer::uploadChanged()
350
if (!m_changed_regions.isEmpty()) {
351
foreach (const VertexArray& region, m_changed_regions) {
352
bufferSubData(GL_ARRAY_BUFFER, region.start * sizeof(Vertex), region.length() * sizeof(Vertex), m_data.constBegin() + region.start);
354
m_changed_regions.clear();
355
} else if (m_changed) {
356
GLsizeiptr size = m_data.count() * sizeof(Vertex);
357
bufferData(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW);
358
bufferSubData(GL_ARRAY_BUFFER, 0, size, m_data.constData());
363
//-----------------------------------------------------------------------------
365
GraphicsLayer21::GraphicsLayer21()
368
AppearanceDialog::setBevelsEnabled(true);
371
genBuffers(1, &vbo_id);
372
bindBuffer(GL_ARRAY_BUFFER, vbo_id);
375
QGLShaderProgram* program = loadProgram(0);
376
program->setAttributeBuffer(Position, GL_FLOAT, 0, 3, sizeof(Vertex));
377
program->enableAttributeArray(Position);
379
program = loadProgram(1);
380
program->setAttributeBuffer(TexCoord0, GL_FLOAT, sizeof(GLfloat) * 3, 2, sizeof(Vertex));
381
program->setAttributeBuffer(Position, GL_FLOAT, 0, 3, sizeof(Vertex));
382
program->enableAttributeArray(Position);
383
program->setUniformValue("texture0", GLuint(0));
385
program = loadProgram(2);
386
program->setAttributeBuffer(TexCoord1, GL_FLOAT, sizeof(GLfloat) * 5, 2, sizeof(Vertex));
387
program->setAttributeBuffer(TexCoord0, GL_FLOAT, sizeof(GLfloat) * 3, 2, sizeof(Vertex));
388
program->setAttributeBuffer(Position, GL_FLOAT, 0, 3, sizeof(Vertex));
389
program->enableAttributeArray(Position);
390
program->setUniformValue("texture0", GLuint(0));
391
program->setUniformValue("texture1", GLuint(1));
394
//-----------------------------------------------------------------------------
396
GraphicsLayer21::~GraphicsLayer21()
399
for (int i = 0; i < 3; ++i) {
400
delete m_programs[i];
406
//-----------------------------------------------------------------------------
408
void GraphicsLayer21::bindTexture(unsigned int unit, GLuint texture)
410
activeTexture(GL_TEXTURE0 + unit);
411
glBindTexture(GL_TEXTURE_2D, texture);
412
activeTexture(GL_TEXTURE0);
415
//-----------------------------------------------------------------------------
417
void GraphicsLayer21::draw(const VertexArray& array, GLenum mode)
419
glDrawArrays(mode, array.start, array.length());
422
//-----------------------------------------------------------------------------
424
void GraphicsLayer21::setBlended(bool enabled)
433
//-----------------------------------------------------------------------------
435
void GraphicsLayer21::setColor(const QColor& color)
437
m_program->setUniformValue(m_color_location, color);
440
//-----------------------------------------------------------------------------
442
void GraphicsLayer21::setModelview(const QMatrix4x4& matrix)
444
m_modelview = matrix;
445
convertMatrix((m_projection * m_modelview).constData(), m_matrix[0]);
446
m_program->setUniformValue(m_matrix_location, m_matrix);
449
//-----------------------------------------------------------------------------
451
void GraphicsLayer21::setProjection(const QMatrix4x4& matrix)
453
m_projection = matrix;
454
convertMatrix((m_projection * m_modelview).constData(), m_matrix[0]);
455
m_program->setUniformValue(m_matrix_location, m_matrix);
458
//-----------------------------------------------------------------------------
460
void GraphicsLayer21::setTextureUnits(unsigned int units)
463
QGLShaderProgram* program = m_programs[units];
464
if (m_program == program) {
471
m_color_location = m_program->uniformLocation("color");
472
m_matrix_location = m_program->uniformLocation("matrix");
473
m_program->setUniformValue(m_matrix_location, m_matrix);
476
m_program->enableAttributeArray(TexCoord1);
477
m_program->enableAttributeArray(TexCoord0);
478
} else if (units > 0) {
479
m_program->disableAttributeArray(TexCoord1);
480
m_program->enableAttributeArray(TexCoord0);
482
m_program->disableAttributeArray(TexCoord1);
483
m_program->disableAttributeArray(TexCoord0);
487
//-----------------------------------------------------------------------------
489
void GraphicsLayer21::uploadData()
494
//-----------------------------------------------------------------------------
496
QGLShaderProgram* GraphicsLayer21::loadProgram(unsigned int index)
498
// Load vertex shader code
500
QFile file(QString(":/shaders/textures%1.vert").arg(index));
501
if (file.open(QFile::ReadOnly)) {
502
vertex = file.readAll();
506
// Load fragment shader code
508
file.setFileName(QString(":/shaders/textures%1.frag").arg(index));
509
if (file.open(QFile::ReadOnly)) {
510
frag = file.readAll();
514
// Update GLSL version
516
version = (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_3_0) ? "130" : version;
517
#if QT_VERSION >= 0x040700
518
version = (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_3_1) ? "140" : version;
519
version = (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_3_2) ? "150" : version;
520
version = (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_3_3) ? "330" : version;
521
version = (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_4_0) ? "400" : version;
523
if (!version.isEmpty()) {
524
vertex.replace("#version 120\n", "#version " + version + "\n");
525
vertex.replace("attribute ", "in ");
526
vertex.replace("varying ", "out ");
528
frag.replace("#version 120\n", "#version " + version + "\n\nout vec4 out_color;\n");
529
frag.replace("gl_FragColor", "out_color");
530
frag.replace("varying ", "in ");
534
m_programs[index] = new QGLShaderProgram;
535
m_programs[index]->addShaderFromSourceCode(QGLShader::Vertex, vertex);
536
m_programs[index]->addShaderFromSourceCode(QGLShader::Fragment, frag);
538
// Set attribute locations
539
m_programs[index]->bindAttributeLocation("position", Position);
541
m_programs[index]->bindAttributeLocation("texcoord0", TexCoord0);
544
m_programs[index]->bindAttributeLocation("texcoord1", TexCoord1);
547
// Link and bind program
548
m_programs[index]->link();
549
m_programs[index]->bind();
550
return m_programs[index];
553
//-----------------------------------------------------------------------------
555
GraphicsLayer11::GraphicsLayer11()
557
AppearanceDialog::setBevelsEnabled(false);
559
// Disable unused OpenGL features
560
glDisable(GL_LIGHTING);
562
// Enable OpenGL features
563
glEnableClientState(GL_VERTEX_ARRAY);
565
// Set OpenGL parameters
569
//-----------------------------------------------------------------------------
571
void GraphicsLayer11::bindTexture(unsigned int unit, GLuint texture)
574
glBindTexture(GL_TEXTURE_2D, texture);
577
//-----------------------------------------------------------------------------
579
void GraphicsLayer11::draw(const VertexArray& array, GLenum mode)
581
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &at(array.start).s);
582
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &at(array.start).x);
583
glDrawArrays(mode, 0, array.length());
586
//-----------------------------------------------------------------------------
588
void GraphicsLayer11::setBlended(bool enabled)
597
//-----------------------------------------------------------------------------
599
void GraphicsLayer11::setColor(const QColor& color)
601
glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF());
604
//-----------------------------------------------------------------------------
606
void GraphicsLayer11::setModelview(const QMatrix4x4& matrix)
608
GLfloat modelview[16];
609
convertMatrix(matrix.constData(), modelview);
610
glLoadMatrixf(modelview);
613
//-----------------------------------------------------------------------------
615
void GraphicsLayer11::setProjection(const QMatrix4x4& matrix)
617
GLfloat projection[16];
618
convertMatrix(matrix.constData(), projection);
619
glMatrixMode(GL_PROJECTION);
620
glLoadMatrixf(projection);
621
glMatrixMode(GL_MODELVIEW);
624
//-----------------------------------------------------------------------------
626
void GraphicsLayer11::setTextureUnits(unsigned int units)
629
glEnable(GL_TEXTURE_2D);
630
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
632
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
633
glDisable(GL_TEXTURE_2D);
637
//-----------------------------------------------------------------------------
639
void GraphicsLayer11::uploadData()
644
//-----------------------------------------------------------------------------
646
GraphicsLayer13::GraphicsLayer13()
648
AppearanceDialog::setBevelsEnabled(true);
650
// Set OpenGL parameters
651
activeTexture(GL_TEXTURE1);
652
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
653
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD_SIGNED);
654
activeTexture(GL_TEXTURE0);
657
//-----------------------------------------------------------------------------
659
void GraphicsLayer13::bindTexture(unsigned int unit, GLuint texture)
661
activeTexture(GL_TEXTURE0 + unit);
662
glBindTexture(GL_TEXTURE_2D, texture);
663
activeTexture(GL_TEXTURE0);
666
//-----------------------------------------------------------------------------
668
void GraphicsLayer13::draw(const VertexArray& array, GLenum mode)
670
clientActiveTexture(GL_TEXTURE1);
671
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &at(array.start).s2);
672
clientActiveTexture(GL_TEXTURE0);
673
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &at(array.start).s);
674
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &at(array.start).x);
675
glDrawArrays(mode, 0, array.length());
678
//-----------------------------------------------------------------------------
680
void GraphicsLayer13::setTextureUnits(unsigned int units)
682
activeTexture(GL_TEXTURE1);
683
clientActiveTexture(GL_TEXTURE1);
685
glEnable(GL_TEXTURE_2D);
686
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
688
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
689
glDisable(GL_TEXTURE_2D);
692
activeTexture(GL_TEXTURE0);
693
clientActiveTexture(GL_TEXTURE0);
695
glEnable(GL_TEXTURE_2D);
696
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
698
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
699
glDisable(GL_TEXTURE_2D);
703
//-----------------------------------------------------------------------------
705
GraphicsLayer15::GraphicsLayer15()
708
genBuffers(1, &vbo_id);
709
bindBuffer(GL_ARRAY_BUFFER, vbo_id);
712
//-----------------------------------------------------------------------------
714
void GraphicsLayer15::draw(const VertexArray& array, GLenum mode)
716
glDrawArrays(mode, array.start, array.length());
719
//-----------------------------------------------------------------------------
721
void GraphicsLayer15::setTextureUnits(unsigned int units)
723
activeTexture(GL_TEXTURE1);
724
clientActiveTexture(GL_TEXTURE1);
726
glEnable(GL_TEXTURE_2D);
727
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
728
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast<GLvoid*>(sizeof(GLfloat) * 5));
730
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
731
glDisable(GL_TEXTURE_2D);
734
activeTexture(GL_TEXTURE0);
735
clientActiveTexture(GL_TEXTURE0);
737
glEnable(GL_TEXTURE_2D);
738
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
739
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast<GLvoid*>(sizeof(GLfloat) * 3));
741
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
742
glDisable(GL_TEXTURE_2D);
745
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), 0);
748
//-----------------------------------------------------------------------------
750
void GraphicsLayer15::uploadData()
755
//-----------------------------------------------------------------------------