383 lines
11 KiB
C++
383 lines
11 KiB
C++
/*
|
|
QGLView
|
|
Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "glcamera.h"
|
|
#include "qglview.h"
|
|
|
|
#include <QPainter>
|
|
|
|
//__GLWidget__ * currentQGLView;
|
|
//QMutex globMutex;
|
|
|
|
|
|
QString readCharsUntilNull(QDataStream & s) {
|
|
QString str;
|
|
char ch;
|
|
s.readRawData(&ch, 1);
|
|
while (ch != '\0') {
|
|
str += ch;
|
|
s.readRawData(&ch, 1);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
QString findFile(const QString & file, const QStringList & pathes) {
|
|
QFileInfo fi(QString(file).replace("\\", "/"));
|
|
//qDebug() << "search" << file << "in" << pathes;
|
|
if (fi.exists()) return fi.absoluteFilePath();
|
|
QString fn = fi.fileName();
|
|
if (fn.contains("/")) fn = fn.mid(fn.lastIndexOf("/"));
|
|
foreach (QString p, pathes) {
|
|
QFileInfoList fil = QDir(p).entryInfoList(QStringList(fn), QDir::Files | QDir::NoDotAndDotDot);
|
|
//qDebug() << "findFile" << fn << "in" << p << "->" << fil.size();
|
|
if (!fil.isEmpty())
|
|
return fil[0].absoluteFilePath();
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
|
|
void glDrawQuad(QOpenGLShaderProgram * prog, QVector4D * corner_dirs, GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
|
|
glResetAllTransforms();
|
|
glSetPolygonMode(GL_FILL);
|
|
glDisable(GL_LIGHTING);
|
|
glEnable(GL_TEXTURE_2D);
|
|
int loc = prog ? prog->attributeLocation("qgl_Color") : -1,
|
|
locv = prog ? prog->attributeLocation("qgl_Vertex") : -1,
|
|
loct = prog ? prog->attributeLocation("qgl_Texture") : -1,
|
|
locc = prog ? prog->attributeLocation("view_corner") : -1;
|
|
//if (prog) {qDebug() << locv << loct << locc;}
|
|
QOpenGLFunctions * glFuncs = QOpenGLContext::currentContext()->functions();
|
|
if (prog) {
|
|
static const GLfloat cols [] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f};
|
|
static const GLfloat verts[] = {x, y, x+w, y, x, y+h, x+w, y+h};
|
|
static const GLfloat texs [] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f};
|
|
GLfloat vcs[] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f};
|
|
if (corner_dirs) {
|
|
vcs[0] = corner_dirs[0].x(); vcs[1] = corner_dirs[0].y(); vcs[2] = corner_dirs[0].z();
|
|
vcs[3] = corner_dirs[1].x(); vcs[4] = corner_dirs[1].y(); vcs[5] = corner_dirs[1].z();
|
|
vcs[6] = corner_dirs[2].x(); vcs[7] = corner_dirs[2].y(); vcs[8] = corner_dirs[2].z();
|
|
vcs[9] = corner_dirs[3].x(); vcs[10] = corner_dirs[3].y(); vcs[11] = corner_dirs[3].z();
|
|
}
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
glFuncs->glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
glFuncs->glEnableVertexAttribArray(loc);
|
|
glFuncs->glVertexAttribPointer(loc, 3, GL_FLOAT, 0, 0, cols);
|
|
glFuncs->glEnableVertexAttribArray(locv);
|
|
glFuncs->glVertexAttribPointer(locv, 2, GL_FLOAT, 0, 0, verts);
|
|
glFuncs->glEnableVertexAttribArray(loct);
|
|
glFuncs->glVertexAttribPointer(loct, 2, GL_FLOAT, 0, 0, texs);
|
|
glFuncs->glEnableVertexAttribArray(locc);
|
|
glFuncs->glVertexAttribPointer(locc, 3, GL_FLOAT, 0, 0, vcs);
|
|
glFuncs->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
glFuncs->glDisableVertexAttribArray(loc);
|
|
glFuncs->glDisableVertexAttribArray(locv);
|
|
glFuncs->glDisableVertexAttribArray(loct);
|
|
glFuncs->glDisableVertexAttribArray(locc);
|
|
} else {
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glColor4f(1.f, 1.f, 1.f, 1.f);
|
|
glTexCoord2f(0.f, 0.f); glVertex2f(x, y);
|
|
glTexCoord2f(1.f, 0.f); glVertex2f(x+w, y);
|
|
glTexCoord2f(0.f, 1.f); glVertex2f(x, y+h);
|
|
glTexCoord2f(1.f, 1.f); glVertex2f(x+w, y+h);
|
|
glEnd();
|
|
}
|
|
}
|
|
|
|
|
|
QMatrix4x4 getGLMatrix(GLenum matrix) {
|
|
GLfloat gm[16];
|
|
glGetFloatv(matrix, gm);
|
|
float qm[16];
|
|
for (int i = 0; i < 16; ++i)
|
|
qm[i] = gm[i];
|
|
return QMatrix4x4(qm).transposed();
|
|
}
|
|
|
|
|
|
void setGLMatrix(QMatrix4x4 matrix) {
|
|
GLfloat gm[16];
|
|
float qm[16];
|
|
matrix.transposed().copyDataTo(qm);
|
|
for (int i = 0; i < 16; ++i)
|
|
gm[i] = qm[i];
|
|
glLoadMatrixf(gm);
|
|
}
|
|
|
|
|
|
void qglMultMatrix(const QMatrix4x4 & m) {
|
|
GLfloat gm[16];
|
|
float qm[16];
|
|
m.transposed().copyDataTo(qm);
|
|
for (int i = 0; i < 16; ++i)
|
|
gm[i] = qm[i];
|
|
glMultMatrixf(gm);
|
|
}
|
|
|
|
|
|
void createGLTexture(GLuint & tex, int width, int height, const GLenum & format, const GLenum & target) {
|
|
glClearError();
|
|
if (tex == 0) {
|
|
glGenTextures(1, &tex);
|
|
glBindTexture(target, tex);
|
|
}
|
|
//glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_COMPONENT16 || format == GL_DEPTH_COMPONENT24 || format == GL_DEPTH_COMPONENT32)
|
|
glTexImage2D(target, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, nullptr);
|
|
else {
|
|
int t = GL_UNSIGNED_BYTE;
|
|
int f = GL_RGBA;
|
|
if (format == GL_RGB32F || format == GL_RGB16F || format == GL_RGBA32F || format == GL_RGBA16F)
|
|
t = GL_FLOAT;
|
|
if (format == GL_RGB32F || format == GL_RGB16F || format == GL_RGB8 || format == GL_RGB)
|
|
f = GL_RGB;
|
|
glTexImage2D(target, 0, format, width, height, 0, f, t, nullptr);
|
|
//glGenerateMipmap(target);
|
|
//qDebug() << "glTexImage2D" << width << height << QString::number(t, 16);
|
|
}
|
|
//qDebug() << QString::number(glGetError(), 16);
|
|
}
|
|
|
|
|
|
void createGLTexture(GLuint & tex, const QImage & image, const GLenum & format, const GLenum & target) {
|
|
if (tex == 0) {
|
|
glGenTextures(1, &tex);
|
|
}
|
|
glBindTexture(target, tex);
|
|
QImage im = image.convertToFormat(QImage::Format_ARGB32).mirrored(false, true);///__GLWidget__::convertToGLFormat(image);
|
|
//const QImage & cim(im);
|
|
//glClearError();
|
|
//glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
if (target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_3D) {
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT);
|
|
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(target, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
|
|
}
|
|
glTexImage2D(target, 0, format, im.width(), im.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, im.bits());
|
|
//qDebug() << tex << im.width() << im.height() << im.bits() << glGetError();
|
|
}
|
|
|
|
|
|
QMatrix4x4 glMatrixPerspective(float angle, float aspect, float near_, float far_) {
|
|
QMatrix4x4 ret;
|
|
float t = 1.f / (tanf(angle * deg2rad / 2.f)), e = 2.4e-7f;
|
|
ret(0, 0) = t / aspect;
|
|
ret(1, 1) = t;
|
|
ret(2, 2) = e - 1.f;//far_ / (far_ - near_) - 1.;
|
|
ret(2, 3) = (e - 2.f) * near_;//2. * far_ * near_ / (far_ - near_);
|
|
ret(3, 2) = -1.f;
|
|
ret(3, 3) = 0.f;
|
|
return ret;
|
|
}
|
|
|
|
|
|
QImage rotateQImageLeft(const QImage & im) {
|
|
QImage ri(im.height(), im.width(), im.format());
|
|
QPainter p(&ri);
|
|
p.rotate(90);
|
|
p.drawImage(0, -im.height(), im);
|
|
p.end();
|
|
return ri;
|
|
}
|
|
|
|
|
|
QImage rotateQImageRight(const QImage & im) {
|
|
QImage ri(im.height(), im.width(), im.format());
|
|
QPainter p(&ri);
|
|
p.rotate(-90);
|
|
p.drawImage(-im.width(), 0, im);
|
|
p.end();
|
|
return ri;
|
|
}
|
|
|
|
|
|
|
|
|
|
QColor colorFromString(const QString & str) {
|
|
QString s = str.trimmed();
|
|
int i = s.indexOf("\t");
|
|
float r, g, b;
|
|
r = s.left(i).toFloat(); s = s.right(s.length() - i - 1); i = s.indexOf("\t");
|
|
g = s.left(i).toFloat(); s = s.right(s.length() - i - 1);
|
|
b = s.toFloat();
|
|
return QColor(r * 255.f, g * 255.f, b * 255.f);
|
|
}
|
|
|
|
|
|
QVector3D orthToVector(const QVector3D & v, const float & scale) {
|
|
if (v.isNull()) return QVector3D();
|
|
QVector3D rv, fn, sn;
|
|
if (v.x() != 0.f) rv.setZ(1.);
|
|
else if (v.y() != 0.f) rv.setX(1.);
|
|
else rv.setY(1.);
|
|
fn = QVector3D::crossProduct(v, rv).normalized();
|
|
sn = QVector3D::crossProduct(v, fn).normalized();
|
|
return fn * urand(scale) + sn * urand(scale);
|
|
}
|
|
|
|
|
|
QVector3D rotateVector(const QVector3D & v, const QVector3D & a) {
|
|
QMatrix4x4 m;
|
|
m.rotate(a.z(), 0., 0., 1.);
|
|
m.rotate(a.y(), 0., 1., 0.);
|
|
m.rotate(a.x(), 1., 0., 0.);
|
|
return m * v;
|
|
}
|
|
|
|
|
|
void setVectorLength(QVector3D & v, const float & l) {
|
|
float vl = v.length();
|
|
if (vl == 0.f) return;
|
|
float c = l / vl;
|
|
v *= c;
|
|
}
|
|
|
|
|
|
void lengthenVector(QVector3D & v, const float & l) {
|
|
float vl = v.length();
|
|
if (l == 0.f || vl == 0.f) return;
|
|
float c = 1.f + l / vl;
|
|
v *= c;
|
|
}
|
|
|
|
|
|
Vector3d::Vector3d(const QString & str) {
|
|
QString s = str.trimmed();
|
|
int i = s.indexOf("\t");
|
|
x = s.left(i).toFloat(); s = s.right(s.length() - i - 1); i = s.indexOf("\t");
|
|
y = s.left(i).toFloat(); s = s.right(s.length() - i - 1);
|
|
z = s.toFloat();
|
|
}
|
|
|
|
|
|
Vector3i::Vector3i(const QString & str) {
|
|
QString s = str.trimmed();
|
|
int i = s.indexOf("\t");
|
|
p0 = s.left(i).toInt(); s = s.right(s.length() - i - 1); i = s.indexOf("\t");
|
|
p1 = s.left(i).toInt(); s = s.right(s.length() - i - 1);
|
|
p2 = s.toInt();
|
|
}
|
|
|
|
|
|
void glEnableDepth() {
|
|
glEnable(GL_DEPTH_TEST);
|
|
//glDepthFunc(GL_GREATER);
|
|
glDepthFunc(GL_LESS);
|
|
glDepthMask(GL_TRUE);
|
|
}
|
|
|
|
|
|
void glDisableDepth() {
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
}
|
|
|
|
|
|
void glClearFramebuffer(const QColor & color, bool depth) {
|
|
glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
|
|
//glClearDepth(0.);
|
|
if (depth)
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
else
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
|
|
|
|
|
|
QGLViewBase::QGLViewBase() {
|
|
camera_ = new Camera();
|
|
textures_manager = new GLTextureManager();
|
|
}
|
|
|
|
|
|
QGLViewBase::~QGLViewBase() {
|
|
delete textures_manager;
|
|
}
|
|
|
|
|
|
Camera * QGLViewBase::camera() {
|
|
return camera_;
|
|
}
|
|
|
|
|
|
const Camera * QGLViewBase::camera() const {
|
|
return camera_;
|
|
}
|
|
|
|
|
|
void QGLViewBase::setCamera(Camera * camera) {
|
|
camera_ = camera;
|
|
}
|
|
|
|
|
|
GLTextureManagerBase * QGLViewBase::textureManager() {
|
|
return textures_manager;
|
|
}
|
|
|
|
|
|
Box3D::Box3D(const QVector<QVector3D> & points) {
|
|
x = y = z = width = length = height = angle_z = angle_xy = angle_roll = 0.f;
|
|
if (points.isEmpty()) return;
|
|
float ix, iy, iz, ax, ay, az;
|
|
ix = ax = points[0].x();
|
|
iy = ay = points[0].y();
|
|
iz = az = points[0].z();
|
|
for (int i = 1; i < points.size(); ++i) {
|
|
ix = qMin<float>(ix, points[i].x()); ax = qMax<float>(ax, points[i].x());
|
|
iy = qMin<float>(iy, points[i].y()); ay = qMax<float>(ay, points[i].y());
|
|
iz = qMin<float>(iz, points[i].z()); az = qMax<float>(az, points[i].z());
|
|
}
|
|
x = ix;
|
|
y = iy;
|
|
z = iz;
|
|
length = ax - ix;
|
|
width = ay - iy;
|
|
height = az - iz;
|
|
}
|
|
|
|
|
|
QVector<QVector3D> Box3D::corners() const {
|
|
QVector<QVector3D> ret;
|
|
ret << QVector3D(x, y, z) << QVector3D(x, y + width, z) << QVector3D(x, y, z + height) << QVector3D(x, y + width, z + height)
|
|
<< QVector3D(x + length, y, z) << QVector3D(x + length, y + width, z)
|
|
<< QVector3D(x + length, y, z + height) << QVector3D(x + length, y + width, z + height);
|
|
return ret;
|
|
}
|
|
|
|
|
|
Box3D & Box3D::operator |=(const Box3D & o) {
|
|
if (isEmpty()) *this = o;
|
|
else {
|
|
GLfloat mx = x + length, my = y + width, mz = z + height;
|
|
GLfloat omx = o.x + o.length, omy = o.y + o.width, omz = o.z + o.height;
|
|
x = qMin(x, o.x); y = qMin(y, o.y); z = qMin(z, o.z);
|
|
mx = qMax(mx, omx); my = qMax(my, omy); mz = qMax(mz, omz);
|
|
length = mx - x; width = my - y; height = mz - z;
|
|
}
|
|
return *this;
|
|
}
|