Wed, 22 Jan 2020 00:23:29 +0200
at least VAOs work now
19 | 1 | /* |
2 | * LDForge: LDraw parts authoring CAD | |
3 | * Copyright (C) 2013 - 2018 Teemu Piippo | |
4 | * | |
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. | |
9 | * | |
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. | |
14 | * | |
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/>. | |
17 | */ | |
18 | ||
19 | #define GL_GLEXT_PROTOTYPES | |
20 | #include <GL/glu.h> | |
21 | #include <GL/glext.h> | |
26 | 22 | #include <QMessageBox> |
19 | 23 | #include "gl/compiler.h" |
24 | #include "documentmanager.h" | |
25 | #include "invert.h" | |
26 | #include "ring.h" | |
27 | ||
26 | 28 | gl::Compiler::Compiler(const ColorTable& colorTable, QObject* parent) : |
29 | QObject{parent}, | |
30 | colorTable{colorTable} | |
21 | 31 | { |
32 | } | |
33 | ||
19 | 34 | gl::Compiler::~Compiler() |
35 | { | |
26 | 36 | if (this->initialized) |
37 | { | |
38 | for (int arrayId = 0; arrayId < gl::NUM_ARRAY_CLASSES; arrayId += 1) | |
39 | { | |
40 | glDeleteProgram(this->glObjects[arrayId].program); | |
41 | glDeleteShader(this->glObjects[arrayId].vertexShader); | |
42 | glDeleteProgram(this->glObjects[arrayId].fragmentShader); | |
43 | } | |
44 | } | |
45 | } | |
19 | 46 | |
26 | 47 | void gl::Compiler::initialize() |
48 | { | |
49 | if (not this->initialized) | |
50 | { | |
51 | this->initializeOpenGLFunctions(); | |
52 | for (int arrayId = 0; arrayId < gl::NUM_ARRAY_CLASSES; arrayId += 1) | |
53 | { | |
54 | QOpenGLVertexArrayObject& vao = this->vertexArrays[arrayId]; | |
55 | vao.create(); | |
56 | vao.bind(); | |
57 | glGenBuffers(gl::numVboSubclasses, &this->storedVbo[gl::numVboSubclasses * arrayId]); | |
58 | this->buildShaders(arrayId); | |
59 | vao.release(); | |
60 | } | |
61 | //glGenBuffers(countof(this->storedVbo), &this->storedVbo[0]); | |
62 | this->initialized = true; | |
63 | } | |
19 | 64 | } |
21 | 65 | |
66 | void gl::Compiler::build(Model* model, DocumentManager* context) | |
67 | { | |
22
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
68 | this->boundingBox = {}; |
21 | 69 | std::vector<GLfloat> vboData[gl::numVbos]; |
70 | const std::vector<gl::Polygon> polygons = model->getPolygons(context); | |
71 | for (const gl::Polygon& polygon : polygons) | |
72 | { | |
73 | this->buildPolygon(polygon, vboData); | |
74 | } | |
26 | 75 | /* |
76 | for (int index = 0; index < gl::numVbos; index += 1) | |
77 | { | |
78 | this->upload(index, vboData[index]); | |
79 | }*/ | |
80 | for (int arrayId = 0; arrayId < gl::NUM_ARRAY_CLASSES; arrayId += 1) | |
21 | 81 | { |
26 | 82 | this->vertexArrays[arrayId].bind(); |
83 | for (int i = 0; i < gl::numVboSubclasses; i += 1) | |
84 | { | |
85 | const int vboIndex = gl::vboIndex({static_cast<gl::ArrayClass>(arrayId), static_cast<gl::VboSubclass>(i)}); | |
86 | this->upload(vboIndex, vboData[vboIndex]); | |
87 | } | |
88 | this->vertexArrays[arrayId].release(); | |
21 | 89 | } |
90 | } | |
91 | ||
26 | 92 | gl::ArrayClass classifyPolygon(const gl::Polygon& polygon) |
21 | 93 | { |
94 | switch (polygon.type) | |
95 | { | |
96 | case gl::Polygon::EdgeLine: | |
26 | 97 | return gl::ArrayClass::Lines; |
21 | 98 | case gl::Polygon::Triangle: |
26 | 99 | return gl::ArrayClass::Triangles; |
21 | 100 | case gl::Polygon::Quadrilateral: |
26 | 101 | return gl::ArrayClass::Quads; |
21 | 102 | case gl::Polygon::ConditionalEdge: |
26 | 103 | return gl::ArrayClass::ConditionalLines; |
21 | 104 | } |
26 | 105 | return gl::ArrayClass::Lines; |
21 | 106 | } |
107 | ||
26 | 108 | [[maybe_unused]] |
109 | static QColor colorFromId(linetypes::Id id) | |
21 | 110 | { |
111 | // Calculate a color based from this index. This method caters for | |
112 | // 16777216 objects. I don't think that will be exceeded anytime soon. | |
113 | const int r = (id.value / 0x10000) % 0x100; | |
114 | const int g = (id.value / 0x100) % 0x100; | |
115 | const int b = id.value % 0x100; | |
116 | return {r, g, b}; | |
117 | } | |
118 | ||
119 | void gl::Compiler::buildPolygon(gl::Polygon polygon, std::vector<GLfloat>* vboData) | |
120 | { | |
26 | 121 | const gl::ArrayClass vboClass = classifyPolygon(polygon); |
122 | std::vector<GLfloat>& vertexBuffer = vboData[gl::vboIndex({vboClass, gl::VboSubclass::VertexData})]; | |
123 | std::vector<GLfloat>& normalsBuffer = vboData[gl::vboIndex({vboClass, gl::VboSubclass::Normals})]; | |
21 | 124 | auto vertexRing = iter::ring(polygon.vertices, polygon.numPolygonVertices()); |
26 | 125 | reserveMore(normalsBuffer, polygon.numPolygonVertices() * 3_z); |
22
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
126 | for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) |
21 | 127 | { |
128 | const Point3D& v1 = vertexRing[i - 1]; | |
129 | const Point3D& v2 = vertexRing[i]; | |
130 | const Point3D& v3 = vertexRing[i + 1]; | |
131 | const QVector3D normal = QVector3D::crossProduct(v3 - v2, v1 - v2).normalized(); | |
132 | for (const GLfloat coord : {normal.x(), normal.y(), normal.z()}) | |
26 | 133 | normalsBuffer.push_back(coord); |
21 | 134 | } |
26 | 135 | reserveMore(vertexBuffer, polygon.numPolygonVertices() * 7); |
136 | const QColor color = this->getColorForPolygon(polygon); | |
21 | 137 | // Transform vertices so that they're suitable for GL rendering |
22
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
138 | for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) |
21 | 139 | { |
26 | 140 | Point3D& point = polygon.vertices[i]; |
21 | 141 | polygon.vertices[i].y = -polygon.vertices[i].y; |
142 | polygon.vertices[i].z = -polygon.vertices[i].z; | |
22
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
143 | this->boundingBox.consider(polygon.vertices[i]); |
26 | 144 | vertexBuffer.push_back(static_cast<GLfloat>(point.x)); |
145 | vertexBuffer.push_back(static_cast<GLfloat>(point.y)); | |
146 | vertexBuffer.push_back(static_cast<GLfloat>(point.z)); | |
147 | vertexBuffer.push_back(static_cast<GLfloat>(color.redF())); | |
148 | vertexBuffer.push_back(static_cast<GLfloat>(color.greenF())); | |
149 | vertexBuffer.push_back(static_cast<GLfloat>(color.blueF())); | |
150 | vertexBuffer.push_back(static_cast<GLfloat>(color.alphaF())); | |
21 | 151 | } |
152 | } | |
153 | ||
26 | 154 | QColor gl::Compiler::getColorForPolygon(const gl::Polygon& polygon) |
21 | 155 | { |
156 | QColor color; | |
26 | 157 | // For normal colors, use the polygon's color. |
158 | if (polygon.color == colors::main) | |
21 | 159 | { |
26 | 160 | color = {255, 255, 64}; // mainColorRepresentation(); |
21 | 161 | } |
26 | 162 | else if (polygon.color == colors::edge) |
21 | 163 | { |
26 | 164 | // Edge color is black, unless we have a dark background, in which case lines need to be bright. |
165 | color = Qt::black; //luma(config::backgroundColor()) > 40 ? Qt::black : Qt::white; | |
21 | 166 | } |
167 | else | |
168 | { | |
26 | 169 | // Not main or edge color, use the polygon's color as is. |
170 | color = this->colorTable[polygon.color].faceColor; | |
21 | 171 | } |
172 | return color; | |
173 | } | |
174 | ||
22
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
175 | Point3D gl::Compiler::modelCenter() const |
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
176 | { |
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
177 | return boxCenter(this->boundingBox); |
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
178 | } |
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
179 | |
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
180 | double gl::Compiler::modelDistance() const |
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
181 | { |
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
182 | return longestMeasure(this->boundingBox); |
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
183 | } |
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
184 | |
26 | 185 | void gl::Compiler::bindVertexArray(gl::ArrayClass arrayClass) |
186 | { | |
187 | this->vertexArrays[static_cast<int>(arrayClass)].bind(); | |
188 | glUseProgram(this->glObjects[static_cast<int>(arrayClass)].program); | |
189 | } | |
190 | ||
191 | void gl::Compiler::releaseVertexArray(gl::ArrayClass arrayClass) | |
192 | { | |
193 | this->vertexArrays[static_cast<int>(arrayClass)].release(); | |
194 | } | |
195 | ||
196 | void gl::Compiler::buildShaders(int arrayId) | |
21 | 197 | { |
26 | 198 | /* |
199 | this->glObjects[arrayId].vertexShader = glCreateShader(GL_VERTEX_SHADER); | |
200 | glShaderSource(this->glObjects[arrayId].vertexShader, 1, &vertexShaderSource, nullptr); | |
201 | glCompileShader(this->glObjects[arrayId].vertexShader); | |
202 | this->glObjects[arrayId].fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); | |
203 | glShaderSource(this->glObjects[arrayId].fragmentShader, 1, &fragmentShaderSource, nullptr); | |
204 | glCompileShader(this->glObjects[arrayId].fragmentShader); | |
205 | for (auto&& pair : { | |
206 | std::make_pair(this->glObjects[arrayId].vertexShader, tr("vertex shader")), | |
207 | std::make_pair(this->glObjects[arrayId].fragmentShader, tr("fragment shader")), | |
208 | }) | |
209 | { | |
210 | GLint status; | |
211 | glGetShaderiv(this->glObjects[arrayId].fragmentShader, GL_COMPILE_STATUS, &status); | |
212 | if (status != GL_TRUE) | |
213 | { | |
214 | char compileLog[512]; | |
215 | glGetShaderInfoLog(pair.first, countof(compileLog), nullptr, compileLog); | |
216 | QMessageBox::critical(nullptr, tr("Shader compile error"), tr("Unable to compile the %1. Compile log:\n\n%2").arg(pair.second).arg(compileLog)); | |
217 | abort(); | |
218 | } | |
219 | } | |
220 | this->glObjects[arrayId].program = glCreateProgram(); | |
221 | glAttachShader(this->glObjects[arrayId].program, this->glObjects[arrayId].vertexShader); | |
222 | glAttachShader(this->glObjects[arrayId].program, this->glObjects[arrayId].fragmentShader); | |
223 | glLinkProgram(this->glObjects[arrayId].program); | |
224 | glUseProgram(this->glObjects[arrayId].program); | |
225 | const std::size_t size = gl::FLOATS_PER_VERTEX * sizeof(GLfloat); | |
226 | const GLuint posAttrib = static_cast<GLuint>(glGetAttribLocation(this->glObjects[arrayId].program, "position")); | |
227 | glEnableVertexAttribArray(posAttrib); | |
228 | glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, size, gl::offset(0)); | |
229 | const GLuint colAttrib = static_cast<GLuint>(glGetAttribLocation(this->glObjects[arrayId].program, "color")); | |
230 | glEnableVertexAttribArray(colAttrib); | |
231 | glVertexAttribPointer(colAttrib, 4, GL_FLOAT, GL_FALSE, size, gl::offset(3 * sizeof(GLfloat))); | |
232 | */ | |
21 | 233 | } |
234 | ||
235 | /// | |
236 | /// \brief Uploads data to a vbo | |
237 | /// \param vboAddress Which vbo to upload to | |
238 | /// \param data Data to upload to vbo | |
239 | /// | |
240 | void gl::Compiler::upload(const int vboIndex, const std::vector<GLfloat>& data) | |
241 | { | |
242 | glBindBuffer(GL_ARRAY_BUFFER, this->storedVbo[vboIndex]); | |
22
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
243 | glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(data.size() * sizeof data[0]), data.data(), GL_STATIC_DRAW); |
21 | 244 | this->storedVboSizes[vboIndex] = data.size(); |
245 | } | |
246 | ||
247 | GLuint gl::Compiler::vbo(const VboAddress vboAddress) const | |
248 | { | |
249 | return this->storedVbo[gl::vboIndex(vboAddress)]; | |
250 | } | |
251 | ||
22
6da867fa5429
commit work on GL rendering
Teemu Piippo <teemu@hecknology.net>
parents:
21
diff
changeset
|
252 | std::size_t gl::Compiler::vboSize(const VboAddress vboAddress) const |
21 | 253 | { |
254 | return this->storedVboSizes[gl::vboIndex(vboAddress)]; | |
255 | } | |
256 | ||
257 | int gl::vboIndex(const VboAddress vboAddress) | |
258 | { | |
259 | return static_cast<std::uint8_t>(vboAddress.vboClass) * gl::numVboSubclasses | |
260 | + static_cast<std::uint8_t>(vboAddress.vboSubclass); | |
261 | } |