172 QObject::tr("Could not link shaders: %1").arg(shaderProgram->log()) |
161 QObject::tr("Could not link shaders: %1").arg(shaderProgram->log()) |
173 ); |
162 ); |
174 } |
163 } |
175 } |
164 } |
176 |
165 |
177 void gl::Compiler::initialize() |
166 void gl::initializeModelShaders(gl::ModelShaders *modelShaders) |
178 { |
167 { |
179 if (not this->initialized) |
168 if (not modelShaders->initialized) |
180 { |
169 { |
181 this->initializeOpenGLFunctions(); |
170 for (auto& shader : modelShaders->shaderObjects) |
182 for (auto& object : this->glObjects) |
171 { |
183 { |
172 shader.program = std::make_unique<QOpenGLShaderProgram>(); |
184 object.program = new QOpenGLShaderProgram; |
173 gl::buildShaders(shader.program.get(), ::vertexShaderSource, ::fragmentShaderSource); |
185 gl::buildShaders(object.program, ::vertexShaderSource, ::fragmentShaderSource); |
174 shader.program->bind(); |
186 object.program->bind(); |
175 shader.buffer.create(); |
187 object.buffer.create(); |
176 shader.buffer.bind(); |
188 object.buffer.bind(); |
177 shader.buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); |
189 object.buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw); |
178 shader.vertexArray.create(); |
190 object.vertexArray.create(); |
179 shader.vertexArray.bind(); |
191 object.vertexArray.bind(); |
|
192 for (int k : {0, 1, 2, 3, 4}) |
180 for (int k : {0, 1, 2, 3, 4}) |
193 { |
181 { |
194 object.program->enableAttributeArray(k); |
182 shader.program->enableAttributeArray(k); |
195 } |
183 } |
|
184 using Vertex = ModelShaders::Vertex; |
196 constexpr int stride = sizeof(Vertex); |
185 constexpr int stride = sizeof(Vertex); |
197 object.program->setAttributeBuffer(0, GL_FLOAT, offsetof(Vertex, position), 3, stride); |
186 shader.program->setAttributeBuffer(0, GL_FLOAT, offsetof(Vertex, position), 3, stride); |
198 object.program->setAttributeBuffer(1, GL_FLOAT, offsetof(Vertex, color), 4, stride); |
187 shader.program->setAttributeBuffer(1, GL_FLOAT, offsetof(Vertex, color), 4, stride); |
199 object.program->setAttributeBuffer(2, GL_FLOAT, offsetof(Vertex, normal), 3, stride); |
188 shader.program->setAttributeBuffer(2, GL_FLOAT, offsetof(Vertex, normal), 3, stride); |
200 glVertexAttribIPointer(3, 1, GL_INT, stride, reinterpret_cast<void*>(offsetof(Vertex, id))); |
189 glVertexAttribIPointer(3, 1, GL_INT, stride, reinterpret_cast<void*>(offsetof(Vertex, id))); |
201 glVertexAttribIPointer(4, 1, GL_INT, stride, reinterpret_cast<void*>(offsetof(Vertex, selected))); |
190 glVertexAttribIPointer(4, 1, GL_INT, stride, reinterpret_cast<void*>(offsetof(Vertex, selected))); |
202 object.vertexArray.release(); |
191 shader.vertexArray.release(); |
203 object.buffer.release(); |
192 shader.buffer.release(); |
204 object.program->release(); |
193 shader.program->release(); |
205 } |
194 } |
206 this->initialized = true; |
195 modelShaders->initialized = true; |
207 } |
196 } |
208 } |
197 } |
209 |
198 |
210 void gl::Compiler::build(DocumentManager* context, const gl::RenderPreferences& preferences) |
199 static gl::ArrayClass classifyPolygon(const gl::Polygon& polygon) |
211 { |
|
212 this->boundingBox = {}; |
|
213 std::vector<Vertex> vboData[gl::NUM_POLYGON_TYPES]; |
|
214 std::optional<ModelId> modelId = context->findIdForModel(this->model); |
|
215 if (modelId.has_value()) |
|
216 { |
|
217 PolygonCache* polygonBuilder = context->getPolygonCacheForModel(modelId.value()); |
|
218 if (polygonBuilder != nullptr) |
|
219 { |
|
220 const std::vector<gl::Polygon> polygons = polygonBuilder->getPolygons(context); |
|
221 for (const gl::Polygon& polygon : polygons) |
|
222 { |
|
223 this->buildPolygon(polygon, vboData, preferences); |
|
224 } |
|
225 for (int arrayId = 0; arrayId < gl::NUM_POLYGON_TYPES; arrayId += 1) |
|
226 { |
|
227 auto& buffer = this->glObjects[arrayId].buffer; |
|
228 auto& vector = vboData[arrayId]; |
|
229 this->storedVertexCounts[arrayId] = vector.size(); |
|
230 this->glObjects[arrayId].cachedData = vector; // todo: get rid of this copy |
|
231 buffer.bind(); |
|
232 buffer.allocate(vector.data(), static_cast<int>(vector.size() * sizeof vector[0])); |
|
233 buffer.release(); |
|
234 } |
|
235 } |
|
236 } |
|
237 } |
|
238 |
|
239 gl::ArrayClass classifyPolygon(const gl::Polygon& polygon) |
|
240 { |
200 { |
241 switch (polygon.type) |
201 switch (polygon.type) |
242 { |
202 { |
243 case gl::Polygon::EdgeLine: |
203 case gl::Polygon::EdgeLine: |
244 return gl::ArrayClass::Lines; |
204 return gl::ArrayClass::Lines; |
250 return gl::ArrayClass::ConditionalLines; |
210 return gl::ArrayClass::ConditionalLines; |
251 } |
211 } |
252 return gl::ArrayClass::Lines; |
212 return gl::ArrayClass::Lines; |
253 } |
213 } |
254 |
214 |
255 ldraw::id_t gl::Compiler::idFromColor(const std::array<GLubyte, 3>& data) |
215 template<typename Fn> |
256 { |
216 void iterateModelPolygons(Model* model, DocumentManager* context, Fn&& fn) |
257 return {data[0] * std::int32_t{0x10000} + data[1] * std::int32_t{0x100} + data[2]}; |
217 { |
258 } |
218 std::optional<ModelId> modelId = context->findIdForModel(model); |
259 |
219 if (modelId.has_value()) |
260 void gl::Compiler::buildPolygon( |
220 { |
261 gl::Polygon polygon, |
221 PolygonCache* polygonCache= context->getPolygonCacheForModel(modelId.value()); |
262 std::vector<Vertex>* vboData, |
222 if (polygonCache != nullptr) |
263 const gl::RenderPreferences& preferences) |
223 { |
264 { |
224 for (const gl::Polygon& polygon : polygonCache->getPolygons(context)) |
265 const gl::ArrayClass vboClass = classifyPolygon(polygon); |
225 { |
266 std::vector<Vertex>& vertexBuffer = vboData[static_cast<int>(vboClass)]; |
226 fn(polygon); |
267 auto vertexRing = iter::ring(polygon.vertices, polygon.numPolygonVertices()); |
227 } |
268 reserveMore(vertexBuffer, polygon.numPolygonVertices()); |
228 } |
269 const QColor color = this->getColorForPolygon(polygon, preferences); |
229 } |
270 for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) |
230 } |
271 { |
231 |
272 const glm::vec3& v1 = vertexRing[i - 1]; |
232 static QColor getColorForPolygon( |
273 const glm::vec3& v2 = vertexRing[i]; |
233 const gl::Polygon& polygon, |
274 const glm::vec3& v3 = vertexRing[i + 1]; |
234 const gl::RenderPreferences& preferences, |
275 this->boundingBox.consider(polygon.vertices[i]); |
235 const ldraw::ColorTable& colorTable) |
276 Vertex& vertex = vertexBuffer.emplace_back(); |
|
277 vertex.position = polygon.vertices[i]; |
|
278 vertex.normal = glm::normalize(glm::cross(v1 - v2, v3 - v2)); |
|
279 vertex.color = glm::vec4{color.redF(), color.greenF(), color.blueF(), color.alphaF()}; |
|
280 vertex.id = polygon.id.value; |
|
281 } |
|
282 } |
|
283 |
|
284 QColor gl::Compiler::getColorForPolygon(const gl::Polygon& polygon, const gl::RenderPreferences& preferences) |
|
285 { |
236 { |
286 QColor color; |
237 QColor color; |
287 // For normal colors, use the polygon's color. |
238 // For normal colors, use the polygon's color. |
288 if (polygon.color == ldraw::MAIN_COLOR) |
239 if (polygon.color == ldraw::MAIN_COLOR) |
289 { |
240 { |
295 color = luma(preferences.backgroundColor) > (40.0 / 256.0) ? Qt::black : Qt::white; |
246 color = luma(preferences.backgroundColor) > (40.0 / 256.0) ? Qt::black : Qt::white; |
296 } |
247 } |
297 else |
248 else |
298 { |
249 { |
299 // Not main or edge color, use the polygon's color as is. |
250 // Not main or edge color, use the polygon's color as is. |
300 color = this->colorTable[polygon.color].faceColor; |
251 color = colorTable[polygon.color].faceColor; |
301 } |
252 } |
302 return color; |
253 return color; |
303 } |
254 } |
304 |
255 |
305 glm::vec3 gl::Compiler::modelCenter() const |
256 /** |
306 { |
257 * @brief Computes the minimum bounding box for a model |
307 return boxCenter(this->boundingBox); |
258 */ |
308 } |
259 BoundingBox gl::boundingBoxForModel(Model* model, DocumentManager* context) |
309 |
260 { |
310 double gl::Compiler::modelDistance() const |
261 BoundingBox result = emptyBoundingBox; |
311 { |
262 iterateModelPolygons(model, context, [&](const gl::Polygon& polygon) |
312 return static_cast<double>(longestMeasure(this->boundingBox)); |
263 { |
313 } |
264 for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) |
314 |
265 { |
315 void gl::Compiler::bindVertexArray(gl::ArrayClass arrayClass) |
266 addPointToBox(result, polygon.vertices[i]); |
316 { |
267 } |
317 auto& object = this->glObjects[static_cast<int>(arrayClass)]; |
268 }); |
318 object.vertexArray.bind(); |
269 return result; |
319 object.program->bind(); |
270 } |
320 } |
271 |
321 |
272 /** |
322 void gl::Compiler::releaseVertexArray(gl::ArrayClass arrayClass) |
273 * @brief gl::build Creates GL vertices for objects in the model and buffers them to shaders. |
323 { |
274 */ |
324 auto& object = this->glObjects[static_cast<int>(arrayClass)]; |
275 void gl::build( |
325 object.program->release(); |
276 gl::ModelShaders* shaders, |
326 object.vertexArray.release(); |
277 Model* model, |
327 } |
278 const ldraw::ColorTable& colorTable, |
328 |
279 DocumentManager* context, |
329 void gl::Compiler::setSelectedObjects(const QSet<ldraw::id_t> ids) |
280 const gl::RenderPreferences& preferences) |
330 { |
281 { |
331 for (auto& object : this->glObjects) |
282 for (gl::ModelShaders::ShaderObject& shader : shaders->shaderObjects) { |
332 { |
283 shader.cachedData.clear(); |
333 std::vector<Vertex>& vector = object.cachedData; |
284 } |
334 for (Vertex& vertex : vector) |
285 iterateModelPolygons(model, context, [&](const Polygon& polygon) |
|
286 { |
|
287 const int index = static_cast<int>(classifyPolygon(polygon)); |
|
288 std::vector<gl::ModelShaders::Vertex>& vertexBuffer = shaders->shaderObjects[index].cachedData; |
|
289 auto vertexRing = iter::ring(polygon.vertices, polygon.numPolygonVertices()); |
|
290 reserveMore(vertexBuffer, polygon.numPolygonVertices()); |
|
291 const QColor color = getColorForPolygon(polygon, preferences, colorTable); |
|
292 for (unsigned int i = 0; i < polygon.numPolygonVertices(); i += 1) |
|
293 { |
|
294 const glm::vec3& v1 = vertexRing[i - 1]; |
|
295 const glm::vec3& v2 = vertexRing[i]; |
|
296 const glm::vec3& v3 = vertexRing[i + 1]; |
|
297 gl::ModelShaders::Vertex& vertex = vertexBuffer.emplace_back(); |
|
298 vertex.position = polygon.vertices[i]; |
|
299 vertex.normal = glm::normalize(glm::cross(v1 - v2, v3 - v2)); |
|
300 vertex.color = glm::vec4{color.redF(), color.greenF(), color.blueF(), color.alphaF()}; |
|
301 vertex.id = polygon.id.value; |
|
302 } |
|
303 }); |
|
304 for (gl::ModelShaders::ShaderObject& shader : shaders->shaderObjects) |
|
305 { |
|
306 shader.vertexCount = shader.cachedData.size(); |
|
307 shader.buffer.bind(); |
|
308 const int bytes = static_cast<int>(shader.cachedData.size() * sizeof shader.cachedData[0]); |
|
309 shader.buffer.allocate(shader.cachedData.data(), bytes); |
|
310 shader.buffer.release(); |
|
311 } |
|
312 } |
|
313 |
|
314 ldraw::id_t gl::idFromColor(const std::array<GLubyte, 3>& data) |
|
315 { |
|
316 return {data[0] * std::int32_t{0x10000} + data[1] * std::int32_t{0x100} + data[2]}; |
|
317 } |
|
318 |
|
319 void gl::bindModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass) |
|
320 { |
|
321 ModelShaders::ShaderObject& shaderObject = shaders->shaderObjects[static_cast<int>(arrayClass)]; |
|
322 shaderObject.vertexArray.bind(); |
|
323 shaderObject.program->bind(); |
|
324 } |
|
325 |
|
326 void gl::releaseModelShaderVertexArray(gl::ModelShaders* shaders, gl::ArrayClass arrayClass) |
|
327 { |
|
328 ModelShaders::ShaderObject& shaderObject = shaders->shaderObjects[static_cast<int>(arrayClass)]; |
|
329 shaderObject.program->release(); |
|
330 shaderObject.vertexArray.release(); |
|
331 } |
|
332 |
|
333 void gl::setModelShaderSelectedObjects(gl::ModelShaders* shaders, const QSet<ldraw::id_t>& ids) |
|
334 { |
|
335 for (ModelShaders::ShaderObject& object : shaders->shaderObjects) |
|
336 { |
|
337 std::vector<ModelShaders::Vertex>& vector = object.cachedData; |
|
338 for (ModelShaders::Vertex& vertex : vector) |
335 { |
339 { |
336 vertex.selected = (ids.contains({vertex.id})) ? 1 : 0; |
340 vertex.selected = (ids.contains({vertex.id})) ? 1 : 0; |
337 } |
341 } |
338 const GLsizeiptr size = static_cast<int>(vector.size() * sizeof vector[0]); |
342 const GLsizeiptr size = static_cast<int>(vector.size() * sizeof vector[0]); |
339 object.buffer.bind(); |
343 object.buffer.bind(); |
340 glBufferSubData(GL_ARRAY_BUFFER, 0, size, vector.data()); |
344 glBufferSubData(GL_ARRAY_BUFFER, 0, size, vector.data()); |
341 object.buffer.release(); |
345 object.buffer.release(); |
342 } |
346 } |
343 } |
347 } |
344 |
348 |
345 std::size_t gl::Compiler::vertexCount(const gl::ArrayClass arrayClass) const |
349 std::size_t gl::vertexCount(const gl::ModelShaders* shaders, const gl::ArrayClass arrayClass) |
346 { |
350 { |
347 return this->storedVertexCounts[static_cast<int>(arrayClass)]; |
351 return shaders->shaderObjects[static_cast<int>(arrayClass)].vertexCount; |
348 } |
352 } |