/* QGLView Copyright (C) 2020 Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "glvbo.h" GLVBO::GLVBO(GLenum usage_) { buffer_ = 0; va_ = 0; usage = usage_; changed = true; } GLVBO::~GLVBO() { //destroy(); } void GLVBO::init() { initializeOpenGLFunctions(); if (!isInit()) { //glGenVertexArrays(1, &va_); glGenBuffers(1, &buffer_); } changed = true; } void GLVBO::destroy() { if (buffer_ != 0) glDeleteFramebuffers(1, &buffer_); buffer_ = 0; // if (va_ != 0) glDeleteVertexArrays(1, &va_); // va_ = 0; } void GLVBO::calculateBinormals() { tangents_.clear(); bitangents_.clear(); if (vertices_.isEmpty() || texcoords_.isEmpty()) return; int vcnt = vertices_.size() / 3; if (texcoords_.size() != vcnt * 2) return; int tcnt = vcnt / 3; //qDebug() << "calculateBinormals" << vcnt << tcnt << vertices_.size() << texcoords_.size() << "..."; for (int t = 0; t < tcnt; ++t) { int vi = t*9, ti = t*6; Vector3d v0(vertices_[vi + 0], vertices_[vi + 1], vertices_[vi + 2]); Vector3d v1(vertices_[vi + 3], vertices_[vi + 4], vertices_[vi + 5]); Vector3d v2(vertices_[vi + 6], vertices_[vi + 7], vertices_[vi + 8]); Vector2d t0(texcoords_[ti + 0], texcoords_[ti + 1]); Vector2d t1(texcoords_[ti + 2], texcoords_[ti + 3]); Vector2d t2(texcoords_[ti + 4], texcoords_[ti + 5]); Vector3d dv1 = v1 - v0, dv2 = v2 - v0; Vector2d dt1 = t1 - t0, dt2 = t2 - t0; Vector3d tan; Vector3d bitan; tan = (dv1 * dt2.y - dv2 * dt1.y).normalize(); bitan = (dv2 * dt1.x - dv1 * dt2.x).normalize(); for (int i = 0; i < 3; ++i) { tangents_ << tan.x << tan.y << tan.z; bitangents_ << bitan.x << bitan.y << bitan.z; } //qDebug() << " t" << t << vi << ti << dv1.toQVector3D() << "..."; } //qDebug() << "calculateBinormals" << vcnt << tcnt << tangents_.size(); } bool GLVBO::rebuffer(bool clear_) { initializeOpenGLFunctions(); QVector data; //data.clear(); calculateBinormals(); data << vertices_; if (!normals_.isEmpty()) { data << normals_; has_normals = true; } else has_normals = false; if (!texcoords_.isEmpty()) { data << texcoords_; has_texcoords = true; } else has_texcoords = false; if (!colors_.isEmpty()) { data << colors_; has_colors = true; } else has_colors = false; if (!tangents_.isEmpty()) { data << tangents_ << bitangents_; has_binormals = true; } else has_binormals = false; //glBindVertexArray(va_); //qDebug() << "load buffer" << data.size() << buffer_; glBindBuffer(GL_ARRAY_BUFFER, buffer_); glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), data.constData(), usage); glBindBuffer(GL_ARRAY_BUFFER, 0); //glBindVertexArray(0); vert_count = vertices_.size() / 3; changed = false; //qDebug() << "rebuff" << buffer_ << vert_count; if (clear_) clear(); return !isEmpty(); } void GLVBO::draw(GLenum type, QOpenGLShaderProgram * prog, bool simplest) { if (buffer_ == 0 || vert_count == 0) return; if (changed) rebuffer(); //qDebug() << "draw" << vert_count; void * offset = (void*)(vert_count * 3 * sizeof(GLfloat)); //glBindVertexArray(va_); void * offsets[5] = {0, 0, 0, 0, 0}; if (!simplest) { if (has_normals) { offsets[0] = offset; offset = (void*)(llong(offset) + vert_count * 3 * sizeof(GLfloat)); } if (has_texcoords) { offsets[1] = offset; offset = (void*)(llong(offset) + vert_count * 2 * sizeof(GLfloat)); } if (has_colors) { offsets[2] = offset; offset = (void*)(llong(offset) + vert_count * 4 * sizeof(GLfloat)); } if (has_binormals) { offsets[3] = offset; offset = (void*)(llong(offset) + vert_count * 3 * sizeof(GLfloat)); offsets[4] = offset; } } glBindBuffer(GL_ARRAY_BUFFER, buffer_); if (prog) { locs.clear(); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_COLOR_ARRAY); int loc = prog->attributeLocation("qgl_Vertex"); glEnableVertexAttribArray(loc); glVertexAttribPointer(loc, 3, GL_FLOAT, 0, 0, 0); locs << loc; if (!simplest) { loc = prog->attributeLocation("qgl_Normal"); if (has_normals) { glEnableVertexAttribArray(loc); glVertexAttribPointer(loc, 3, GL_FLOAT, 0, 0, offsets[0]); locs << loc; } else glDisableVertexAttribArray(loc); loc = prog->attributeLocation("qgl_Texture"); if (has_texcoords) { glEnableVertexAttribArray(loc); glVertexAttribPointer(loc, 2, GL_FLOAT, 0, 0, offsets[1]); locs << loc; } else glDisableVertexAttribArray(loc); loc = prog->attributeLocation("qgl_Color"); if (has_colors) { glEnableVertexAttribArray(loc); glVertexAttribPointer(loc, 4, GL_FLOAT, 0, 0, offsets[2]); locs << loc; } else glDisableVertexAttribArray(loc); loc = prog->attributeLocation("qgl_Tangent"); int loc2 = prog->attributeLocation("qgl_Bitangent"); if (has_binormals) { glEnableVertexAttribArray(loc); glEnableVertexAttribArray(loc2); glVertexAttribPointer(loc, 3, GL_FLOAT, 0, 0, offsets[3]); glVertexAttribPointer(loc2, 3, GL_FLOAT, 0, 0, offsets[4]); locs << loc << loc2; } else { glDisableVertexAttribArray(loc); glDisableVertexAttribArray(loc2); } } } else { glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, 0); if (!simplest) { if (has_normals) { glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer(GL_FLOAT, 0, offsets[0]); } else glDisableClientState(GL_NORMAL_ARRAY); if (has_texcoords) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); glTexCoordPointer(2, GL_FLOAT, 0, offsets[1]); } else glDisableClientState(GL_TEXTURE_COORD_ARRAY); if (has_colors) { glEnableClientState(GL_COLOR_ARRAY); glColorPointer(4, GL_FLOAT, 0, offsets[2]); } else glDisableClientState(GL_COLOR_ARRAY); } } //qDebug() << "draw" << vert_count << buffer_ << offsets[0] << offsets[1] << offsets[3]; glDrawArrays(type, 0, vert_count); //qDebug() << "draw" << vert_count << buffer_ << "done"; glBindBuffer(GL_ARRAY_BUFFER, 0); foreach (int l, locs) glDisableVertexAttribArray(l); } void GLVBO::clear() { vertices_.clear(); normals_.clear(); texcoords_.clear(); colors_.clear(); } void GLVBO::translatePoints(const QVector3D & dp) { if (vertices_.isEmpty()) return; int vcnt = vertices_.size() / 3; for (int i = 0; i < vcnt; ++i) { int vi = i * 3; vertices_[vi + 0] += dp.x(); vertices_[vi + 1] += dp.y(); vertices_[vi + 2] += dp.z(); } changed = true; } void GLVBO::scalePoints(const QVector3D & dp) { if (vertices_.isEmpty()) return; int vcnt = vertices_.size() / 3; for (int i = 0; i < vcnt; ++i) { int vi = i * 3; vertices_[vi + 0] *= dp.x(); vertices_[vi + 1] *= dp.y(); vertices_[vi + 2] *= dp.z(); } changed = true; } bool GLVBO::saveToFile(const QString & filename) { if (filename.isEmpty()) return false; QFile f(filename); QByteArray ba; if (f.open(QFile::WriteOnly)) { QDataStream out(&ba, QFile::WriteOnly); out << vertices_ << normals_ << texcoords_ << colors_; ba = qCompress(ba); f.resize(0); f.write(ba); f.close(); return true; } return false; } bool GLVBO::loadFromFile(const QString & filename) { if (filename.isEmpty()) return false; QFile f(filename); QByteArray ba; if (f.open(QFile::ReadOnly)) { ba = f.readAll(); if (ba.isEmpty()) return false; ba = qUncompress(ba); QDataStream in(ba); in >> vertices_ >> normals_ >> texcoords_ >> colors_; changed = true; f.close(); return !isEmpty(); } return false; } Box3D GLVBO::boundingBox() const { if (vertices_.size() < 3) return Box3D(); int vcnt = vertices_.size() / 3; //qDebug() << "calculateBinormals" << vcnt << tcnt << vertices_.size() << texcoords_.size() << "..."; GLfloat mix, miy, miz, max, may, maz; Vector3d v0(vertices_[0], vertices_[1], vertices_[2]); mix = max = v0.x; miy = may = v0.y; miz = maz = v0.z; Box3D bound; for (int t = 1; t < vcnt; ++t) { int vi = t*3; Vector3d v(vertices_[vi + 0], vertices_[vi + 1], vertices_[vi + 2]); if (mix > v.x) mix = v.x; if (max < v.x) max = v.x; if (miy > v.y) miy = v.y; if (may < v.y) may = v.y; if (miz > v.z) miz = v.z; if (maz < v.z) maz = v.z; } bound.x = mix; bound.y = miy; bound.z = miz; bound.length = max - mix; bound.width = may - miy; bound.height = maz - miz; return bound; }