452 lines
12 KiB
C++
452 lines
12 KiB
C++
/*
|
|
QGLView
|
|
Copyright (C) 2012 Ivan Pelipenko peri4ko@gmail.com
|
|
|
|
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 "gltypes.h"
|
|
#include "qglview.h"
|
|
|
|
QGLWidget * currentQGLView;
|
|
Camera * currentCamera;
|
|
QMatrix4x4 globCameraMatrix;
|
|
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(GLfloat x, GLfloat y, GLfloat w, GLfloat h) {
|
|
glResetAllTransforms();
|
|
glSetPolygonMode(GL_FILL);
|
|
glBegin(GL_QUADS);
|
|
glColor3f(1.f, 1.f, 1.f);
|
|
glTexCoord2f(0.f, 0.f); glVertex2f(x, y);
|
|
glTexCoord2f(1.f, 0.f); glVertex2f(x + w, y);
|
|
glTexCoord2f(1.f, 1.f); glVertex2f(x + w, y + h);
|
|
glTexCoord2f(0.f, 1.f); glVertex2f(x, y + h);
|
|
glEnd();
|
|
}
|
|
|
|
|
|
QMatrix4x4 getGLMatrix(GLenum matrix) {
|
|
GLfloat gm[16];
|
|
glGetFloatv(matrix, gm);
|
|
qreal qm[16];
|
|
for (int i = 0; i < 16; ++i)
|
|
qm[i] = gm[i];
|
|
return QMatrix4x4(qm).transposed();
|
|
}
|
|
|
|
|
|
void setGLMatrix(QMatrix4x4 matrix) {
|
|
GLfloat gm[16];
|
|
qreal qm[16];
|
|
matrix.transposed().copyDataTo(qm);
|
|
for (int i = 0; i < 16; ++i)
|
|
gm[i] = qm[i];
|
|
glLoadMatrixf(gm);
|
|
}
|
|
|
|
|
|
void qglMultMatrix(const QMatrix4x4 & m) {
|
|
GLdouble gm[16];
|
|
qreal qm[16];
|
|
m.transposed().copyDataTo(qm);
|
|
for (int i = 0; i < 16; ++i)
|
|
gm[i] = qm[i];
|
|
glMultMatrixd(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, 0);
|
|
else {
|
|
int t = GL_UNSIGNED_BYTE;
|
|
if (format == GL_RGB32F || format == GL_RGB16F)
|
|
t = GL_FLOAT;
|
|
glTexImage2D(target, 0, format, width, height, 0, GL_RGBA, t, 0);
|
|
}
|
|
//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 = QGLWidget::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_RGBA, GL_UNSIGNED_BYTE, im.bits());
|
|
//qDebug() << tex << im.width() << im.height() << im.bits() << glGetError();
|
|
}
|
|
|
|
|
|
QMatrix4x4 glMatrixPerspective(double angle, double aspect, double near_, double far_) {
|
|
QMatrix4x4 ret;
|
|
double t = 1. / (tan(angle * deg2rad / 2.));
|
|
ret(0, 0) = t / aspect;
|
|
ret(1, 1) = t;
|
|
ret(2, 2) = far_ / (far_ - near_) - 1.;
|
|
ret(2, 3) = 2. * far_ * near_ / (far_ - near_);
|
|
ret(3, 2) = -1;
|
|
ret(3, 3) = 0.;
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
void Camera::anglesFromPoints() {
|
|
QVector3D dv = aim_ - pos_, tv;
|
|
tv = QVector3D(dv.x(), dv.y(), 0.);
|
|
angle_z = atan2(tv.x(), tv.y()) * rad2deg;
|
|
angle_xy = piClamp<GLdouble>(atan2(tv.length(), dv.z()) * rad2deg + 180., angle_limit_lower_xy, angle_limit_upper_xy);
|
|
}
|
|
|
|
|
|
void Camera::apply(const GLdouble & aspect) {
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
if (aspect <= 1.)
|
|
glScaled(aspect, aspect, 1.);
|
|
QMatrix4x4 pm;// = glMatrixPerspective(fov_, aspect, depth_start, depth_end);
|
|
pm.perspective(fov_, aspect, depth_start, depth_end);
|
|
//qDebug() << pm << glMatrixPerspective(fov_, aspect, depth_start, depth_end);;
|
|
setGLMatrix(pm);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
glTranslated(0., 0., -distance());
|
|
glRotated(angle_xy, 1., 0., 0.);
|
|
glRotated(angle_roll, 0., -1., 0.);
|
|
glRotated(angle_z, 0., 0., 1.);
|
|
//glTranslated(pos_.x(), pos_.y(), pos_.z());
|
|
//if (mirror_y) glScalef(1,-1,-1);
|
|
//if (mirror_x) glScalef(-1,1,-1);
|
|
//glScalef(-1,-1,1);
|
|
glTranslated(-aim_.x(), -aim_.y(), -aim_.z());
|
|
glGetIntegerv(GL_VIEWPORT, viewport);
|
|
glGetDoublev(GL_PROJECTION_MATRIX, projection);
|
|
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
|
|
//qDebug() << "viewport" << viewport[0] << viewport[1] << viewport[2] << viewport[3];
|
|
}
|
|
|
|
|
|
void Camera::panZ(const double & a) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = QVector2D(dv.x(), dv.y()).length();
|
|
angle_z += a;
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl, cos(angle_z * deg2rad) * tl, dv.z());
|
|
aim_ = pos_ + dv;
|
|
}
|
|
|
|
|
|
void Camera::panXY(const double & a) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = dv.length(), tc;
|
|
angle_xy += a;
|
|
angle_xy = piClamp<GLdouble>(angle_xy, angle_limit_lower_xy, angle_limit_upper_xy);
|
|
tc = -sin(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl * tc, cos(angle_z * deg2rad) * tl * tc, -cos(angle_xy * deg2rad) * tl);
|
|
aim_ = pos_ + dv;
|
|
}
|
|
|
|
|
|
void Camera::rotateZ(const double & a) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = QVector2D(dv.x(), dv.y()).length();
|
|
angle_z += a;
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl, cos(angle_z * deg2rad) * tl, dv.z());
|
|
aim_ = pos_ + dv;
|
|
}
|
|
|
|
|
|
void Camera::rotateXY(const double & a) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = dv.length(), tc;
|
|
angle_xy += a;
|
|
angle_xy = piClamp<GLdouble>(angle_xy, angle_limit_lower_xy, angle_limit_upper_xy);
|
|
tc = -sin(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl * tc, cos(angle_z * deg2rad) * tl * tc, -cos(angle_xy * deg2rad) * tl);
|
|
aim_ = pos_ + dv;
|
|
}
|
|
|
|
|
|
void Camera::orbitZ(const double & a) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = QVector2D(dv.x(), dv.y()).length();
|
|
angle_z += a;
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl, cos(angle_z * deg2rad) * tl, dv.z());
|
|
pos_ = aim_ - dv;
|
|
}
|
|
|
|
|
|
void Camera::orbitXY(const double & a) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = dv.length(), tc;
|
|
angle_xy += a;
|
|
angle_xy = piClamp<GLdouble>(angle_xy, angle_limit_lower_xy, angle_limit_upper_xy);
|
|
tc = -sin(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl * tc, cos(angle_z * deg2rad) * tl * tc, -cos(angle_xy * deg2rad) * tl);
|
|
pos_ = aim_ - dv;
|
|
}
|
|
|
|
|
|
void Camera::setAngleZ(const double & a) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = QVector2D(dv.x(), dv.y()).length();
|
|
angle_z = a;
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl, cos(angle_z * deg2rad) * tl, dv.z());
|
|
aim_ = pos_ + dv;
|
|
}
|
|
|
|
|
|
void Camera::setAngleXY(const double & a) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = dv.length(), tc;
|
|
angle_xy = a;
|
|
tc = -sin(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl * tc, cos(angle_z * deg2rad) * tl * tc, -cos(angle_xy * deg2rad) * tl);
|
|
//pos_ = aim_ - dv;
|
|
aim_ = pos_ + dv;
|
|
//anglesFromPoints();
|
|
}
|
|
|
|
|
|
void Camera::moveForward(const double & x, bool withZ) {
|
|
QVector3D dv;// = aim_ - pos_;
|
|
double tc = -sin(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tc, cos(angle_z * deg2rad) * tc, 0.);
|
|
if (withZ) dv.setZ(-cos(angle_xy * deg2rad));
|
|
dv.normalize();
|
|
dv *= x;
|
|
pos_ += dv;
|
|
aim_ += dv;
|
|
}
|
|
|
|
|
|
void Camera::moveLeft(const double & x, bool withZ) {
|
|
QVector3D dv;// = aim_ - pos_;
|
|
double tc = -sin(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad - M_PI_2) * tc, cos(angle_z * deg2rad - M_PI_2) * tc, 0.);
|
|
if (withZ) dv.setZ(-sin(angle_roll * deg2rad));
|
|
dv.normalize();
|
|
dv *= x;
|
|
pos_ += dv;
|
|
aim_ += dv;
|
|
}
|
|
|
|
|
|
void Camera::moveUp(const double & x, bool onlyZ) {
|
|
QVector3D dv;
|
|
if (onlyZ)
|
|
dv = QVector3D(0., 0., x);
|
|
else {
|
|
double tc = cos(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tc, cos(angle_z * deg2rad) * tc, -sin(angle_xy * deg2rad));
|
|
dv.normalize();
|
|
dv *= x;
|
|
}
|
|
pos_ += dv;
|
|
aim_ += dv;
|
|
}
|
|
|
|
|
|
void Camera::flyCloser(const double & s) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = dv.length() / (1. + s), tc = -sin(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl * tc, cos(angle_z * deg2rad) * tl * tc, -cos(angle_xy * deg2rad) * tl);
|
|
pos_ = aim_ - dv;
|
|
}
|
|
|
|
|
|
void Camera::flyFarer(const double & s) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tl = dv.length() * (1. + s), tc = -sin(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad) * tl * tc, cos(angle_z * deg2rad) * tl * tc, -cos(angle_xy * deg2rad) * tl);
|
|
pos_ = aim_ - dv;
|
|
}
|
|
|
|
|
|
void Camera::flyToDistance(const double & d) {
|
|
QVector3D dv = aim_ - pos_;
|
|
double tc = -sin(angle_xy * deg2rad);
|
|
dv = QVector3D(sin(angle_z * deg2rad) * d * tc, cos(angle_z * deg2rad) * d * tc, -cos(angle_xy * deg2rad) * d);
|
|
pos_ = aim_ - dv;
|
|
}
|
|
|
|
|
|
QVector3D Camera::pointFromViewport(int x_, int y_, double z_) {
|
|
GLsizei mx = x_, my = viewport[3] - y_, mz = z_;
|
|
GLdouble x,y,z;
|
|
gluUnProject(mx, my, mz, modelview, projection, viewport, &x, &y, &z);
|
|
return QVector3D(x,y,z);
|
|
}
|
|
|
|
|
|
|
|
|
|
QColor colorFromString(const QString & str) {
|
|
QString s = str.trimmed();
|
|
int i = s.indexOf("\t");
|
|
double 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., g * 255., b * 255.);
|
|
}
|
|
|
|
|
|
QVector3D orthToVector(const QVector3D & v, const double & scale) {
|
|
if (v.isNull()) return QVector3D();
|
|
QVector3D rv, fn, sn;
|
|
if (v.x() != 0) rv.setZ(1.);
|
|
else if (v.y() != 0) 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 double & l) {
|
|
double vl = v.length();
|
|
if (vl == 0.) return;
|
|
double c = l / vl;
|
|
v *= c;
|
|
}
|
|
|
|
|
|
void lengthenVector(QVector3D & v, const double & l) {
|
|
double vl = v.length();
|
|
if (l == 0. || vl == 0.) return;
|
|
double c = 1. + 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(1.);
|
|
if (depth)
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
else
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|