git-svn-id: svn://db.shs.com.ru/libs@637 a8b55f48-bf90-11e4-a774-851b48703e85

This commit is contained in:
2019-12-01 16:52:10 +00:00
parent 09298fadcd
commit 385070f70f
33 changed files with 226 additions and 83 deletions

View File

@@ -0,0 +1,87 @@
/*
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/>.
*/
#define GL_GLEXT_PROTOTYPES
#include <QOpenGLExtraFunctions>
#include "glbuffer.h"
Buffer::Buffer(GLenum target, GLenum _usage) {
target_ = target;
usage_ = _usage;
buffer_ = 0;
prev_size = 0;
}
Buffer::~Buffer() {
}
void Buffer::init(QOpenGLExtraFunctions * f) {
if (!isInit()) {
f->glGenBuffers(1, &buffer_);
}
}
void Buffer::destroy(QOpenGLExtraFunctions * f) {
if (buffer_ != 0) {
f->glDeleteBuffers(1, &buffer_);
}
buffer_ = 0;
}
void Buffer::bind(QOpenGLExtraFunctions * f) {
f->glBindBuffer(target_, buffer_);
}
void Buffer::release(QOpenGLExtraFunctions * f) {
f->glBindBuffer(target_, 0);
}
void * Buffer::map(QOpenGLExtraFunctions * f, GLbitfield mode, int size) {
if (size < 0) size = prev_size;
return f->glMapBufferRange(target_, 0, size, mode);
}
void Buffer::unmap(QOpenGLExtraFunctions * f) {
f->glUnmapBuffer(target_);
}
bool Buffer::resize(QOpenGLExtraFunctions * f, int new_size) {
if (new_size <= 0) return false;
//qDebug() << "check resize buffer" << buffer_ << "bytes" << new_size << ", old =" << prev_size;
if (new_size <= prev_size) return false;
prev_size = new_size;
//qDebug() << "resize buffer " << buffer_ << target_ << "for" << new_size << "bytes";
f->glBufferData(target_, new_size, 0, usage_);
return true;
}
void Buffer::load(QOpenGLExtraFunctions * f, const void * data, int size, int offset) {
if (!data || size <= 0) return;
//qDebug() << "load buffer" << buffer_ << "bytes" << size;
f->glBufferSubData(target_, offset, size, data);
}

56
qglengine/core/glbuffer.h Normal file
View File

@@ -0,0 +1,56 @@
/*
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/>.
*/
#ifndef GLBUFFER_H
#define GLBUFFER_H
#include "gltypes.h"
class Buffer
{
friend class ObjectBase;
public:
Buffer(GLenum target, GLenum usage = GL_DYNAMIC_DRAW);
~Buffer();
void init (QOpenGLExtraFunctions * f);
void destroy (QOpenGLExtraFunctions * f);
void bind (QOpenGLExtraFunctions * f);
void release (QOpenGLExtraFunctions * f);
void * map (QOpenGLExtraFunctions * f, GLbitfield mode, int size = -1);
void unmap (QOpenGLExtraFunctions * f);
// returns true if size changed
bool resize (QOpenGLExtraFunctions * f, int new_size);
void load (QOpenGLExtraFunctions * f, const void * data, int size, int offset = 0);
GLuint ID() const {return buffer_;}
GLenum usage() const {return usage_;}
bool isInit() const {return buffer_ != 0;}
private:
GLenum target_, usage_;
GLuint buffer_;
int prev_size;
};
#endif // GLBUFFER_H

View File

@@ -0,0 +1,309 @@
/*
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 <QOpenGLExtraFunctions>
#include "glframebuffer.h"
#include <QTime>
Framebuffer::Framebuffer(QOpenGLExtraFunctions * f_, int colorAttachments_, bool withDepth, GLenum colorFormat_, GLenum _target): f(f_),
pbo(GL_PIXEL_PACK_BUFFER, GL_STREAM_DRAW) {
is_depth = withDepth;
target_ = _target;
color_formats.fill(colorFormat_, colorAttachments_);
colors.fill(0, colorAttachments_);
fbo = drbo = 0;
tex_d = 0;
wid = hei = 0;
pbo_queried = 0;
is_changed = false;
}
Framebuffer::Framebuffer(QOpenGLExtraFunctions * f_, QVector<GLenum> colors_, bool withDepth, GLenum _target): f(f_),
pbo(GL_PIXEL_PACK_BUFFER, GL_STREAM_DRAW) {
is_depth = withDepth;
target_ = _target;
color_formats = colors_;
colors.fill(0, colors_.size());
fbo = drbo = 0;
tex_d = 0;
wid = hei = 0;
pbo_queried = 0;
is_changed = false;
}
Framebuffer::~Framebuffer() {
deleteGLFramebuffer(fbo);
deleteGLRenderbuffer(drbo);
for (int i = 0; i < colors.size(); ++i)
deleteGLTexture(f, colors[i]);
deleteGLTexture(f, tex_d);
}
void Framebuffer::resize(int width, int height, bool force) {
if ((wid == width) && (hei == height) && !force) return;
wid = width;
hei = height;
deleteGLFramebuffer(fbo);
f->glGenFramebuffers(1, &fbo);
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
for (int i = 0; i < colors.size(); ++i) {
deleteGLTexture(f, colors[i]);
createGLTexture(f, colors[i], width, height, color_formats[i], target_);
f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
f->glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, 4);
//f->glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target_, colors[i], 0);
}
if (is_depth) {
deleteGLTexture(f, tex_d);
deleteGLRenderbuffer(drbo);
f->glGenRenderbuffers(1, &drbo);
f->glBindRenderbuffer(GL_RENDERBUFFER, drbo);
f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, drbo);
createGLTexture(f, tex_d, width, height, GL_DEPTH_COMPONENT);
f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//f->glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE);
f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, target_, tex_d, 0);
}
f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (pbo.isInit()) {
pbo.bind(f);
pbo.resize(f, width*height*4);
pbo.release(f);
}
is_changed = false;
}
QImage Framebuffer::grab() const {
//glReadPixels(0, 0, wid, hei, GL_RGBA, );
//QImage ret();
return QImage();
}
void Framebuffer::queryPoint(int index, QPoint p) {
pbo_queried = 0;
if (index < 0 || index >= colors.size()) return;
if (!rect().contains(p) || !pbo.isInit()) return;
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
//QTime tm; tm.restart();
pbo.bind(f);
f->glReadPixels(p.x(), height() - p.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0);
pbo_queried = 1;
pbo.release(f);
//qDebug() << tm.elapsed();
}
void Framebuffer::queryPoints(int index, QRect rect_) {
pbo_queried = 0;
if (index < 0 || index >= colors.size()) return;
rect_ &= rect();
if (rect_.isEmpty() || !pbo.isInit()) return;
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
//QTime tm; tm.restart();
pbo.bind(f);
f->glReadPixels(rect_.x(), height() - rect_.bottom(), rect_.width(), rect_.height(), GL_RGBA, GL_UNSIGNED_BYTE, 0);
pbo_queried = rect_.width() * rect_.height();
pbo.release(f);
//qDebug() << tm.elapsed();
}
void Framebuffer::queryImage(int index) {
queryPoints(index, rect());
}
uint Framebuffer::getPoint() const {
if (!pbo.isInit() || (pbo_queried == 0)) return 0;
//QTime tm; tm.restart();
uint ret = 0;
pbo.bind(f);
//glClearError();
void * map = pbo.map(f, GL_MAP_READ_BIT, sizeof(uint));
//qDebug() << map << QString::number(glGetError(), 16);
if (map)
memcpy(&ret, map, sizeof(uint));
pbo.unmap(f);
pbo.release(f);
//qDebug() << tm.elapsed();
return ret;
}
QVector<uint> Framebuffer::getPoints() const {
QVector<uint> ret;
if (!pbo.isInit() || (pbo_queried == 0)) return ret;
ret.resize(pbo_queried);
//QTime tm; tm.restart();
pbo.bind(f);
//glClearError();
void * map = pbo.map(f, GL_MAP_READ_BIT, pbo_queried * sizeof(uint));
//qDebug() << map << QString::number(glGetError(), 16);
if (map)
memcpy(ret.data(), map, pbo_queried * sizeof(uint));
pbo.unmap(f);
pbo.release(f);
//qDebug() << tm.elapsed();
return ret;
}
QImage Framebuffer::getImage() const {
QImage ret;
if (!pbo.isInit() || (pbo_queried == 0)) return ret;
ret = QImage(size(), QImage::Format_RGBA8888);
int bytes = width() * height() * 4;
//QTime tm; tm.restart();
pbo.bind(f);
//glClearError();
void * map = pbo.map(f, GL_MAP_READ_BIT, bytes);
//qDebug() << map << QString::number(glGetError(), 16);
if (map)
memcpy(ret.bits(), map, bytes);
pbo.unmap(f);
pbo.release(f);
//qDebug() << tm.elapsed();
return ret;
}
QVector<float> Framebuffer::grabF(int index) const {
QVector<float> ret;
if (index < 0 || index >= colors.size()) return ret;
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
ret.resize(wid * hei * 4);
f->glReadPixels(0, 0, wid, hei, GL_RGBA, GL_FLOAT, ret.data());
return ret;
}
void Framebuffer::blit(int index_from, GLuint fb_to, int index_to, QRect from, QRect to, GLbitfield mask, GLenum filter) {
if (index_from < 0 || index_from >= colors.size()) return;
GLenum e = GL_COLOR_ATTACHMENT0 + index_to;
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_to);
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index_from);
f->glDrawBuffers(1, &e);
f->glBlitFramebuffer(from.x(), from.y(), from.right(), from.bottom(), to.x(), to.y(), to.right(), to.bottom(), mask, filter);
}
void Framebuffer::bind() {
if (is_changed) resize(wid, hei);
if (fbo == 0) return;
//glFlush();
f->glGetIntegerv(GL_VIEWPORT, prev_view);
//glClearError();
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
//qDebug() << QString::number(glGetError(), 16);
setWriteBuffers();
f->glReadBuffer(GL_COLOR_ATTACHMENT0);
//f->glDrawBuffer(GL_COLOR_ATTACHMENT0);
f->glViewport(0, 0, wid, hei);
}
void Framebuffer::release() {
is_changed = false;
if (fbo == 0) return;
//glFlush();
f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
f->glViewport(prev_view[0], prev_view[1], prev_view[2], prev_view[3]);
}
void Framebuffer::setWriteBuffer(int index) {
//QVector<GLenum> buffers; buffers << GL_COLOR_ATTACHMENT0 + index;
GLenum e = GL_COLOR_ATTACHMENT0 + index;
f->glDrawBuffers(1, &e);
}
void Framebuffer::setWriteBuffers(int * indeces, int count) {
QVector<GLenum> buffers;
for (int i = 0; i < count; ++i)
buffers << GL_COLOR_ATTACHMENT0 + indeces[i];
f->glDrawBuffers(buffers.size(), buffers.constData());
}
void Framebuffer::setWriteBuffers() {
QVector<GLenum> buffers;
for (int i = 0; i < colors.size(); ++i)
buffers << GL_COLOR_ATTACHMENT0 + i;
f->glDrawBuffers(buffers.size(), buffers.constData());
}
void Framebuffer::enablePixelBuffer() {
pbo.init(f);
}
void Framebuffer::bindColorTexture(int index, int channel) {
if (index < 0 || index >= colors.size()) return;
f->glActiveTexture(GL_TEXTURE0 + channel);
f->glBindTexture(GL_TEXTURE_2D, colors[index]);
}
void Framebuffer::bindColorTextures() {
for (int i = colors.size() - 1; i >= 0; --i) {
f->glActiveTexture(GL_TEXTURE0 + i);
f->glBindTexture(GL_TEXTURE_2D, colors[i]);
//f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
}
void Framebuffer::bindDepthTexture(int channel) {
f->glActiveTexture(GL_TEXTURE0 + channel);
f->glBindTexture(GL_TEXTURE_2D, tex_d);
}
void Framebuffer::deleteGLRenderbuffer(GLuint & drbo) {
if (drbo != 0)
f->glDeleteRenderbuffers(1, &drbo);
drbo = 0;
}
void Framebuffer::deleteGLFramebuffer(GLuint & fbo) {
if (fbo != 0)
f->glDeleteFramebuffers(1, &fbo);
fbo = 0;
}

View File

@@ -0,0 +1,83 @@
/*
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/>.
*/
#ifndef GLFRAMEBUFFER_H
#define GLFRAMEBUFFER_H
#include "glbuffer.h"
class Framebuffer
{
public:
Framebuffer(QOpenGLExtraFunctions * f_, int colorAttachments = 1, bool withDepth = true, GLenum colorFormat = GL_RGBA8, GLenum _target = GL_TEXTURE_2D);
Framebuffer(QOpenGLExtraFunctions * f_, QVector<GLenum> colors_, bool withDepth = true, GLenum _target = GL_TEXTURE_2D);
virtual ~Framebuffer();
GLuint id() const {return fbo;}
GLuint colorTexture(int index = 0) const {return colors[index];}
//GLenum colorFormat() const {return color_format;}
GLuint depthTexture() const {return tex_d;}
GLenum target() const {return target_;}
int width() const {return wid;}
int height() const {return hei;}
QSize size() const {return QSize(wid, hei);}
QRect rect() const {return QRect(0, 0, wid, hei);}
QImage grab() const;
QVector<float> grabF(int index) const;
void queryPoint(int index, QPoint p);
void queryPoints(int index, QRect rect);
void queryImage(int index);
uint getPoint() const;
QVector<uint> getPoints() const;
QImage getImage() const;
int queriedPoints() const {return pbo_queried;}
void blit(int index_from, GLuint fb_to, int index_to, QRect from, QRect to, GLbitfield mask = GL_COLOR_BUFFER_BIT, GLenum filter = GL_NEAREST );
void resize(int width, int height, bool force = false);
void bind();
void release();
void setReadBuffer(int index) {glReadBuffer(GL_COLOR_ATTACHMENT0 + index);}
void setWriteBuffer(int index);
void setWriteBuffers(int * indeces, int count);
void setWriteBuffers();
//void setColorFormat(GLenum format) {color_format = format; is_changed = true;}
void enablePixelBuffer();
void copyDepthFrom(GLuint tex) {;}
void bindColorTexture(int index, int channel = 0);
void bindColorTextures();
void bindDepthTexture(int channel);
private:
void deleteGLRenderbuffer(GLuint & drbo);
void deleteGLFramebuffer(GLuint & fbo);
bool is_depth, is_changed;
int pbo_queried;
QOpenGLExtraFunctions * f;
mutable Buffer pbo;
QVector<GLuint> colors;
QVector<GLenum> color_formats;
GLenum target_;
GLuint fbo, drbo, tex_d;
GLint prev_view[4], wid, hei;
};
#endif // GLFRAMEBUFFER_H

View File

@@ -0,0 +1,200 @@
/*
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 "gltypes.h"
#include "gltexture_manager.h"
#include "qglview.h"
using namespace QGLEngineShaders;
/*
bool CubeTexture::create() {
//qDebug("create");
destroy();
glGenTextures(1, &id_);
glBindTexture(GL_TEXTURE_CUBE_MAP, id_);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
//glClearError();
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
//qDebug() << glGetError();
changed_ = false;
return id_ > 0;
}
void CubeTexture::load() {
if (isEmpty()) return;
create();
if (!path(0).isEmpty()) loadFront(path(0));
if (!path(1).isEmpty()) loadBack(path(1));
if (!path(2).isEmpty()) loadLeft(path(2));
if (!path(3).isEmpty()) loadRight(path(3));
if (!path(4).isEmpty()) loadTop(path(4));
if (!path(5).isEmpty()) loadBottom(path(5));
}
void CubeTexture::loadFromDirectory(const QString & dir) {
QDir d(dir); QFileInfoList sl;
sl = d.entryInfoList(QStringList("front.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadFront(sl[0].absoluteFilePath());
sl = d.entryInfoList(QStringList("back.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadBack(sl[0].absoluteFilePath());
sl = d.entryInfoList(QStringList("left.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadLeft(sl[0].absoluteFilePath());
sl = d.entryInfoList(QStringList("right.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadRight(sl[0].absoluteFilePath());
sl = d.entryInfoList(QStringList("top.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadTop(sl[0].absoluteFilePath());
sl = d.entryInfoList(QStringList("bottom.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadBottom(sl[0].absoluteFilePath());
}
void CubeTexture::loadPathesFromDirectory(const QString & dir) {
QDir d(dir); QFileInfoList sl;
sl = d.entryInfoList(QStringList("front.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[0] = sl[0].absoluteFilePath();
sl = d.entryInfoList(QStringList("back.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[1] = sl[0].absoluteFilePath();
sl = d.entryInfoList(QStringList("left.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[2] = sl[0].absoluteFilePath();
sl = d.entryInfoList(QStringList("right.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[3] = sl[0].absoluteFilePath();
sl = d.entryInfoList(QStringList("top.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[4] = sl[0].absoluteFilePath();
sl = d.entryInfoList(QStringList("bottom.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[5] = sl[0].absoluteFilePath();
}
*/
Map::Map() {
bitmap_id = 0;
color_amount = 1.f;
color_offset = 0.f;
bitmap_scale = QPointF(1., 1.);
_changed = true;
_layer = 0;
}
void Map::setBitmapPath(const QString & p) {
bitmap_path = p;
_changed = true;
}
void Map::load(TextureManager * tm) {
if (bitmap_id == 0)
bitmap_id = tm->loadTexture(bitmap_path, true, _type == mtNormal);
}
void Map::copyToQGLMap(QGLMap & m) const {
m.amount = color_amount;
m.offset = color_offset;
m.scale = QVector2D(bitmap_scale);
if (hasBitmap()) {
m.array_index = tarMaps;
m.map_index = _layer;
} else {
m.array_index = tarEmpty;
m.map_index = (_type == mtNormal ? emrBlue : emrWhite);
}
}
Material::Material(const QString _name)/*: map_reflection(512)*/ {
setTypes();
name = _name;
color_diffuse = color_specular = Qt::darkGray;
color_emission = Qt::black;
glass = false;
transparency = reflectivity = 0.f;
map_roughness.color_amount = 0.75f;
map_specular.color_amount = 1.f;
iof = 1.f;
dispersion = 0.05f;
_changed = true;
_index = 0;
}
uint Material::hash() {
return qHash(name);
}
bool Material::hasTransparency() const {
return float(color_diffuse.alphaF()) * (1.f - transparency) < 1.f;
}
bool Material::isMapsChanged() const {
return map_diffuse ._changed ||
map_normal ._changed ||
map_specular ._changed ||
map_roughness._changed ||
map_emission ._changed ||
map_relief ._changed;
}
bool Material::isMapChanged(int type) const {
switch (type) {
case mtDiffuse : return map_diffuse ._changed;
case mtNormal : return map_normal ._changed;
case mtSpecular : return map_specular ._changed;
case mtRoughness: return map_roughness._changed;
case mtEmission : return map_emission ._changed;
case mtRelief : return map_relief ._changed;
}
return false;
}
void Material::load(TextureManager * tm) {
map_diffuse .load(tm);
map_normal .load(tm);
map_specular .load(tm);
map_roughness.load(tm);
map_emission .load(tm);
map_relief .load(tm);
}
void Material::setMapsChanged() {
map_diffuse ._changed = true;
map_normal ._changed = true;
map_specular ._changed = true;
map_roughness._changed = true;
map_emission ._changed = true;
map_relief ._changed = true;
}
void Material::setTypes() {
map_diffuse ._type = mtDiffuse;
map_normal ._type = mtNormal;
map_specular ._type = mtSpecular;
map_roughness._type = mtRoughness;
map_emission ._type = mtEmission;
map_relief ._type = mtRelief;
}

173
qglengine/core/glmaterial.h Normal file
View File

@@ -0,0 +1,173 @@
/*
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/>.
*/
#ifndef GLMATERIAL_H
#define GLMATERIAL_H
#include "glshaders_types.h"
#include "chunkstream.h"
/*
class Texture {
public:
Texture(int _width, int _height, const GLenum & _format = GL_RGBA8, const GLenum & _target = GL_TEXTURE_2D) {wid = _width; hei = _height; format_ = _format; target_ = _target; id_ = 0;}
bool create() {destroy(); createGLTexture(id_, wid, hei, format_, target_); return id_ > 0;}
void destroy() {if (id_ > 0) glDeleteTextures(1, &id_); id_ = 0;}
void bind() {if (id_ > 0) glBindTexture(target_, id_);}
void release() {glBindTexture(target_, 0);}
int width() const {return wid;}
int height() const {return hei;}
GLenum format() const {return format_;}
GLenum target() const {return target_;}
GLuint id() const {return id_;}
private:
int wid, hei;
GLenum format_, target_;
GLuint id_;
};
class CubeTexture {
public:
CubeTexture(int _size, const GLenum & _format = GL_RGBA8) {size = _size; format_ = _format; id_ = 0; changed_ = false; pathes.resize(6);}
bool create();
void destroy() {if (id_ > 0) glDeleteTextures(1, &id_); id_ = 0;}
void bind() {if (changed_) {changed_ = false; create();} if (id_ > 0) glBindTexture(GL_TEXTURE_CUBE_MAP, id_);}
void release() {glBindTexture(GL_TEXTURE_CUBE_MAP, 0);}
void resize(int _size) {size = _size; changed_ = true;}
void loadFromDirectory(const QString & dir);
void loadFront(const QString & path) {bind(); pathes[0] = path; createGLTexture(id_, rotateQImageLeft(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_POSITIVE_X);}
void loadBack(const QString & path) {bind(); pathes[1] = path; createGLTexture(id_, rotateQImageRight(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_NEGATIVE_X);}
void loadLeft(const QString & path) {bind(); pathes[2] = path; createGLTexture(id_, QImage(path).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y);}
void loadRight(const QString & path) {bind(); pathes[3] = path; createGLTexture(id_, rotateQImage180(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_POSITIVE_Y);}
void loadTop(const QString & path) {bind(); pathes[4] = path; createGLTexture(id_, rotateQImageLeft(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);}
void loadBottom(const QString & path) {bind(); pathes[5] = path; createGLTexture(id_, rotateQImageLeft(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_POSITIVE_Z);}
void load();
bool isEmpty() const {foreach (const QString & i, pathes) if (!i.isEmpty()) return false; return true;}
GLenum format() const {return format_;}
void setFormat(GLenum f) {format_ = f; changed_ = true;}
GLuint id() const {return id_;}
const QString & path(int side) const {return pathes[side];}
void setPath(int side, const QString & p) {pathes[side] = p;}
void loadPathesFromDirectory(const QString & dir);
private:
bool changed_;
int size;
GLenum format_;
GLuint id_;
QVector<QString> pathes;
};
*/
class Map {
public:
Map();
void setBitmapPath(const QString & p);
void clearBitmap() {setBitmapPath(QString());}
bool hasBitmap() const {return !bitmap_path.isEmpty();}
void load(TextureManager * tm);
void copyToQGLMap(QGLEngineShaders::QGLMap & m) const;
QString bitmap_path;
GLuint bitmap_id;
QPointF bitmap_offset;
QPointF bitmap_scale;
float color_amount;
float color_offset;
bool _changed;
int _type, _layer;
};
class Material {
public:
Material(const QString _name = QString());
uint hash();
bool hasTransparency() const;
bool isMapsChanged() const;
bool isMapChanged(int type) const;
void load(TextureManager * tm);
void setMapsChanged();
void setTypes();
QString name;
QColor color_diffuse;
QColor color_specular;
QColor color_emission;
bool glass;
float transparency;
float reflectivity;
float iof;
float dispersion;
Map map_diffuse ;
Map map_normal ;
Map map_specular ;
Map map_roughness;
Map map_emission ;
Map map_relief ;
bool _changed;
int _index;
//GLCubeTexture map_reflection;
};
inline QDataStream & operator <<(QDataStream & s, const Map & m) {
ChunkStream cs;
cs.add(1, m.bitmap_path).add(2, m.color_amount).add(3, m.color_offset).add(6, m.bitmap_scale);
s << cs.data(); return s;
}
inline QDataStream & operator >>(QDataStream & s, Map & m) {
ChunkStream cs(s);
cs.readAll();
cs.get(1, m.bitmap_path).get(2, m.color_amount).get(3, m.color_offset).get(6, m.bitmap_scale);
return s;
}
inline QDataStream & operator <<(QDataStream & s, const Material * m) {
ChunkStream cs;
cs.add(1, m->name).add(2, m->color_diffuse).add(3, m->color_specular).add(4, m->color_emission)
.add(5, m->transparency).add(6, m->reflectivity).add(7, m->glass).add(8, m->map_diffuse).add(9, m->map_normal)
.add(10, m->map_relief).add(11, m->map_specular).add(12, m->map_roughness).add(13, m->map_emission);
s << /*qCompress*/(cs.data()); return s;
}
inline QDataStream & operator >>(QDataStream & s, Material *& m) {
m = new Material();
//QByteArray ba;
//s >> ba;
//ba = qUncompres(ba);
ChunkStream cs(s);
while (!cs.atEnd()) {
switch (cs.read()) {
case 1: cs.get(m->name); break;
case 2: cs.get(m->color_diffuse); break;
case 3: cs.get(m->color_specular); break;
case 4: cs.get(m->color_emission); break;
case 5: cs.get(m->transparency); break;
case 6: cs.get(m->reflectivity); break;
case 7: cs.get(m->glass); break;
case 8: cs.get(m->map_diffuse); break;
case 9: cs.get(m->map_normal); break;
case 10: cs.get(m->map_relief); break;
case 11: cs.get(m->map_specular); break;
case 12: cs.get(m->map_roughness); break;
case 13: cs.get(m->map_emission); break;
}
}
m->setTypes();
return s;
}
#endif // GLMATERIAL_H

456
qglengine/core/glmesh.cpp Normal file
View File

@@ -0,0 +1,456 @@
/*
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/>.
*/
#define GL_GLEXT_PROTOTYPES
#include <QOpenGLExtraFunctions>
#include "glmesh.h"
#include "globject.h"
#include <QTime>
using namespace QGLEngineShaders;
static int _count = 0;
Mesh::Mesh(GLenum geom_type_): geom_type(geom_type_),
buffer_geom(GL_ARRAY_BUFFER, GL_STATIC_DRAW),
buffer_ind (GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW),
buffer_obj (GL_ARRAY_BUFFER, GL_STREAM_DRAW),
buffer_sel (GL_ARRAY_BUFFER, GL_STREAM_DRAW) {
vao = 0;
hash_ = 0;
changed = hash_changed = objects_changed = selected_changed = true;
//qDebug() << "Mesh, now" << ++_count;
}
Mesh::~Mesh() {
//qDebug() << "~Mesh, now" << --_count;
//destroy();
}
Mesh * Mesh::clone() {
Mesh * c = new Mesh();
c->vertices_ = vertices_ ;
c->normals_ = normals_ ;
c->texcoords_ = texcoords_;
c->triangles_ = triangles_;
c->lines_ = lines_;
c->geom_type = geom_type;
c->hash_ = hash_;
c->hash_changed = hash_changed;
//qDebug() << "clone VBO";
return c;
}
void Mesh::init(QOpenGLExtraFunctions * f) {
if (!isInit()) {
buffer_geom.init(f);
buffer_ind .init(f);
buffer_obj .init(f);
buffer_sel .init(f);
f->glGenVertexArrays(1, &vao);
}
changed = true;
}
void Mesh::destroy(QOpenGLExtraFunctions * f) {
if (vao != 0) {
f->glDeleteVertexArrays(1, &vao);
buffer_geom.destroy(f);
buffer_ind .destroy(f);
buffer_obj .destroy(f);
buffer_sel .destroy(f);
}
vao = 0;
}
void Mesh::calculateNormals() {
normals_.resize(vertices_.size());
QVector3D dv1, dv2, n;
foreach (const Vector3i & t, triangles_) {
QVector3D & v0(vertices_[t.p0]);
QVector3D & v1(vertices_[t.p1]);
QVector3D & v2(vertices_[t.p2]);
dv1 = v1 - v0, dv2 = v2 - v0;
n = QVector3D::crossProduct(dv1, dv2).normalized();
normals_[t.p0] = n;
normals_[t.p1] = n;
normals_[t.p2] = n;
}
}
void Mesh::calculateTangents() {
if (vertices_.isEmpty() || texcoords_.isEmpty()) return;
if (texcoords_.size() != vertices_.size()) return;
tangents_ .resize(vertices_.size());
bitangents_.resize(vertices_.size());
//qDebug() << "calculateBinormals" << vcnt << tcnt << vertices_.size() << texcoords_.size() << "...";
QVector3D dv1, dv2;
QVector2D dt1, dt2;
QVector3D tan, bitan;
foreach (const Vector3i & t, triangles_) {
QVector3D & v0(vertices_ [t.p0]);
QVector3D & v1(vertices_ [t.p1]);
QVector3D & v2(vertices_ [t.p2]);
QVector2D & t0(texcoords_[t.p0]);
QVector2D & t1(texcoords_[t.p1]);
QVector2D & t2(texcoords_[t.p2]);
dv1 = v1 - v0, dv2 = v2 - v0;
dt1 = t1 - t0, dt2 = t2 - t0;
tan = (dv1 * dt2.y() - dv2 * dt1.y()).normalized();
bitan = (dv2 * dt1.x() - dv1 * dt2.x()).normalized();
tangents_ [t.p0] = tan;
tangents_ [t.p1] = tan;
tangents_ [t.p2] = tan;
bitangents_[t.p0] = bitan;
bitangents_[t.p1] = bitan;
bitangents_[t.p2] = bitan;
//qDebug() << " t" << t << vi << ti << dv1.toQVector3D() << "...";
}
//qDebug() << "calculateBinormals" << vcnt << tcnt << tangents_.size();
}
void Mesh::loadBuffer(QOpenGLExtraFunctions * f, Buffer & buf, const void * data, int size) {
if (!isInit()) init(f);
if (!buf.isInit() || !data) return;
buf.bind(f);
buf.resize(f, size);
buf.load(f, data, size);
//qDebug() << "loadBuffer" << size << "bytes";
/*void * map = buf.map(f, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
qDebug() << map;
if (map)
memcpy(map, objects_.constData(), osize);
buf.unmap(f);*/
}
bool Mesh::rebuffer(QOpenGLExtraFunctions * f) {
changed = false;
if (vertices_.isEmpty()) return true;
if (normals_.isEmpty())
calculateNormals();
calculateTangents();
vert_count = qMin(vertices_.size(), normals_.size());
vert_count = qMin(vert_count, tangents_.size());
vert_count = qMin(vert_count, bitangents_.size());
vert_count = qMin(vert_count, texcoords_.size());
data_.resize(vert_count);
for (int i = 0; i < vert_count; ++i) {
Vertex & v(data_[i]);
v.pos = vertices_ [i];
v.normal = normals_ [i];
v.tangent = tangents_ [i];
v.bitangent = bitangents_[i];
v.tex = texcoords_ [i];
}
int gsize = data_.size() * sizeof(Vertex);
int tsize = triangles_.size() * sizeof(Vector3i);
int lsize = lines_.size() * sizeof(Vector2i);
f->glBindVertexArray(vao);
buffer_geom.bind(f);
buffer_geom.resize(f, gsize);
buffer_geom.load(f, data_.constData(), gsize);
prepareDrawGeom(f);
buffer_ind.bind(f);
if (geom_type == GL_TRIANGLES) {
buffer_ind.resize(f, tsize);
buffer_ind.load(f, triangles_.constData(), tsize);
} else {
buffer_ind.resize(f, lsize);
buffer_ind.load(f, lines_.constData(), lsize);
}
buffer_obj.bind(f);
prepareDrawObj(f);
buffer_sel.bind(f);
prepareDrawSel(f);
f->glBindVertexArray(0);
return !isEmpty();
}
void Mesh::prepareDrawGeom(QOpenGLExtraFunctions * f) {
//qDebug() << "prepareDrawGeom";
f->glEnableVertexAttribArray(pos_loc );
f->glEnableVertexAttribArray(normal_loc );
f->glEnableVertexAttribArray(tangent_loc );
f->glEnableVertexAttribArray(bitangent_loc);
f->glEnableVertexAttribArray(tex_loc );
int size = sizeof(Vertex);
f->glVertexAttribPointer(pos_loc , 3, GL_FLOAT, GL_FALSE, size, (const void *)pos_offset );
f->glVertexAttribPointer(normal_loc , 3, GL_FLOAT, GL_FALSE, size, (const void *)normal_offset );
f->glVertexAttribPointer(tangent_loc , 3, GL_FLOAT, GL_FALSE, size, (const void *)tangent_offset );
f->glVertexAttribPointer(bitangent_loc, 3, GL_FLOAT, GL_FALSE, size, (const void *)bitangent_offset);
f->glVertexAttribPointer(tex_loc , 2, GL_FLOAT, GL_FALSE, size, (const void *)tex_offset );
}
void Mesh::prepareDrawObj(QOpenGLExtraFunctions * f) {
//qDebug() << "prepareDrawObj";
f->glEnableVertexAttribArray(material_loc );
f->glEnableVertexAttribArray(object_id_loc);
f->glEnableVertexAttribArray(color_loc );
for (int i = 0; i < 4; ++i) {
f->glEnableVertexAttribArray(modelmatrix_loc + i);
}
GLsizei size = sizeof(Object);
f->glVertexAttribIPointer(material_loc , 1, GL_UNSIGNED_INT , size, (const void *)material_offset );
f->glVertexAttribIPointer(object_id_loc, 1, GL_UNSIGNED_INT , size, (const void *)object_id_offset);
f->glVertexAttribPointer (color_loc , 4, GL_FLOAT, GL_FALSE, size, (const void *)color_offset );
for (int i = 0; i < 4; ++i) {
f->glVertexAttribPointer(modelmatrix_loc + i, 4, GL_FLOAT, GL_FALSE, size, (const void *)(modelmatrix_offset + sizeof(QVector4D)*i));
}
f->glVertexAttribDivisor(material_loc , 1);
f->glVertexAttribDivisor(object_id_loc, 1);
f->glVertexAttribDivisor(color_loc , 1);
for (int i = 0; i < 4; ++i) {
f->glVertexAttribDivisor(modelmatrix_loc + i, 1);
}
}
void Mesh::prepareDrawSel(QOpenGLExtraFunctions * f) {
//qDebug() << "prepareDrawObj";
f->glEnableVertexAttribArray(is_selected_loc);
GLsizei size = 1;
f->glVertexAttribIPointer(is_selected_loc, 1, GL_UNSIGNED_BYTE, size, (const void *)is_selected_offset);
f->glVertexAttribDivisor(is_selected_loc, 1);
}
void Mesh::draw(QOpenGLExtraFunctions * f, int count) {
if (isEmpty()) return;
if (!isInit()) init(f);
if (changed) rebuffer(f);
//qDebug() << "draw" << geom_type << vert_count << count;
f->glBindVertexArray(vao);
if (geom_type == GL_TRIANGLES)
f->glDrawElementsInstanced(geom_type, triangles_.size() * 3, GL_UNSIGNED_INT, 0, count);
else
f->glDrawElementsInstanced(geom_type, lines_.size() * 2, GL_UNSIGNED_INT, 0, count);
f->glBindVertexArray(0);
}
void Mesh::clear() {
vertices_.clear();
normals_.clear();
tangents_.clear();
bitangents_.clear();
texcoords_.clear();
triangles_.clear();
lines_.clear();
data_.clear();
changed = hash_changed = true;
}
void Mesh::loadObject(QOpenGLExtraFunctions * f, const Object & object) {
loadBuffer(f, buffer_obj, &object, sizeof(Object));
}
void Mesh::loadObjects(QOpenGLExtraFunctions * f, const QVector<Object> & objects) {
loadBuffer(f, buffer_obj, objects.constData(), objects.size() * sizeof(Object));
}
void Mesh::loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels) {
//qDebug() << "loadSelections" << sels;
loadBuffer(f, buffer_sel, sels.constData(), sels.size());
}
uint Mesh::hash() const {
if (hash_changed) {
hash_changed = false;
hash_ = qHashBits(vertices_ .constData(), vertices_ .size() * sizeof(QVector3D));
hash_ ^= qHashBits(normals_ .constData(), normals_ .size() * sizeof(QVector3D));
hash_ ^= qHashBits(texcoords_.constData(), texcoords_.size() * sizeof(QVector2D));
hash_ ^= qHashBits(triangles_.constData(), triangles_.size() * sizeof( Vector3i));
hash_ ^= qHashBits(lines_ .constData(), lines_ .size() * sizeof( Vector2i));
}
return hash_;
}
void Mesh::translatePoints(const QVector3D & dp) {
QMatrix4x4 m;
m.translate(dp);
transformPoints(m);
}
void Mesh::scalePoints(const QVector3D & dp) {
QMatrix4x4 m;
m.scale(dp);
transformPoints(m);
}
void Mesh::rotatePoints(const double & angle, const QVector3D & a) {
QMatrix4x4 m;
m.rotate(angle, a);
transformPoints(m);
}
void Mesh::transformPoints(const QMatrix4x4 & mat) {
if (vertices_.isEmpty()) return;
int vcnt = vertices_.size(), ncnt = normals_.size();
for (int i = 0; i < vcnt; ++i) {
vertices_[i] = (mat * QVector4D(vertices_[i], 1)).toVector3D();
if (i < ncnt)
normals_[i] = (mat * QVector4D(normals_[i], 0)).toVector3D();
}
changed = hash_changed = true;
}
void Mesh::append(const Mesh * m) {
if (!m) return;
if (m->isEmpty()) return;
if (normals_.isEmpty()) calculateNormals();
int vcnt = vertices_.size();
vertices_ .append(m->vertices_ );
normals_ .append(m->normals_ );
texcoords_.append(m->texcoords_);
QVector<Vector3i> tri = m->triangles_;
for (int i = 0; i < tri.size(); ++i)
tri[i] += vcnt;
triangles_.append(tri);
QVector<Vector2i> lin = m->lines_;
for (int i = 0; i < lin.size(); ++i)
lin[i] += vcnt;
lines_.append(lin);
}
bool Mesh::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_ << triangles_ << lines_;
ba = qCompress(ba);
f.resize(0);
f.write(ba);
f.close();
return true;
}
return false;
}
bool Mesh::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_ >> triangles_ >> lines_;
changed = hash_changed = true;
f.close();
return !isEmpty();
}
return false;
}
Box3D Mesh::boundingBox() const {
if (vertices_.isEmpty()) return Box3D();
int vcnt = vertices_.size();
//qDebug() << "calculateBinormals" << vcnt << tcnt << vertices_.size() << texcoords_.size() << "...";
GLfloat mix, miy, miz, max, may, maz;
QVector3D v0(vertices_[0]);
mix = max = v0.x();
miy = may = v0.y();
miz = maz = v0.z();
Box3D bound;
for (int i = 1; i < vcnt; ++i) {
const QVector3D & v(vertices_[i]);
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;
}
QDataStream & operator <<(QDataStream & s, const Mesh * m) {
ChunkStream cs;
//qDebug() << "place VBO" << m.vertices_.size() << m.normals_.size() << m.texcoords_.size() << m.colors_.size() << "...";
cs.add(1, m->vertices_).add(2, m->normals_).add(3, m->texcoords_)
.add(6, m->triangles_).add(7, m->lines_).add(10, int(m->geom_type));
//qDebug() << "place VBO done" << cs.data().size() << "...";
s << /*qCompress*/(cs.data()); return s;
}
QDataStream & operator >>(QDataStream & s, Mesh *& m) {
m = new Mesh();
//QByteArray ba;
//s >> ba;
//ba = qUncompress(ba);
ChunkStream cs(s);
while (!cs.atEnd()) {
switch (cs.read()) {
case 1 : cs.get(m->vertices_ ); break;
case 2 : cs.get(m->normals_ ); break;
case 3 : cs.get(m->texcoords_); break;
case 6 : cs.get(m->triangles_); break;
case 7 : cs.get(m->lines_ ); break;
case 10: m->geom_type = cs.getData<int>(); break;
}
}
m->changed = true;
return s;
}

106
qglengine/core/glmesh.h Normal file
View File

@@ -0,0 +1,106 @@
/*
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/>.
*/
#ifndef GLMESH_H
#define GLMESH_H
#include <chunkstream.h>
#include "glbuffer.h"
#include "glshaders_types.h"
class Mesh
{
friend class ObjectBase;
friend class Scene;
friend class Renderer;
friend QDataStream & operator <<(QDataStream & s, const Mesh * m);
friend QDataStream & operator >>(QDataStream & s, Mesh *& m);
public:
Mesh(GLenum geom_type_ = GL_TRIANGLES);
~Mesh();
Mesh * clone();
//GLVBO & operator =(const GLVBO & o) {return *this;}
void init (QOpenGLExtraFunctions * f);
void destroy (QOpenGLExtraFunctions * f);
bool rebuffer(QOpenGLExtraFunctions * f);
void draw (QOpenGLExtraFunctions * f, int count);
void clear();
void loadObject (QOpenGLExtraFunctions * f, const QGLEngineShaders::Object & object);
void loadObjects (QOpenGLExtraFunctions * f, const QVector<QGLEngineShaders::Object> & objects);
void loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels);
int verticesCount() const {return vertices_.size();}
int trianglesCount() const {return triangles_.size();}
int linesCount() const {return lines_.size();}
bool isInit() const {return vao != 0;}
bool isEmpty() const {return vertices_.isEmpty();}
uint hash() const;
QVector<QVector3D> & vertices () {changed = hash_changed = true; return vertices_;}
QVector<QVector3D> & normals () {changed = hash_changed = true; return normals_;}
QVector<QVector2D> & texcoords() {changed = hash_changed = true; return texcoords_;}
QVector< Vector3i> & indicesTriangles() {changed = hash_changed = true; return triangles_;}
QVector< Vector2i> & indicesLines () {changed = hash_changed = true; return lines_;}
void translatePoints(const QVector3D & dp);
void translatePoints(const double & x, const double & y, const double & z) {translatePoints(QVector3D(x, y, z));}
void scalePoints (const QVector3D & dp);
void scalePoints (const double & s) {scalePoints(QVector3D(s, s, s));}
void rotatePoints (const double & angle, const QVector3D & a);
void rotatePoints (const double & angle, const double & x, const double & y, const double & z) {rotatePoints(angle, QVector3D(x, y, z));}
void transformPoints(const QMatrix4x4 & mat);
void append(const Mesh * m);
bool saveToFile(const QString & filename);
bool loadFromFile(const QString & filename);
Box3D boundingBox() const;
static void prepareDrawGeom(QOpenGLExtraFunctions * f);
static void prepareDrawObj (QOpenGLExtraFunctions * f);
static void prepareDrawSel (QOpenGLExtraFunctions * f);
private:
void calculateNormals();
void calculateTangents();
void loadBuffer(QOpenGLExtraFunctions * f, Buffer & buf, const void * data, int size);
QVector<QVector3D> vertices_, normals_, tangents_, bitangents_;
QVector<QVector2D> texcoords_;
QVector< Vector3i> triangles_;
QVector< Vector2i> lines_;
QVector<QGLEngineShaders::Vertex> data_;
GLenum vao, geom_type;
Buffer buffer_geom, buffer_ind, buffer_obj, buffer_sel;
mutable uint hash_;
mutable bool hash_changed;
int vert_count;
bool changed, objects_changed, selected_changed;
};
QDataStream & operator <<(QDataStream & s, const Mesh * m);
QDataStream & operator >>(QDataStream & s, Mesh *& m);
#endif // GLMESH_H

View File

@@ -0,0 +1,407 @@
/*
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 "glprimitives.h"
#include "glmesh.h"
Mesh * Primitive::plane(float width, float length) {
Mesh * ret = new Mesh();
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector3i> & i(ret->indicesTriangles ());
float hw = width / 2.f, hl = length / 2.f;
for (int j = 0; j < 4; ++j) n << QVector3D(0., 0., 1.);
t << QVector2D(0., 0.) << QVector2D(0., 1.) << QVector2D(1., 1.) << QVector2D(1., 0.);
v << QVector3D(-hw, -hl, 0.) << QVector3D(-hw, hl, 0.) << QVector3D(hw, hl, 0.) << QVector3D(hw, -hl, 0.);
i << Vector3i(0, 2, 1) << Vector3i(0, 3, 2);
return ret;
}
Mesh * Primitive::cube(float width, float length, float height) {
Mesh * ret = new Mesh();
QVector3D scale(width, length, height);
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector3i> & i(ret->indicesTriangles ());
float hs = 0.5f;
int si = 0;
QMatrix4x4 mat;
si = v.size();
for (int j = 0; j < 4; ++j) n << QVector3D(0., -1., 0.);
t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.);
v << QVector3D(-hs, -hs, -hs) << QVector3D(hs, -hs, -hs) << QVector3D(hs, -hs, hs) << QVector3D(-hs, -hs, hs);
i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3);
for (int r = 0; r < 3; ++r) {
si = v.size();
mat.rotate(90., 0., 0., 1.);
QVector3D cn = mat.map(n[0]);
for (int j = 0; j < 4; ++j) {
n << cn;
v << mat.map(QVector4D(v[j])).toVector3D();
}
t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.);
i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3);
}
mat.setToIdentity();
mat.rotate(90., 1., 0.,0.);
for (int r = 0; r < 2; ++r) {
si = v.size();
mat.rotate(180., 1., 0.,0.);
QVector3D cn = mat.map(n[0]);
for (int j = 0; j < 4; ++j) {
n << cn;
v << mat.map(QVector4D(v[j])).toVector3D();
}
t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.);
i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3);
}
for (int i = 0; i < v.size(); ++i)
v[i] *= scale;
return ret;
}
Mesh * Primitive::ellipsoid(int segments_wl, int segments_h, float width, float length, float height) {
Mesh * ret = new Mesh();
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector3i> & ind(ret->indicesTriangles());
double hh = height / 2.f;
int hseg = segments_h + 1, wlseg = segments_wl + 1;
double crw, crl, a, ch, twl;
QVector3D cp;
for (int i = 0; i <= hseg; i++) {
ch = -cos((double)i / hseg * M_PI);
cp.setZ(ch * hh);
twl = sqrt(1. - ch * ch) / 2.;
crw = twl * width;
crl = twl * length;
int cvcnt = wlseg * 2;
for (int j = 0; j < cvcnt; j++) {
a = (double)j / (cvcnt - 1) * M_2PI;
cp.setX(crl * cos(a));
cp.setY(crw * sin(a));
v << cp; t << QVector2D((double)j / (cvcnt - 1), ch/2.f + 0.5f);
int si = v.size() - 1;
if (j > 0 && i > 0) {
ind << Vector3i(si - cvcnt - 1, si, si - 1);
ind << Vector3i(si - cvcnt, si, si - cvcnt - 1);
}
}
}
n.resize(v.size());
for (int i = 0; i < v.size(); i++)
n[i] = v[i].normalized();
return ret;
}
Mesh * Primitive::disc(int segments, float width, float length, bool up, float end_angle) {
Mesh * ret = new Mesh();
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector3i> & ind(ret->indicesTriangles());
segments = qMax(segments + 1, 4);
QVector3D cp;
v << QVector3D();
t << QVector2D(0.5f, 0.5f);
end_angle *= deg2rad;
for (int i = 0; i < segments; i++) {
double a = (double)i / (segments - 1) * end_angle;
cp.setX(length / 2. * cos(a));
cp.setY(width / 2. * sin(a));
v << cp;
t << QVector2D(cp.x() / width + 0.5f, cp.y() / length + 0.5f);
int si = v.size() - 1;
if (i > 0) {
if (up)
ind << Vector3i(si - 1, si, 0);
else
ind << Vector3i(si, si - 1, 0);
}
}
n.resize(v.size());
for (int i = 0; i < v.size(); i++)
n[i] = QVector3D(0, 0, up ? 1 : -1);
return ret;
}
QVector3D coneNormal(double rx, double ry, double height, double ang) {
QVector3D norm;
norm.setX(rx * cos(ang));
norm.setY(ry * sin(ang));
norm.setZ(0.);
double rl = norm.length();
double ca = atan2(rl, height);
norm *= cos(ca);
norm.setZ(norm.length() * tan(ca));
return norm.normalized();
}
Mesh * Primitive::cone(int segments, float width, float length, float height) {
Mesh * ret = new Mesh();
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector3i> & ind(ret->indicesTriangles());
int seg = qMax(segments + 1, 4);
double rx = width / 2., ry = length / 2.;
QVector3D cp;
for (int i = 0; i < seg; i++) {
double a = (double)i / (seg - 1) * M_2PI;
cp.setX(ry * cos(a));
cp.setY(rx * sin(a));
if (i > 0) {
v << QVector3D(0, 0, height);
t << QVector2D((double)(i - 1) / (seg - 1), 1.f);
double ta = ((double)i - 0.5) / (seg - 1) * M_2PI;
n << coneNormal(rx, ry, height, ta);
}
v << cp;
t << QVector2D((double)i / (seg - 1), 0.f);
n << coneNormal(rx, ry, height, a);
int si = v.size() - 1;
if (i > 0)
ind << Vector3i(si - 1, si - 2, si);
}
Mesh * cap = Primitive::disc(segments, width, length, false);
ret->append(cap);
delete cap;
return ret;
}
Mesh * Primitive::cylinder(int segments, float width, float length, float height) {
Mesh * ret = new Mesh();
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector3i> & ind(ret->indicesTriangles());
int seg = qMax(segments + 1, 4);
double rx = width / 2., ry = length / 2.;
QVector3D cp, norm;
for (int i = 0; i < seg; i++) {
double a = (double)i / (seg - 1) * M_2PI;
cp.setX(ry * cos(a));
cp.setY(rx * sin(a));
cp.setZ(0.);
norm = cp.normalized();
v << cp;
cp.setZ(height);
v << cp;
t << QVector2D((double)i / (seg - 1), 0.f);
t << QVector2D((double)i / (seg - 1), 1.f);
n << norm; n << norm;
int si = v.size() - 1;
if (i > 0) {
ind << Vector3i(si - 2, si - 1, si);
ind << Vector3i(si - 1, si - 2, si - 3);
}
}
Mesh * cap = Primitive::disc(segments, width, length, false);
ret->append(cap);
delete cap;
cap = Primitive::disc(segments, width, length, true);
cap->translatePoints(QVector3D(0., 0., height));
ret->append(cap);
delete cap;
return ret;
}
Mesh * Primitive::arrow(int segments, float thick, float angle) {
double cone_d = 3. * thick;
double cone_h = cone_d / tan(angle * deg2rad);
Mesh * ret = new Mesh();
Mesh * m = Primitive::cylinder(segments, thick, thick, 1. - cone_h);
ret->append(m);
delete m;
m = Primitive::cone(segments, cone_d, cone_d, cone_h);
m->translatePoints(QVector3D(0., 0., 1. - cone_h));
ret->append(m);
delete m;
return ret;
}
Mesh * Primitive::torus(int segments_main, int segments_second, float radius_main, float radius_second, float end_angle) {
Mesh * ret = new Mesh();
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector3i> & ind(ret->indicesTriangles());
QVector<QVector3D> cv, cn;
QVector<QVector2D> ct;
segments_second = qMax(segments_second + 1, 4);
for (int i = 0; i < segments_second; i++) {
double x = (double)i / (segments_second - 1);
double a = x * M_2PI;
cv << QVector3D(radius_second * cos(a), 0., radius_second * sin(a));
cn << cv.back().normalized();
ct << QVector2D(0., x);
cv.back() += QVector3D(radius_main, 0., 0.);
}
segments_main = qMax(segments_main + 1, 4);
int ccnt = cv.size(), pcnt = 0;
for (int i = 0; i < segments_main; i++) {
double x = (double)i / (segments_main - 1);
QMatrix4x4 rm;
rm.rotate(x * end_angle, 0., 0., 1.);
for (int j = 0; j < ccnt; j++) {
ct[j].setX(x);
v << rm.map(cv[j]);
n << rm.map(cn[j]);
}
t.append(ct);
if (i > 0) {
for (int j = 0; j < ccnt - 1; j++) {
ind << Vector3i(pcnt + j, pcnt + j + 1, pcnt + j - ccnt);
ind << Vector3i(pcnt + j + 1, pcnt + j + 1 - ccnt, pcnt + j - ccnt);
}
}
pcnt = v.size();
}
return ret;
}
Mesh * Primitive::cubeFrame(float width, float length, float height) {
Mesh * ret = new Mesh(GL_LINES);
QVector3D scale(width, length, height);
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector2i> & i(ret->indicesLines());
float hs = 0.5f;
v << QVector3D(-hs, -hs, -hs) << QVector3D(-hs, hs, -hs) << QVector3D( hs, hs, -hs) << QVector3D( hs, -hs, -hs);
v << QVector3D(-hs, -hs, hs) << QVector3D(-hs, hs, hs) << QVector3D( hs, hs, hs) << QVector3D( hs, -hs, hs);
for (int j = 0; j < 8; ++j) {
v[j] *= scale;
t << QVector2D(0, 0);
n << QVector3D(0,0,1);
}
for (int j = 0; j < 4; ++j) {
i << Vector2i(j, (j + 1) % 4);
i << Vector2i(j, j + 4);
i << Vector2i(j + 4, (j + 1) % 4 + 4);
}
return ret;
}
Mesh * Primitive::ellipsoidFrame(int segments_wl, int segments_h, float width, float length, float height) {
Mesh * ret = new Mesh(GL_LINES);
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector2i> & ind(ret->indicesLines());
double hh = height / 2.f;
int hseg = segments_h + 1, wlseg = segments_wl + 1;
double crw, crl, a, ch, twl;
QVector3D cp;
for (int i = 0; i <= hseg; i++) {
ch = -cos((double)i / hseg * M_PI);
cp.setZ(ch * hh);
twl = sqrt(1. - ch * ch) / 2.;
crw = twl * width;
crl = twl * length;
int cvcnt = wlseg * 2;
for (int j = 0; j < cvcnt; j++) {
a = (double)j / (cvcnt - 1) * M_2PI;
cp.setX(crl * cos(a));
cp.setY(crw * sin(a));
v << cp; t << QVector2D((double)j / (cvcnt - 1), ch/2.f + 0.5f);
int si = v.size() - 1;
if (j > 0 && i > 0) {
ind << Vector2i(si, si - 1);
ind << Vector2i(si - cvcnt, si);
}
}
}
n.resize(v.size());
for (int i = 0; i < v.size(); i++)
n[i] = v[i].normalized();
return ret;
}
Mesh * Primitive::coneFrame(int segments, float width, float length, float height) {
Mesh * ret = new Mesh(GL_LINES);
QVector<QVector3D> & v(ret->vertices ());
QVector<QVector3D> & n(ret->normals ());
QVector<QVector2D> & t(ret->texcoords());
QVector< Vector2i> & ind(ret->indicesLines());
int seg = qMax(segments + 1, 4);
double rx = width / 2., ry = length / 2.;
QVector3D cp;
for (int i = 0; i < seg; i++) {
double a = (double)i / (seg - 1) * M_2PI;
cp.setX(ry * cos(a));
cp.setY(rx * sin(a));
if (i > 0) {
v << QVector3D(0, 0, height);
t << QVector2D((double)(i - 1) / (seg - 1), 1.f);
double ta = ((double)i - 0.5) / (seg - 1) * M_2PI;
n << coneNormal(rx, ry, height, ta);
}
v << cp;
t << QVector2D((double)i / (seg - 1), 0.f);
n << coneNormal(rx, ry, height, a);
int si = v.size() - 1;
if (i > 0) {
ind << Vector2i(si - 1, si);
ind << Vector2i(si - 2, si);
}
}
return ret;
}

View File

@@ -0,0 +1,102 @@
/*
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/>.
*/
#ifndef GLPRIMITIVE_CUBE_H
#define GLPRIMITIVE_CUBE_H
#include "gltypes.h"
namespace Primitive {
Mesh * plane(float width = 1., float length = 1.);
Mesh * cube(float width = 1., float length = 1., float height = 1.);
Mesh * ellipsoid(int segments_wl, int segments_h, float width = 1., float length = 1., float height = 1.);
Mesh * disc(int segments, float width = 1., float length = 1., bool up = true, float end_angle = 360.);
Mesh * cone(int segments, float width = 1., float length = 1., float height = 1.);
Mesh * cylinder(int segments, float width = 1., float length = 1., float height = 1.);
Mesh * arrow(int segments = 16, float thick = 0.04, float angle = 30.); // length = 1
Mesh * torus(int segments_main = 30, int segments_second = 16, float radius_main = 2.5, float radius_second = 0.5, float end_angle = 360.);
Mesh * cubeFrame(float width = 1., float length = 1., float height = 1.);
Mesh * ellipsoidFrame(int segments_wl, int segments_h, float width = 1., float length = 1., float height = 1.);
Mesh * coneFrame(int segments, float width = 1., float length = 1., float height = 1.);
}
/*
class GLPrimitivePoint: public GLObjectBase
{
public:
GLPrimitivePoint(double size = 1., QVector3D pos = QVector3D()) {sz = 8.;}
virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false);
private:
double sz;
};
class GLPrimitiveLine: public GLObjectBase
{
public:
GLPrimitiveLine(QVector3D p0_ = QVector3D(), QVector3D p1_ = QVector3D()) {p0 = p0_; p1 = p1_;}
virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false);
QVector3D point0() const {return p0;}
QVector3D point1() const {return p1;}
void setPoint0(const QVector3D & p) {p0 = p;}
void setPoint1(const QVector3D & p) {p1 = p;}
private:
QVector3D p0, p1;
};
class GLPrimitiveEllipsoid: public GLObjectBase
{
public:
GLPrimitiveEllipsoid(float width = 1., float length = 1., float height = 1., int seg_wl = 10, int seg_h = 10, QVector3D pos = QVector3D());
virtual void init();
private:
void putTriangle(const QVector3D & v0, const QVector3D & v1, const QVector3D & v2);
float w, l, h;
int swl, sh;
};
class GLPrimitiveAxis: public GLObjectBase
{
public:
GLPrimitiveAxis() {accept_fog = accept_light = cast_shadow = rec_shadow = select_ = false;}
virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false);
};
*/
#endif // GLPRIMITIVE_CUBE_H

View File

@@ -0,0 +1,181 @@
/*
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 "gltypes.h"
#include "qglview.h"
#include "glshaders.h"
#include "glshaders_headers.h"
using namespace QGLEngineShaders;
bool addShader(QOpenGLShaderProgram * prog, QOpenGLShader::ShaderType type, QString & content, const QString & file, const QString & defs) {
if (type == 0 || content.isEmpty()) {
content.clear();
return true;
}
//qDebug() << "[QGLView] Shader" << file << "found" << (QOpenGLShader::ShaderTypeBit)(int)type << "section ...";
switch (type) {
case QOpenGLShader::Fragment:
content.prepend(qgl_fragment_head);
content.prepend(qgl_uniform);
content.prepend(qgl_structs);
break;
case QOpenGLShader::Vertex :
content.prepend(qgl_vertex_head );
break;
case QOpenGLShader::Geometry:
content.prepend(qgl_geometry_head);
break;
}
content.prepend(defs);
content.prepend(qgl_common_head);
bool ret = prog->addShaderFromSourceCode(type, content.toLatin1());
if (!ret) qDebug() << "[QGLView] Shader" << file << "Compile error:\n" << prog->log();
content.clear();
return ret;
}
QString prepareDefines(const QStringList & defines) {
if (defines.isEmpty()) return QString();
QString ret;
foreach (QString s, defines)
ret.append("#define " + s + "\n");
return ret;
}
bool QGLEngineShaders::loadShadersMulti(QOpenGLShaderProgram *& prog, const QString & file, const QStringList & defines) {
if (!prog)
prog = new QOpenGLShaderProgram();
prog->removeAllShaders();
QFile f(file);
if (!f.open(QIODevice::ReadOnly)) return false;
QTextStream ts(&f);
QString cur_shader, line, pl, defs = prepareDefines(defines);
QOpenGLShader::ShaderType type = 0;
while (!ts.atEnd()) {
line = ts.readLine();
pl = line.trimmed().remove(' ').remove('\t').mid(2).toLower();
pl.chop(2);
if (pl == "vertex" || pl == "vert") {
if (!addShader(prog, type, cur_shader, file, defs)) return false;
type = QOpenGLShader::Vertex;
continue;
}
if (pl == "fragment" || pl == "frag") {
if (!addShader(prog, type, cur_shader, file, defs)) return false;
type = QOpenGLShader::Fragment;
continue;
}
if (pl == "geometry" || pl == "geom") {
if (!addShader(prog, type, cur_shader, file, defs)) return false;
type = QOpenGLShader::Geometry;
continue;
}
if (pl == "tessellation_control") {
if (!addShader(prog, type, cur_shader, file, defs)) return false;
type = QOpenGLShader::TessellationControl;
continue;
}
if (pl == "tessellation_evaluation") {
if (!addShader(prog, type, cur_shader, file, defs)) return false;
type = QOpenGLShader::TessellationEvaluation;
continue;
}
cur_shader.append("\n");
cur_shader.append(line);
}
if (!addShader(prog, type, cur_shader, file, defs)) return false;
if (!prog->link()) {
qDebug() << "[QGLView] Shader" << file << "Link error:\n" << prog->log();
return false;
}
qDebug() << "[QGLView] Shader" << file << "ok";
return true;
}
void QGLEngineShaders::setUniformMatrices(QOpenGLShaderProgram * prog, QMatrix4x4 proj, QMatrix4x4 view, QMatrix4x4 prevproj, QMatrix4x4 prevview) {
if (!prog) return;
if (!prog->isLinked()) return;
QMatrix4x4 mvpm = proj * view;
QMatrix4x4 pmvpm = prevproj * prevview;
QMatrix3x3 nm = view.normalMatrix();
//nm.in;
prog->setUniformValue("qgl_ModelViewMatrix", view);
prog->setUniformValue("qgl_ProjectionMatrix", proj);
prog->setUniformValue("prev_ModelViewProjectioMatrix", pmvpm);
prog->setUniformValue("prev_ModelViewMatrix", prevview);
prog->setUniformValue("qgl_ModelViewProjectionMatrix", mvpm);
prog->setUniformValue("qgl_NormalMatrix", nm);
//prog->setUniformValue("qgl_BumpMatrix", nm.);
prog->setUniformValue("qgl_ModelViewMatrixTranspose", view.transposed());
prog->setUniformValue("qgl_ProjectionMatrixTranspose", proj.transposed());
prog->setUniformValue("qgl_ModelViewProjectionMatrixTranspose", mvpm.transposed());
}
void QGLEngineShaders::setUniformLights(QOpenGLShaderProgram * prog, const QVector<Light*> & lights, const QMatrix4x4 & mat, int shadow_start) {
for (int i = 0; i < lights.size(); ++i)
;//setUniformLight(prog, lights[i], QString("qgl_Light[%1]").arg(i), mat, shadow_start + i);
}
/*
" vec3 position;\n"
" vec3 direction;\n"
" vec4 color;\n"
" float intensity;\n"
" float startAngle;\n"
" float endAngle;\n"
" float constantAttenuation;\n"
" float linearAttenuation;\n"
" float quadraticAttenuation;\n"
" sampler2DShadow shadow;\n"
" mat4 shadowMatrix;\n"
*/
void QGLEngineShaders::setUniformLight(QOpenGLShaderProgram * prog, Light * light, QString ulightn, const QMatrix4x4 & mat, int shadow) {
if (!prog) return;
if (!prog->isLinked()) return;
QMatrix4x4 m = mat * light->worldTransform();
QVector4D pos(0, 0, 0, 1.), dir(light->direction(), 1);//, dir0(light->dir0), dir1(light->dir1);
pos = m * pos;
dir = ((m * dir) - pos).normalized();
float ang_start = light->angle_start / 2.f, ang_end = light->angle_end / 2.f;
if (light->light_type == Light::Omni)
ang_start = ang_end = 180.;
//qDebug() << "light" << light->name() << ulightn << pos;
prog->setUniformValue((ulightn + ".position").toLatin1().constData(), pos);
prog->setUniformValue((ulightn + ".direction").toLatin1().constData(), dir);
prog->setUniformValue((ulightn + ".intensity").toLatin1().constData(), GLfloat(light->intensity));
prog->setUniformValue((ulightn + ".startAngle").toLatin1().constData(), GLfloat(ang_start));
prog->setUniformValue((ulightn + ".startAngleCos").toLatin1().constData(), GLfloat(cosf(ang_start * deg2rad)));
prog->setUniformValue((ulightn + ".endAngle").toLatin1().constData(), GLfloat(ang_end));
prog->setUniformValue((ulightn + ".endAngleCos").toLatin1().constData(), GLfloat(cosf(ang_end * deg2rad)));
//prog->setUniformValue((ulightn + ".color").toLatin1().constData(), light->color());
prog->setUniformValue((ulightn + ".constantAttenuation").toLatin1().constData(), GLfloat(light->decay_const));
prog->setUniformValue((ulightn + ".linearAttenuation").toLatin1().constData(), GLfloat(light->decay_linear));
prog->setUniformValue((ulightn + ".quadraticAttenuation").toLatin1().constData(), GLfloat(light->decay_quadratic));
prog->setUniformValue((ulightn + ".shadow").toLatin1().constData(), shadow);
prog->setUniformValue((ulightn + ".shadowColor").toLatin1().constData(), shadow);
prog->setUniformValue((ulightn + ".shadowMatrix").toLatin1().constData(), light->shadow_matrix);
//qDebug() << light->shadow_matrix;
//prog->setUniformValue((ulightn + ".shadowDir0").toLatin1().constData(), (mat * dir0));
//prog->setUniformValue((ulightn + ".shadowDir1").toLatin1().constData(), (mat * dir1));
//qDebug() << light->direction << light->dir0 << light->dir1;
}

View File

@@ -0,0 +1,34 @@
/*
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/>.
*/
#ifndef GLSHADERS_H
#define GLSHADERS_H
#include "gltypes.h"
namespace QGLEngineShaders {
bool loadShadersMulti(QOpenGLShaderProgram *& prog, const QString & file, const QStringList & defines = QStringList());
void setUniformMatrices(QOpenGLShaderProgram * prog, QMatrix4x4 proj, QMatrix4x4 view, QMatrix4x4 prevproj = QMatrix4x4(), QMatrix4x4 prevview = QMatrix4x4());
void setUniformLights(QOpenGLShaderProgram * prog, const QVector<Light*> & lights, const QMatrix4x4 & mat, int shadow_start);
void setUniformLight(QOpenGLShaderProgram * prog, Light * light, QString ulightn, const QMatrix4x4 & mat = QMatrix4x4(), int shadow = 0);
}
#endif // GLSHADERS_H

View File

@@ -0,0 +1,133 @@
/*
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/>.
*/
#ifndef GLSHADERS_HEADERS_H
#define GLSHADERS_HEADERS_H
namespace QGLEngineShaders {
const int max_materials = 128;
const int max_lights = 64 ;
const char qgl_common_head[] =
"#version 400 core\n"
//"#extension GL_EXT_texture_aray: require\n"
"";
const char qgl_vertex_head[] =
"layout(location = 1 ) in vec3 qgl_Vertex ;\n"
"layout(location = 2 ) in vec3 qgl_Normal ;\n"
"layout(location = 3 ) in vec3 qgl_Tangent ;\n"
"layout(location = 4 ) in vec3 qgl_Bitangent ;\n"
"layout(location = 5 ) in vec2 qgl_Texture ;\n"
"layout(location = 6 ) in uint qgl_Material ;\n"
"layout(location = 7 ) in uint qgl_ObjectSelected;\n"
"layout(location = 8 ) in uint qgl_ObjectID ;\n"
"layout(location = 9 ) in vec4 qgl_ObjectColor ;\n"
"layout(location = 10) in mat4 qgl_ModelMatrix ;\n"
"out vec2 qgl_FragTexture;\n"
"flat out uint qgl_MaterialIndex;\n"
"uniform mat4 qgl_ViewMatrix;\n"
"uniform mat4 qgl_ViewProjMatrix;\n"
"mat3 qgl_getNormalMatrix() {return mat3(qgl_ViewMatrix * qgl_ModelMatrix);}\n"
"vec4 qgl_ftransform() {return qgl_ViewProjMatrix * (qgl_ModelMatrix * vec4(qgl_Vertex, 1));}\n"
"";
const char qgl_fragment_head[] =
"in vec2 qgl_FragTexture;\n"
"flat in uint qgl_MaterialIndex;\n"
"out vec4 qgl_FragData[gl_MaxDrawBuffers];\n"
"vec4 qgl_materialTexture(uint type, vec2 coord, vec4 tex_shift) {\n"
" coord *= qgl_material[qgl_MaterialIndex].map[type].scale;\n"
" vec4 t = texture(qgl_texture_array[qgl_material[qgl_MaterialIndex].map[type].array_index],\n"
" vec3(coord, qgl_material[qgl_MaterialIndex].map[type].map_index));\n"
" t += tex_shift;\n"
" t = t * qgl_material[qgl_MaterialIndex].map[type].amount + qgl_material[qgl_MaterialIndex].map[type].offset;\n"
" return t;\n"
"}\n"
"#define qgl_FragColor qgl_FragData[0]\n"
"";
const char qgl_geometry_head[] =
"";
const char qgl_structs[] =
"#define QGL_MAPS_COUNT 6\n"
"#define QGL_MAP_DIFFUSE 0\n"
"#define QGL_MAP_NORMAL 1\n"
"#define QGL_MAP_SPECULAR 2\n"
"#define QGL_MAP_ROUGHNESS 3\n"
"#define QGL_MAP_EMISSION 4\n"
"#define QGL_MAP_RELIEF 5\n"
"#define QGL_TEXTURE_ARRAY_EMPTY 0\n"
"#define QGL_TEXTURE_ARRAY_MAPS 1\n"
"struct QGLMap {\n"
" float offset;\n"
" float amount;\n"
" vec2 scale;\n"
" uint array_index;\n"
" uint map_index;\n"
"};\n"
"struct QGLMaterial {\n"
" vec4 color_diffuse;\n"
" vec4 color_specular;\n"
" vec4 color_emission;\n"
" float transparency;\n"
" float reflectivity;\n"
" float iof;\n"
" float dispersion;\n"
" QGLMap map[QGL_MAPS_COUNT];\n"
"};\n"
"struct QGLLightParameter {\n"
" vec4 color;\n"
" float intensity;\n"
" float startAngle;\n"
" float startAngleCos;\n"
" float endAngle;\n"
" float endAngleCos;\n"
" float constantAttenuation;\n"
" float linearAttenuation;\n"
" float quadraticAttenuation;\n"
//" sampler2DShadow shadow;\n"
//" sampler2D shadowColor\n"
//" mat4 shadowMatrix;\n"
//" vec4 shadowDir0;\n"
//" vec4 shadowDir1;\n"
"};\n"
"struct QGLLightPosition {\n"
" vec4 position;\n"
" vec4 direction;\n"
"};\n"
"";
const char qgl_uniform[] =
"layout (std140) uniform QGLMaterialData {\n"
" QGLMaterial qgl_material[128];\n"
"};\n"
"layout (std140) uniform QGLLightParameterData {\n"
" QGLLightParameter qgl_light_parameter[64];\n"
"};\n"
"layout (std140) uniform QGLLightPositionData {\n"
" QGLLightPosition qgl_light_position[64];\n"
"};\n"
"uniform sampler2DArray qgl_texture_array[2];\n"
"";
}
#endif // GLSHADERS_HEADERS_H

View File

@@ -0,0 +1,41 @@
/*
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 "glshaders_types.h"
QGLEngineShaders::QGLMap::QGLMap() {
offset = 0.;
amount = 1.;
scale = QVector2D(1., 1.);
array_index = map_index = 0;
}
QGLEngineShaders::QGLMaterial::QGLMaterial() {
color_diffuse = QVector4D(.5, .5, .5, 0.);
color_specular = QVector4D(.5, .5, .5, 0.);
color_emission = QVector4D(0., 0., 0., 0.);
transparency = 0.;
reflectivity = 0.;
iof = 0.;
dispersion = 0.;
map[mtNormal].map_index = emrBlue;
map[mtRoughness].amount = 0.75;
}

View File

@@ -0,0 +1,148 @@
/*
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/>.
*/
#ifndef GLSHADERS_TYPES_H
#define GLSHADERS_TYPES_H
#include "gltypes.h"
namespace QGLEngineShaders {
/// VBO
// geometry
const GLsizei pos_offset = 0;
const GLsizei normal_offset = sizeof(QVector3D) + pos_offset ;
const GLsizei tangent_offset = sizeof(QVector3D) + normal_offset ;
const GLsizei bitangent_offset = sizeof(QVector3D) + tangent_offset ;
const GLsizei tex_offset = sizeof(QVector3D) + bitangent_offset;
// object
const GLsizei material_offset = 0;
const GLsizei object_id_offset = sizeof(GLuint ) + material_offset ;
const GLsizei color_offset = sizeof(GLuint ) + object_id_offset ;
const GLsizei modelmatrix_offset = sizeof(QVector4D) + color_offset;
const GLsizei is_selected_offset = 0;
const GLuint pos_loc = 1 ; // qgl_Vertex
const GLuint normal_loc = 2 ; // qgl_Normal
const GLuint tangent_loc = 3 ; // qgl_Tangent
const GLuint bitangent_loc = 4 ; // qgl_Bitangent
const GLuint tex_loc = 5 ; // qgl_Texture
const GLuint material_loc = 6 ; // qgl_Material
const GLuint object_id_loc = 8 ; // qgl_ObjectID
const GLuint color_loc = 9 ; // qgl_ObjectColor
const GLuint modelmatrix_loc = 10; // qgl_ModelViewProjectionMatrix
const GLuint is_selected_loc = 7 ; // qgl_ObjectSelected
#pragma pack(push, 1)
struct Vertex {
QVector3D pos;
QVector3D normal;
QVector3D tangent;
QVector3D bitangent;
QVector2D tex;
};
struct Object {
Object() {
material = object_id = 0;
color = QVector4D(1,1,1,1);
QMatrix4x4().copyDataTo(modelmatrix);
}
GLuint material;
GLuint object_id;
QVector4D color;
GLfloat modelmatrix[16];
};
#pragma pack(pop)
/// UBO
enum BindingPoints {
bpMaterials,
bpLightParameters,
bpLightPositions,
};
enum MapType {
mtDiffuse = 0,
mtNormal = 1,
mtSpecular = 2,
mtRoughness = 3,
mtEmission = 4,
mtRelief = 5,
};
enum TextureArrayRole {
tarEmpty = 0,
tarMaps = 1,
};
enum EmptyMapRole {
emrWhite = 0,
emrBlue = 1,
};
#define QGL_MAPS_COUNT 6
#pragma pack(push, 1)
struct QGLMap {
QGLMap();
GLfloat offset;
GLfloat amount;
QVector2D scale;
GLuint array_index;
GLuint map_index;
GLfloat __res_2[2];
};
struct QGLMaterial {
QGLMaterial();
QVector4D color_diffuse;
QVector4D color_specular;
QVector4D color_emission;
GLfloat transparency;
GLfloat reflectivity;
GLfloat iof;
GLfloat dispersion;
QGLMap map[QGL_MAPS_COUNT];
};
struct QGLLightParameter {
QVector4D color;
//QVector4D shadowColor;
GLfloat intensity;
GLfloat startAngle;
GLfloat startAngleCos;
GLfloat endAngle;
GLfloat endAngleCos;
GLfloat constantAttenuation;
GLfloat linearAttenuation;
GLfloat quadraticAttenuation;
//GLfloat shadow;
//GLfloat shadowMatrix[16];
};
struct QGLLightPosition {
QVector4D position;
QVector4D direction;
};
#pragma pack(pop)
}
#endif // GLSHADERS_TYPES_H

View File

@@ -0,0 +1,96 @@
/*
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/>.
*/
#define GL_GLEXT_PROTOTYPES
#include <QOpenGLExtraFunctions>
#include "gltexturearray.h"
Texture2DArray::Texture2DArray(bool filter) {
target_ = GL_TEXTURE_2D_ARRAY;
texture_ = 0;
layers_ = 0;
filtering_ = filter;
}
Texture2DArray::~Texture2DArray() {
}
void Texture2DArray::init(QOpenGLExtraFunctions * f) {
if (!isInit()) {
f->glGenTextures(1, &texture_);
}
}
void Texture2DArray::destroy(QOpenGLExtraFunctions * f) {
if (texture_ != 0) {
f->glDeleteTextures(1, &texture_);
}
texture_ = 0;
}
void Texture2DArray::bind(QOpenGLExtraFunctions * f, int channel) {
f->glActiveTexture(GL_TEXTURE0 + channel);
f->glBindTexture(target_, texture_);
}
void Texture2DArray::release(QOpenGLExtraFunctions * f) {
f->glBindTexture(target_, 0);
}
bool Texture2DArray::resize(QOpenGLExtraFunctions * f, QSize new_size, int layers_count) {
if (new_size.isNull() || layers_count <= 0) return false;
if ((size_ == new_size) && (layers_ >= layers_count)) return false;
size_ = new_size;
layers_ = layers_count;
f->glBindTexture(target_, texture_);
f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_REPEAT);
f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_REPEAT);
if (filtering_) {
f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
f->glTexParameteri(target_, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8);
} else {
f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
f->glTexImage3D(target_, 0, GL_RGBA8, size_.width(), size_.height(), layers_, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
return true;
}
void Texture2DArray::load(QOpenGLExtraFunctions * f, const QImage & image, int layer) {
if (image.isNull() || size_.isNull() || layer < 0 || layer >= layers_) return;
QImage im = image.mirrored(false, true)
.scaled(size_, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)
.convertToFormat(QImage::Format_RGBA8888);
//qDebug() << "Texture2DArray::load image" << image.size() << "to layer" << layer;
f->glTexSubImage3D(target_, 0, 0, 0, layer, size_.width(), size_.height(), 1, GL_RGBA, GL_UNSIGNED_BYTE, im.constBits());
}
void Texture2DArray::mipmaps(QOpenGLExtraFunctions * f) {
f->glBindTexture(target_, texture_);
f->glGenerateMipmap(target_);
}

View File

@@ -0,0 +1,56 @@
/*
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/>.
*/
#ifndef GLTEXTUREARRAY_H
#define GLTEXTUREARRAY_H
#include "gltypes.h"
class Texture2DArray
{
friend class ObjectBase;
public:
Texture2DArray(bool filter);
~Texture2DArray();
void init (QOpenGLExtraFunctions * f);
void destroy (QOpenGLExtraFunctions * f);
void bind (QOpenGLExtraFunctions * f, int channel = 0);
void release (QOpenGLExtraFunctions * f);
// returns true if size changed
bool resize (QOpenGLExtraFunctions * f, QSize new_size, int layers_count);
void load (QOpenGLExtraFunctions * f, const QImage & image, int layer);
void mipmaps (QOpenGLExtraFunctions * f);
GLuint ID() const {return texture_;}
bool isInit() const {return texture_ != 0;}
private:
GLenum target_;
GLuint texture_;
QSize size_;
int layers_;
bool filtering_;
};
#endif // GLTEXTUREARRAY_H

373
qglengine/core/gltypes.cpp Normal file
View File

@@ -0,0 +1,373 @@
/*
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 "gltexture_manager.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(QOpenGLExtraFunctions * f, GLuint & tex, int width, int height, const GLenum & format, const GLenum & target) {
//glClearError();
if (tex == 0) {
f->glGenTextures(1, &tex);
f->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)
f->glTexImage2D(target, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, nullptr);
else {
int type = GL_UNSIGNED_BYTE;
int fmt = GL_RGBA;
if (format == GL_RGB32F || format == GL_RGB16F || format == GL_RGBA32F || format == GL_RGBA16F)
type = GL_FLOAT;
if (format == GL_RGB32F || format == GL_RGB16F || format == GL_RGB8 || format == GL_RGB)
fmt = GL_RGB;
f->glTexImage2D(target, 0, format, width, height, 0, fmt, type, nullptr);
//glGenerateMipmap(target);
//qDebug() << "glTexImage2D" << width << height << QString::number(t, 16);
}
//qDebug() << QString::number(glGetError(), 16);
}
void createGLTexture(QOpenGLExtraFunctions * f, GLuint & tex, const QImage & image, const GLenum & format, const GLenum & target) {
if (tex == 0) {
f->glGenTextures(1, &tex);
}
f->glBindTexture(target, tex);
QImage im = image.mirrored(false, true).convertToFormat(QImage::Format_RGBA8888);
//const QImage & cim(im);
f->glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT);
f->glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT);
f->glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT);
if (target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_3D) {
f->glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
f->glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8);
} else {
f->glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
f->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
glClearError();
f->glTexImage2D(target, 0, format, im.width(), im.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, im.constBits());
if (target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_3D) {
f->glGenerateMipmap(target);
}
//qDebug() << target << format << tex << im.width() << im.height() << im.bits() << QString::number(glGetError(), 16);
}
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;
if (aspect >= 1.) {
ret(0, 0) = t / aspect;
ret(1, 1) = t;
} else {
ret(0, 0) = t;
ret(1, 1) = t * aspect;
}
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;
}
Vector2i::Vector2i(const QString & str) {
QString s = str.trimmed();
int i = s.indexOf("\t");
p0 = s.left(i).toInt(); s = s.right(s.length() - i - 1);
p1 = s.toInt();
}
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);
}
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 (o.isEmpty()) return *this;
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;
}
QVector3D vectorFromString(const QString & str) {
QTextStream s(const_cast<QString*>(&str), QIODevice::ReadOnly);
QVector3D ret;
float f(0.f);
s >> f; ret.setX(f);
s >> f; ret.setY(f);
s >> f; ret.setZ(f);
return ret;
}

291
qglengine/core/gltypes.h Normal file
View File

@@ -0,0 +1,291 @@
/*
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/>.
*/
#ifndef GLTYPES_H
#define GLTYPES_H
#if WIN32 || WIN64 || _WIN32 || _WIN64 || __WIN32__ || __WIN64__
# define WINDOWS
#endif
#if __QNX__ || __QNXNTO__
# define QNX
#endif
#ifdef __APPLE__
# define MAC
#endif
#ifndef WINDOWS
# ifndef QNX
# ifndef MAC
# define LINUX
# endif
# endif
#endif
#if __GNUC__
# define CC_GCC
#elif _MSC_VER
# define CC_VC
#endif
#include <QObject>
//#ifndef WINDOWS
//# ifdef MAC
//# include <OpenGL/gl.h>
//# include <OpenGL/glu.h>
//# include <GLUT/glut.h>
//# else
//# include <GL/gl.h>
//# include <GL/glext.h>
//# include <GL/glu.h>
//# endif
//#endif
#include <QOpenGLExtraFunctions>
#include <QOpenGLShader>
#include <QOpenGLShaderProgram>
#include <qopenglext.h>
#include <cmath>
#include <float.h>
#include <QMatrix4x4>
#include <QDebug>
#include <QDataStream>
#include <QColor>
#include <QVector2D>
#include <QVector3D>
#include <QImage>
#include <QMutex>
#include <QFile>
#include <QDir>
#ifndef QNX
# include <cmath>
# include <complex>
#else
# include <math.h>
# include <complex.h>
#endif
#include <iostream>
//#ifdef WINDOWS
//# define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
//# define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
//#endif
#ifndef M_PI
# define M_PI 3.14159265358979323846
#endif
#ifndef M_2PI
# define M_2PI 6.28318530717958647692
#endif
#ifndef M_PI_3
# define M_PI_3 1.04719755119659774615
#endif
#ifndef GL_RGBA16F
# define GL_RGBA16F GL_RGBA16F_ARB
#endif
using std::complex;
#ifndef PIP_VERSION
typedef long long llong;
typedef unsigned char uchar;
typedef unsigned short int ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned long long ullong;
typedef long double ldouble;
const float deg2rad = atanf(1.f) / 45.f;
const float rad2deg = 45.f / atanf(1.f);
# ifdef WINDOWS
inline int random() {return rand();}
# endif
#else
#define random randomi
#endif
#ifdef CC_VC
inline float round(const float & v) {return floor(v + 0.5);}
#endif
inline float randomu() {return float(random()) / RAND_MAX;}
inline const QSizeF operator *(const QSizeF & f, const QSizeF & s) {return QSizeF(f.width() * s.width(), f.height() * s.height());}
#ifndef PIP_VERSION
template<typename Type> inline void piSwap(Type & f, Type & s) {Type t = f; f = s; s = t;}
template<typename Type> inline Type piMin(const Type & f, const Type & s) {return (f > s) ? s : f;}
template<typename Type> inline Type piMin(const Type & f, const Type & s, const Type & t) {return (f < s && f < t) ? f : ((s < t) ? s : t);}
template<typename Type> inline Type piMax(const Type & f, const Type & s) {return (f < s) ? s : f;}
template<typename Type> inline Type piMax(const Type & f, const Type & s, const Type & t) {return (f > s && f > t) ? f : ((s > t) ? s : t);}
template<typename Type> inline Type piClamp(const Type & v, const Type & min, const Type & max) {return (v > max ? max : (v < min ? min : v));}
inline ushort letobe_s(ushort v) {return (v << 8) | (v >> 8);}
inline uint letobe_i(const uint & v) {return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | ((v << 24) & 0xFF000000);}
#endif
// return [-1, 1]
inline float urand(const float & scale = 1.) {return ((float)rand() / RAND_MAX - .5f) * (scale + scale);}
// return [0, 1]
inline float uprand(const float & scale = 1.) {return ((float)rand() / RAND_MAX) * scale;}
QString readCharsUntilNull(QDataStream & s);
QString findFile(const QString & file, const QStringList & pathes);
inline QColor operator *(const QColor & c, float v) {return QColor(piClamp<int>(c.red() * v, 0, 255), piClamp<int>(c.green() * v, 0, 255), piClamp<int>(c.blue() * v, 0, 255), piClamp<int>(c.alpha() * v, 0, 255));}
inline QColor operator /(const QColor & c, float v) {return QColor(piClamp<int>(c.red() / v, 0, 255), piClamp<int>(c.green() / v, 0, 255), piClamp<int>(c.blue() / v, 0, 255), piClamp<int>(c.alpha() / v, 0, 255));}
inline void qglColor(const QColor & c) {glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF());}
inline void glClearError() {int c = 100; while (glGetError() != GL_NO_ERROR && --c > 0) glGetError();}
inline void glSetCapEnabled(GLenum cap, bool on = true) {if (on) glEnable(cap); else glDisable(cap);}
inline void glSetPolygonMode(GLenum mode) {glPolygonMode(GL_FRONT_AND_BACK, mode);}
inline void deleteGLTexture(QOpenGLExtraFunctions * f, GLuint & tex) {if (tex != 0) f->glDeleteTextures(1, &tex); tex = 0;}
void glEnableDepth();
void glDisableDepth();
void glClearFramebuffer(const QColor & color = Qt::black, bool depth = true);
void glDrawQuad(QOpenGLShaderProgram * prog = nullptr, QVector4D * corner_dirs = nullptr, GLfloat x = -1.f, GLfloat y = -1.f, GLfloat w = 2.f, GLfloat h = 2.f);
void createGLTexture(QOpenGLExtraFunctions * f, GLuint & tex, int width, int height, const GLenum & format = GL_RGBA, const GLenum & target = GL_TEXTURE_2D);
void createGLTexture(QOpenGLExtraFunctions * f, GLuint & tex, const QImage & image, const GLenum & format = GL_RGBA, const GLenum & target = GL_TEXTURE_2D);
QMatrix4x4 glMatrixPerspective(float angle, float aspect, float near_, float far_);
QImage rotateQImageLeft(const QImage & im);
QImage rotateQImageRight(const QImage & im);
inline QImage rotateQImage180(const QImage & im) {return im.mirrored(true, true);}
class QGLView;
class MouseController;
class ObjectBase;
class AimedObject;
class Light;
class Camera;
class Texture;
class CubeTexture;
class Map;
class Material;
class TextureManager;
class Texture2DArray;
class Mesh;
class Scene;
class RendererBase;
class Renderer;
class RendererMaterial;
class RendererService;
class RendererSelection;
struct Box3D {
GLfloat x;
GLfloat y;
GLfloat z;
GLfloat width;
GLfloat length;
GLfloat height;
GLfloat angle_z;
GLfloat angle_xy;
GLfloat angle_roll;
Box3D() {x = y = z = width = length = height = angle_z = angle_xy = angle_roll = 0.f;}
Box3D(const QVector3D & center, GLfloat hwid, GLfloat hlen, GLfloat hhei) {x = center.x() - hwid; y = center.y() - hlen; z = center.z() - hhei; width = 2 * hwid; length = 2 * hlen; height = 2 * hhei; angle_z = angle_xy = angle_roll = 0.f;}
Box3D(const QVector<QVector3D> & points);
bool isEmpty() const {return (qAbs(width) < 1E-6f) && (qAbs(length) < 1E-6f) && (qAbs(height) < 1E-6f);}
QVector3D randomPoint() const {return QVector3D(uprand(length) + x, uprand(width) + y, uprand(height) + z);}
QVector3D pos() const {return QVector3D(x, y, z);}
QVector3D size() const {return QVector3D(length, width, height);}
QVector3D center() const {return QVector3D(length / 2.f + x, width / 2.f + y, height / 2.f + z);}
QVector3D angles() const {return QVector3D(angle_xy, angle_roll, angle_z);}
QVector<QVector3D> corners() const;
void setPos(const QVector3D & p) {x = p.x(); y = p.y(); z = p.z();}
void setAngles(const QVector3D & a) {angle_xy = a.x(); angle_roll = a.y(); angle_z = a.z();}
void setSize(const QVector3D & s) {length = s.x(); width = s.y(); height = s.z();}
Box3D & moveTo(const QVector3D & v) {x = v.x(); y = v.y(); z = v.z(); return *this;}
Box3D & move(const QVector3D & v) {x += v.x(); y += v.y(); z += v.z(); return *this;}
Box3D movedTo(const QVector3D & v) const {Box3D t(*this); t.x = v.x(); t.y = v.y(); t.z = v.z(); return t;}
Box3D moved(const QVector3D & v) const {Box3D t(*this); t.x += v.x(); t.y += v.y(); t.z += v.z(); return t;}
Box3D & operator |=(const Box3D & o);
};
inline QDebug operator <<(QDebug d, const Box3D & v) {d << "Box3D {start (" << v.x << "," << v.y << "," << v.z << "), size (" << v.length << "," << v.width << "," << v.height << ")}"; return d;}
#pragma pack(push, 1)
struct Vector2i {
Vector2i(int p0_ = 0, int p1_ = 0) {p0 = p0_; p1 = p1_;}
Vector2i(const QString & str);
Vector2i movedX(const int & o) {return Vector2i(p0 + o, p1);}
Vector2i movedY(const int & o) {return Vector2i(p0, p1 + o);}
Vector2i moved(const int & x, const int & y) {return Vector2i(p0 + x, p1 + y);}
GLint p0;
GLint p1;
bool operator ==(const Vector2i & o) const {return p0 == o.p0 && p1 == o.p1;}
bool operator !=(const Vector2i & o) const {return p0 != o.p0 || p1 != o.p1;}
void operator +=(int v) {p0 += v; p1 += v;}
QVector2D toQVector2D() const {return QVector2D(p0, p1);}
};
#pragma pack(pop)
inline Vector2i operator +(const Vector2i & f, const Vector2i & s) {return Vector2i(f.p0 + s.p0, f.p1 + s.p1);}
inline Vector2i operator -(const Vector2i & f, const Vector2i & s) {return Vector2i(f.p0 - s.p0, f.p1 - s.p1);}
inline Vector2i operator /(const Vector2i & f, const int & s) {return Vector2i(f.p0 / s, f.p1 / s);}
inline uint qHash(const Vector2i & v) {return v.p0 ^ ((v.p1 << 8) | (v.p1 >> 24));}
inline QDebug operator <<(QDebug d, const Vector2i & v) {d.nospace() << "{" << v.p0 << ", " << v.p1 << "}"; return d.space();}
inline QDataStream & operator <<(QDataStream & s, const Vector2i & v) {s << v.p0 << v.p1; return s;}
inline QDataStream & operator >>(QDataStream & s, Vector2i & v) {s >> v.p0 >> v.p1; return s;}
#pragma pack(push, 1)
struct Vector3i {
Vector3i(int p0_ = 0, int p1_ = 0, int p2_ = 0) {p0 = p0_; p1 = p1_; p2 = p2_;}
Vector3i(const QString & str);
Vector3i movedX(const int & o) {return Vector3i(p0 + o, p1, p2);}
Vector3i movedY(const int & o) {return Vector3i(p0, p1 + o, p2);}
Vector3i movedZ(const int & o) {return Vector3i(p0, p1, p2 + o);}
Vector3i moved(const int & x, const int & y, const int & z) {return Vector3i(p0 + x, p1 + y, p2 + z);}
GLint p0;
GLint p1;
GLint p2;
bool operator ==(const Vector3i & o) const {return p0 == o.p0 && p1 == o.p1 && p2 == o.p2;}
bool operator !=(const Vector3i & o) const {return p0 != o.p0 || p1 != o.p1 || p2 != o.p2;}
void operator +=(int v) {p0 += v; p1 += v; p2 += v;}
QVector3D toQVector3D() const {return QVector3D(p0, p1, p2);}
};
#pragma pack(pop)
inline Vector3i operator +(const Vector3i & f, const Vector3i & s) {return Vector3i(f.p0 + s.p0, f.p1 + s.p1, f.p2 + s.p2);}
inline Vector3i operator -(const Vector3i & f, const Vector3i & s) {return Vector3i(f.p0 - s.p0, f.p1 - s.p1, f.p2 - s.p2);}
inline Vector3i operator /(const Vector3i & f, const int & s) {return Vector3i(f.p0 / s, f.p1 / s, f.p2 / s);}
inline uint qHash(const Vector3i & v) {return v.p0 ^ ((v.p1 << 8) | (v.p1 >> 24)) ^ ((v.p2 << 16) | (v.p2 >> 16));}
inline QDebug operator <<(QDebug d, const Vector3i & v) {d.nospace() << "{" << v.p0 << ", " << v.p1 << ", " << v.p2 << "}"; return d.space();}
inline QDataStream & operator <<(QDataStream & s, const Vector3i & v) {s << v.p0 << v.p1 << v.p2; return s;}
inline QDataStream & operator >>(QDataStream & s, Vector3i & v) {s >> v.p0 >> v.p1 >> v.p2; return s;}
QVector3D vectorFromString(const QString & str);
QColor colorFromString(const QString & str);
inline QVector4D QColor2QVector(const QColor & c) {return QVector4D(c.redF(), c.greenF(), c.blueF(), c.alphaF());}
inline float cosABV(const QVector3D & v0, const QVector3D & v1) {
float l = v0.length() * v1.length();
if (l == 0.f) return 0.;
return (QVector3D::dotProduct(v0, v1)) / l;
}
inline QVector3D projection(const QVector3D & v, const QVector3D & to) {return to.normalized() * v.length() * cosABV(v, to);}
QVector3D orthToVector(const QVector3D & v, const float & scale = 1.);
QVector3D rotateVector(const QVector3D & v, const QVector3D & a);
void setVectorLength(QVector3D & v, const float & l);
void lengthenVector(QVector3D & v, const float & l);
inline float squareLength(const QVector3D & from, const QVector3D & to) {return (to.x() - from.x())*(to.x() - from.x()) + (to.y() - from.y())*(to.y() - from.y()) + (to.z() - from.z())*(to.z() - from.z());}
inline QVector3D directionFromAngles(const QVector3D & a) {return rotateVector(QVector3D(1., 0., 0.), a);}
inline float frac(const float & x, const float & b) {return x - int(x / b) * b;}
#endif // GLTYPES_H