initial commit

This commit is contained in:
2020-08-25 21:57:31 +03:00
commit 3e29fd4373
166 changed files with 24675 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/.svn
CMakeLists.txt.user*

2
AUTHORS.txt Normal file
View File

@@ -0,0 +1,2 @@
peri4 = Пелипенко Иван <peri4ko@yandex.ru>
andrey = Бычков Андрей <andrey@signalmodelling.ru>

55
CMakeLists.txt Normal file
View File

@@ -0,0 +1,55 @@
cmake_minimum_required(VERSION 3.0)
project(qglengine)
find_package(QAD REQUIRED)
set(_DOMAIN "org.SHS")
set(_COMPANY "SHS")
find_qt(Qt5 Core Gui OpenGL Xml)
if (NOT Qt5)
message(WARNING "Building ${PROJECT_NAME} available only on Qt5!")
else()
find_package(OpenGL REQUIRED)
set_version(qglengine
MAJOR 1
MINOR 0
REVISION 0
BUILD "${BUILD_NUMBER}"
SUFFIX rc
OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/core/qglengine_version.h")
qt_sources(SRC)
qt_sources(FSRC DIR "formats")
list(APPEND SRC ${FSRC})
qt_sources(FSRC DIR "core")
list(APPEND SRC ${FSRC})
qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM)
file(GLOB PHS "*_p.h" "formats/*_p.h" "core/*_p.h")
list(REMOVE_ITEM out_HDR "${PHS}")
import_version(qglengine_core qglengine)
set_deploy_property(qglengine_core SHARED
LABEL "QGLEngine core library"
FULLNAME "${_DOMAIN}.qglengine_core"
COMPANY "${_COMPANY}"
INFO "QGLEngine core library")
make_rc(qglengine_core _RC)
qt_add_library(qglengine_core SHARED out_CPP ${_RC})
qt_generate_export_header(qglengine_core)
list(APPEND out_HDR "${CMAKE_CURRENT_BINARY_DIR}/qglengine_core_export.h")
qt_target_include_directories(qglengine_core PRIVATE ${QAD_INCLUDES} "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/core")
qt_target_link_libraries(qglengine_core qad_utils qad_widgets assimp ${OPENGL_LIBRARIES})
message(STATUS "Building QGLEngine version ${qglengine_VERSION} (SHARED)")
list(APPEND QT_MULTILIB_LIST qglengine_core)
add_subdirectory(widgets)
copy_to_parent("")
sdk_install("qglengine" FALSE "qglengine_core" "${out_HDR}" "${out_QM}")
if (NOT DEFINED ANDROID_PLATFORM)
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/plugin")
#add_subdirectory(plugin)
endif()
endif()
qt_sources(test_SRC DIR "qglview_test")
qt_wrap(${test_SRC} CPPS test_CPP)
qt_add_executable(qglengine_test test_CPP)
qt_target_link_libraries(qglengine_test qglengine_core qglengine_widgets)
qt_target_include_directories(qglengine_test PRIVATE ${QAD_INCLUDES} "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/core" "${CMAKE_CURRENT_SOURCE_DIR}/widgets")
endif()

165
LICENSE.txt Normal file
View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

BIN
coeffs_brdf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

88
core/glbuffer.cpp Normal file
View File

@@ -0,0 +1,88 @@
/*
QGL Buffer
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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) {
//qDebug() << "bind" << target_ << buffer_;
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);
}

58
core/glbuffer.h Normal file
View File

@@ -0,0 +1,58 @@
/*
QGL Buffer
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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_;}
GLenum target() const {return target_;}
void setTarget(GLenum t) {target_ = t;}
bool isInit() const {return buffer_ != 0;}
private:
GLenum target_, usage_;
GLuint buffer_;
int prev_size;
};
#endif // GLBUFFER_H

281
core/glcubemap.cpp Normal file
View File

@@ -0,0 +1,281 @@
/*
QGL CubeTexture
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gltypes.h"
#include "glcubemap.h"
#include "hdr_p.h"
using namespace QGLEngineShaders;
QVector<QVector3D> loadFileHDR(const QString & path, QSize * size) {
if (size) *size = QSize();
QVector<QVector3D> ret;
QFile f(path);
if (!f.open(QIODevice::ReadOnly)) {
qDebug() << "[QGLEngine] Can`t open" << path;
return ret;
}
QTextStream ts(&f);
QString line = ts.readLine(256);
if (line != "#?RADIANCE") return ret;
QSize sz;
while (!ts.atEnd()) {
line = ts.readLine(256);
if (line.startsWith("FORMAT")) {
line.remove(0, 7);
if (!line.startsWith("32-bit")) {
qDebug() << "[QGLEngine] File" << path << "has unknown format!";
return ret;
}
}
if (line.mid(1, 2) == "Y ") {
QStringList sl = line.trimmed().split(" ");
sl.removeAll("");
if (sl.size() != 4) {
qDebug() << "[QGLEngine] File" << path << "has unknown size!";
return ret;
}
sz.setWidth (sl[3].toInt());
sz.setHeight(sl[1].toInt());
//qDebug() << "found size" << sz;
break;
}
}
if (sz.isEmpty()) return ret;
f.seek(ts.pos());
QDataStream ds(&f);
int count = sz.width() * sz.height();
QVector<float> data(count*3);
if (!RGBE_ReadPixels_RLE(&ds, data.data(), sz.width(), sz.height()))
return ret;
if (size) *size = sz;
ret.resize(count);
//QColor col;
//QImage im(sz, QImage::Format_ARGB32);
//QRgb * imdata = (QRgb*)im.bits();
for (int i = 0; i < count; ++i) {
QVector3D p(pow(data[i*3 + 2], 1. / 2.2),
pow(data[i*3 + 1], 1. / 2.2),
pow(data[i*3 + 0], 1. / 2.2));
ret[i] = p;
//col = QColor::fromRgbF(piClamp(p[0], 0.f, 1.f),
// piClamp(p[1], 0.f, 1.f),
// piClamp(p[2], 0.f, 1.f));
//imdata[i] = col.rgb();
}
//im.save("_hdr.png");
return ret;
}
//#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
//#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
//#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
//#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
//#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
//#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
QVector<QVector3D> faceHDR(const QVector<QVector3D> & data, QSize sz, QSize & fsz, int face) {
QVector<QVector3D> ret;
if (data.isEmpty() || sz.isNull()) return ret;
QRect fr;
int fw = sz.width () / 4;
int fh = sz.height() / 3;
fsz = QSize(fw, fh);
ret.reserve(fw * fh);
switch (face) {
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
fr.setRect(fw, fh, fw, fh);
for (int x = fr.left(); x <= fr.right(); ++x) {
for (int y = fr.top(); y <= fr.bottom(); ++y) {
ret << data[y*sz.width() + x];
}
}
break;
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
fr.setRect(fw*3, fh, fw, fh);
for (int x = fr.right(); x >= fr.left(); --x) {
for (int y = fr.bottom(); y >= fr.top(); --y) {
ret << data[y*sz.width() + x];
}
}
break;
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
fr.setRect( 0, fh, fw, fh);
for (int y = fr.bottom(); y >= fr.top(); --y) {
for (int x = fr.left(); x <= fr.right(); ++x) {
ret << data[y*sz.width() + x];
}
}
break;
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
fr.setRect(fw*2, fh, fw, fh);
for (int y = fr.top(); y <= fr.bottom(); ++y) {
for (int x = fr.right(); x >= fr.left(); --x) {
ret << data[y*sz.width() + x];
}
}
break;
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
fr.setRect(fw, 0, fw, fh);
for (int x = fr.left(); x <= fr.right(); ++x) {
for (int y = fr.top(); y <= fr.bottom(); ++y) {
ret << data[y*sz.width() + x];
}
}
break;
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
fr.setRect(fw, fh*2, fw, fh);
for (int x = fr.left(); x <= fr.right(); ++x) {
for (int y = fr.top(); y <= fr.bottom(); ++y) {
ret << data[y*sz.width() + x];
}
}
break;
default: break;
}
if (fr.isEmpty()) return ret;
//qDebug() << ret.size() << fr;
return ret;
}
CubeTexture::CubeTexture(QOpenGLExtraFunctions * f_, int _size, const GLenum & _format): f(f_) {
size = _size;
format_ = _format;
id_ = 0;
changed_ = false;
}
bool CubeTexture::init() {
if (isInit()) return true;
f->glGenTextures(1, &id_);
f->glBindTexture(GL_TEXTURE_CUBE_MAP, id_);
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
//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);
changed_ = false;
return id_ > 0;
}
void CubeTexture::destroy() {
if (!isInit()) return;
f->glDeleteTextures(1, &id_);
id_ = 0;
}
void CubeTexture::bind(int channel) {
init();
f->glActiveTexture(GL_TEXTURE0 + channel);
f->glBindTexture(GL_TEXTURE_CUBE_MAP, id_);
}
void CubeTexture::release() {
f->glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
void CubeTexture::loadHDR(const QVector<QVector3D> & data, QSize sz) {
bind();
QSize fsz;
QVector<QVector3D> fd;
for (int i = 0; i < 6; ++i) {
fd = faceHDR(data, sz, fsz, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i);
//qDebug() << "load cube" << fd[0];
//f->glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
f->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format_, fsz.width(), fsz.height(), 0, GL_RGB, GL_FLOAT, fd.isEmpty() ? 0 : fd.constData());
//qDebug() << QString::number(GetLastError(), 16);
}
f->glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
}
void CubeTexture::setFileHDR(const QString & path) {
hdr_path = path;
changed_ = true;
}
void CubeTexture::load() {
if (!changed_) return;
init();
if (!hdr_path.isEmpty()) {
QSize sz;
QVector<QVector3D> data = loadFileHDR(hdr_path, &sz);
loadHDR(data, sz);
} else {
destroy();
bind();
for (int i = 0; i < 6; ++i) {
f->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format_, 1, 1, 0, GL_RGB, GL_FLOAT, 0);
}
f->glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
}
changed_ = false;
}
/*
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();
}
*/

67
core/glcubemap.h Normal file
View File

@@ -0,0 +1,67 @@
/*
QGL CubeTexture
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLCUBEMAP_H
#define GLCUBEMAP_H
#include "glshaders_types.h"
#include "chunkstream.h"
QVector<QVector3D> loadFileHDR(const QString & path, QSize * size = 0);
class CubeTexture {
public:
CubeTexture(QOpenGLExtraFunctions * f_, int _size, const GLenum & _format = GL_RGB16F);
bool init();
void destroy();
void bind(int channel = 0);
void release();
void resize(int _size) {size = _size; changed_ = true;}
void loadHDR(const QVector<QVector3D> & data, QSize sz);
void setFileHDR(const QString & path);
QString fileHDR() const {return hdr_path;}
//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_;}
bool isInit() const {return id_ != 0;}
//const QString & path(int side) const {return pathes[side];}
//void setPath(int side, const QString & p) {pathes[side] = p;}
//void loadPathesFromDirectory(const QString & dir);
void load();
private:
QOpenGLExtraFunctions * f;
bool changed_;
int size;
GLenum format_;
GLuint id_;
QString hdr_path;
//QVector<QString> pathes;
};
#endif // GLCUBEMAP_H

315
core/glframebuffer.cpp Normal file
View File

@@ -0,0 +1,315 @@
/*
QGL Framebuffer
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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_LINEAR);
f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
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()) {
enablePixelBuffer();
}
is_changed = false;
}
QImage Framebuffer::grab() const {
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);
pbo.bind(f);
f->glReadPixels(p.x(), height() - p.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0);
pbo_queried = 1;
pbo.release(f);
}
void Framebuffer::queryPoints(int index, QRect rect_, GLenum pixel_format) {
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);
pbo.bind(f);
f->glReadPixels(rect_.x(), height() - rect_.bottom(), rect_.width(), rect_.height(), GL_RGBA, pixel_format, 0);
pbo_queried = rect_.width() * rect_.height();
pbo.release(f);
}
void Framebuffer::queryImage(int index) {
queryPoints(index, rect());
}
uint Framebuffer::getPoint() const {
if (!pbo.isInit() || (pbo_queried == 0)) return 0;
uint ret = 0;
pbo.bind(f);
void * map = pbo.map(f, GL_MAP_READ_BIT, sizeof(uint));
if (map)
memcpy(&ret, map, sizeof(uint));
pbo.unmap(f);
pbo.release(f);
return ret;
}
QVector<uint> Framebuffer::getPointsByte() const {
QVector<uint> ret;
if (!pbo.isInit() || (pbo_queried == 0)) return ret;
ret.resize(pbo_queried);
pbo.bind(f);
void * map = pbo.map(f, GL_MAP_READ_BIT, pbo_queried * sizeof(uint));
if (map)
memcpy(ret.data(), map, pbo_queried * sizeof(uint));
pbo.unmap(f);
pbo.release(f);
return ret;
}
QVector<QVector4D> Framebuffer::getPointsFloat() const {
QVector<QVector4D> ret;
if (!pbo.isInit() || (pbo_queried == 0)) return ret;
ret.resize(pbo_queried);
pbo.bind(f);
void * map = pbo.map(f, GL_MAP_READ_BIT, pbo_queried * sizeof(QVector4D));
if (map)
memcpy(ret.data(), map, pbo_queried * sizeof(QVector4D));
pbo.unmap(f);
pbo.release(f);
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;
pbo.bind(f);
void * map = pbo.map(f, GL_MAP_READ_BIT, bytes);
if (map)
memcpy(ret.bits(), map, bytes);
pbo.unmap(f);
pbo.release(f);
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) const {
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;
f->glGetIntegerv(GL_VIEWPORT, prev_view);
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
setWriteBuffers();
f->glReadBuffer(GL_COLOR_ATTACHMENT0);
f->glViewport(0, 0, wid, hei);
}
void Framebuffer::release() {
is_changed = false;
if (fbo == 0) return;
f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
f->glViewport(prev_view[0], prev_view[1], prev_view[2], prev_view[3]);
}
void Framebuffer::setWriteBuffer(int index) {
unsetWriteBuffers();
GLenum e = GL_COLOR_ATTACHMENT0 + index;
f->glDrawBuffers(1, &e);
}
void Framebuffer::setWriteBuffers(const int * indeces, int count) {
unsetWriteBuffers();
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::unsetWriteBuffers() {
QVector<GLenum> buffers(colors.size(), GL_NONE);
f->glDrawBuffers(buffers.size(), buffers.constData());
}
void Framebuffer::enablePixelBuffer() {
pbo.init(f);
pbo.bind(f);
pbo.resize(f, width()*height()*4*4);
pbo.release(f);
}
void Framebuffer::setColorTextureFiltering(int index, GLenum filter) {
bindColorTexture(index);
f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, filter);
f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, filter);
}
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;
}

89
core/glframebuffer.h Normal file
View File

@@ -0,0 +1,89 @@
/*
QGL Framebuffer
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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
{
friend class FramebufferMipmap;
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_;}
bool isInit() const {return fbo != 0;}
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, GLenum pixel_format = GL_UNSIGNED_BYTE);
void queryImage(int index);
uint getPoint() const;
QVector<uint> getPointsByte() const;
QVector<QVector4D> getPointsFloat() 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) const;
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(const int * indeces, int count);
void setWriteBuffers(const QVector<int> & indeces) {setWriteBuffers(indeces.constData(), indeces.size());}
void setWriteBuffers();
void unsetWriteBuffers();
//void setColorFormat(GLenum format) {color_format = format; is_changed = true;}
void enablePixelBuffer();
void setColorTextureFiltering(int index, GLenum filter);
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,50 @@
/*
QGL FramebufferMipmap
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QOpenGLExtraFunctions>
#include "glframebuffer_mipmap.h"
#include <QTime>
FramebufferMipmap::FramebufferMipmap(const Framebuffer & fb, int index_from_, int levels): src_fb(fb) {
index_from = index_from_;
for (int i = 0; i < levels; ++i)
fbo << new Framebuffer(fb.f, 1, false, fb.color_formats[index_from]);
}
FramebufferMipmap::~FramebufferMipmap() {
}
void FramebufferMipmap::resize() {
QSize sz = src_fb.size();
for (int i = 0; i < fbo.size(); ++i) {
sz /= 2;
fbo[i]->resize(sz.width(), sz.height());
}
}
void FramebufferMipmap::create() {
if (fbo.isEmpty()) return;
src_fb.blit(index_from, fbo[0]->id(), 0, src_fb.rect(), fbo[0]->rect(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
for (int i = 0; i < fbo.size() - 1; ++i)
fbo[i]->blit(0, fbo[i + 1]->id(), 0, fbo[i]->rect(), fbo[i + 1]->rect(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
}

View File

@@ -0,0 +1,50 @@
/*
QGL FramebufferMipmap
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLFRAMEBUFFER_MIPMAP_H
#define GLFRAMEBUFFER_MIPMAP_H
#include "glframebuffer.h"
class FramebufferMipmap
{
public:
FramebufferMipmap(const Framebuffer & fb, int index_from_, int levels = 2);
virtual ~FramebufferMipmap();
int levelsCount() const {return fbo.size();}
int lastLevel() const {return fbo.size() - 1;}
Framebuffer & plane(int level) {return *fbo[level];}
Framebuffer & lastPlane() {return *fbo[lastLevel()];}
int width (int level) const {return fbo[level]->wid;}
int height(int level) const {return fbo[level]->hei;}
QSize size(int level) const {return fbo[level]->size();}
QRect rect(int level) const {return fbo[level]->rect();}
void resize();
void create();
private:
int index_from;
const Framebuffer & src_fb;
QVector<Framebuffer*> fbo;
};
#endif // GLFRAMEBUFFER_MIPMAP_H

151
core/glmaterial.cpp Normal file
View File

@@ -0,0 +1,151 @@
/*
QGL Material
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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;
Map::Map() {
bitmap_id = 0;
color_amount = 1.f;
color_offset = 0.f;
bitmap_scale = QPointF(1., 1.);
use_bitmap = false;
_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() && use_bitmap) {
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 = Qt::white;
color_emission = Qt::black;
glass = false;
transparency = reflectivity = 0.f;
map_roughness.color_amount = 0.75f;
map_metalness.color_amount = 0.25f;
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_metalness._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 mtMetalness: return map_metalness._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_metalness.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_metalness._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_metalness._type = mtMetalness;
map_roughness._type = mtRoughness;
map_emission ._type = mtEmission ;
map_relief ._type = mtRelief ;
}
void Material::detectMaps() {
map_diffuse .use_bitmap = !map_diffuse .bitmap_path.isEmpty();
map_normal .use_bitmap = !map_normal .bitmap_path.isEmpty();
map_metalness.use_bitmap = !map_metalness.bitmap_path.isEmpty();
map_roughness.use_bitmap = !map_roughness.bitmap_path.isEmpty();
map_emission .use_bitmap = !map_emission .bitmap_path.isEmpty();
map_relief .use_bitmap = !map_relief .bitmap_path.isEmpty();
}

124
core/glmaterial.h Normal file
View File

@@ -0,0 +1,124 @@
/*
QGL Material
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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 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 use_bitmap;
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();
void detectMaps();
QString name;
QColor color_diffuse;
QColor color_emission;
bool glass;
float transparency;
float reflectivity;
float iof;
float dispersion;
Map map_diffuse ;
Map map_normal ;
Map map_metalness;
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)
.add(7, m.use_bitmap);
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)
.get(7, m.use_bitmap);
return s;
}
inline QDataStream & operator <<(QDataStream & s, const Material * m) {
ChunkStream cs;
cs.add(1, m->name).add(2, m->color_diffuse).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_metalness).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 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_metalness); break;
case 12: cs.get(m->map_roughness); break;
case 13: cs.get(m->map_emission); break;
}
}
m->setTypes();
return s;
}
#endif // GLMATERIAL_H

426
core/glmesh.cpp Normal file
View File

@@ -0,0 +1,426 @@
/*
QGL Mesh
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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) {
hash_ = 0;
changed = hash_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);
changed = true;
}
}
void Mesh::destroy(QOpenGLExtraFunctions * f) {
buffer_geom.destroy(f);
buffer_ind .destroy(f);
QList<VertexObject*> vaol = vao_map.values();
foreach (VertexObject* vao, vaol)
vao->destroy(f);
qDeleteAll(vao_map);
vao_map.clear();
}
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();
}
VertexObject * Mesh::vaoByType(int type) {
VertexObject *& vao(vao_map[type]);
if (!vao) {
vao = new VertexObject();
}
return vao;
}
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);
buffer_geom.bind(f);
buffer_geom.resize(f, gsize);
buffer_geom.load(f, data_.constData(), gsize);
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);
}
return !isEmpty();
}
void Mesh::draw(QOpenGLExtraFunctions * f, int count, int type) {
if (isEmpty()) return;
if (!isInit()) init(f);
if (changed) rebuffer(f);
//qDebug() << "draw" << geom_type << vert_count << count;
VertexObject * vao = vaoByType(type);
vao->bindBuffers(f, buffer_geom, buffer_ind);
if (geom_type == GL_TRIANGLES)
vao->draw(f, geom_type, triangles_.size() * 3, count);
else
vao->draw(f, geom_type, lines_.size() * 2, count);
}
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, int type) {
VertexObject * vao = vaoByType(type);
vao->loadObject(f, object);
}
void Mesh::loadObjects(QOpenGLExtraFunctions * f, const QVector<Object> & objects, int type) {
VertexObject * vao = vaoByType(type);
vao->loadObjects(f, objects);
}
void Mesh::loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels, int type) {
VertexObject * vao = vaoByType(type);
vao->loadSelections(f, sels);
}
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_;
}
bool Mesh::isObjectsChanged(int type) const {
return (const_cast<Mesh*>(this))->vaoByType(type)->isObjectsChanged();
}
bool Mesh::isSelectionChanged(int type) const {
return (const_cast<Mesh*>(this))->vaoByType(type)->isSelectionChanged();
}
void Mesh::setObjectsChanged(int type, bool yes) {
vaoByType(type)->setObjectsChanged(yes);
}
void Mesh::setSelectionChanged(int type, bool yes) {
vaoByType(type)->setSelectionChanged(yes);
}
void Mesh::setAllObjectsChanged(bool yes) {
QMapIterator<int, VertexObject * > it(vao_map);
while (it.hasNext())
it.next().value()->setObjectsChanged(yes);
}
void Mesh::setAllSelectionChanged(bool yes) {
QMapIterator<int, VertexObject * > it(vao_map);
while (it.hasNext())
it.next().value()->setSelectionChanged(yes);
}
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::flipNormals() {
if (vertices_.isEmpty()) return;
for (int i = 0; i < triangles_.size(); ++i)
piSwap(triangles_[i].p1, triangles_[i].p2);
for (int i = 0; i < lines_.size(); ++i)
piSwap(lines_[i].p0, lines_[i].p1);
int ncnt = normals_.size();
for (int i = 0; i < ncnt; ++i)
normals_[i] = -normals_[i];
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;
}

110
core/glmesh.h Normal file
View File

@@ -0,0 +1,110 @@
/*
QGL Mesh
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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 "glvertexobject.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, int type = 0);
void clear();
void loadObject (QOpenGLExtraFunctions * f, const QGLEngineShaders::Object & object, int type = 0);
void loadObjects (QOpenGLExtraFunctions * f, const QVector<QGLEngineShaders::Object> & objects, int type = 0);
void loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels, int type = 0);
int verticesCount() const {return vertices_.size();}
int trianglesCount() const {return triangles_.size();}
int linesCount() const {return lines_.size();}
bool isInit() const {return buffer_geom.isInit();}
bool isEmpty() const {return vertices_.isEmpty();}
uint hash() const;
bool isObjectsChanged (int type = 0) const;
bool isSelectionChanged (int type = 0) const;
void setObjectsChanged (int type = 0, bool yes = true);
void setSelectionChanged(int type = 0, bool yes = true);
void setAllObjectsChanged (bool yes = true);
void setAllSelectionChanged(bool yes = true);
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 flipNormals();
void append(const Mesh * m);
bool saveToFile(const QString & filename);
bool loadFromFile(const QString & filename);
Box3D boundingBox() const;
private:
void calculateNormals();
void calculateTangents();
VertexObject * vaoByType(int type);
QVector<QVector3D> vertices_, normals_, tangents_, bitangents_;
QVector<QVector2D> texcoords_;
QVector< Vector3i> triangles_;
QVector< Vector2i> lines_;
QVector<QGLEngineShaders::Vertex> data_;
GLenum geom_type;
Buffer buffer_geom, buffer_ind;
QMap<int, VertexObject * > vao_map;
mutable uint hash_;
mutable bool hash_changed;
int vert_count;
bool changed;
};
QDataStream & operator <<(QDataStream & s, const Mesh * m);
QDataStream & operator >>(QDataStream & s, Mesh *& m);
#endif // GLMESH_H

437
core/glprimitives.cpp Normal file
View File

@@ -0,0 +1,437 @@
/*
QGL Primitives
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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 radius, 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());
int hseg = segments_h + 1, wlseg = segments_wl + 1;
double crw, crl, a, ch, twl;
double eang = deg2rad * end_angle;
QVector3D cp;
for (int i = 0; i <= hseg; i++) {
ch = -cos((double)i / hseg * M_PI);
cp.setZ(ch * radius);
twl = sqrt(1. - ch * ch);
crw = twl * radius;
crl = twl * radius;
int cvcnt = wlseg * 2;
for (int j = 0; j < cvcnt; j++) {
a = (double)j / (cvcnt - 1) * eang;
cp.setX(crl * cos(a));
cp.setY(crw * sin(a));
v << cp;
t << QVector2D((double)j / (cvcnt - 1), ch/2.f + 0.5f);
n << cp.normalized();
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);
}
}
}
if (end_angle < 360.) {
Mesh * cap = Primitive::disc(segments_h+1, radius, 180);
cap->rotatePoints(90, 0, 1, 0);
cap->rotatePoints(-90, 0, 0, 1);
ret->append(cap);
cap->flipNormals();
cap->rotatePoints(end_angle, 0, 0, 1);
ret->append(cap);
delete cap;
}
return ret;
}
Mesh * Primitive::disc(int segments, float radius, 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();
n << QVector3D(0, 0, 1);
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(radius * cos(a));
cp.setY(radius * sin(a));
v << cp;
n << QVector3D(0, 0, 1);
t << QVector2D(cp.x() / radius + 1, cp.y() / radius + 1);
int si = v.size() - 1;
if (i > 0) ind << Vector3i(si - 1, si, 0);
}
return ret;
}
QVector3D coneNormal(double r, double height, double ang) {
QVector3D norm;
norm.setX(r * cos(ang));
norm.setY(r * 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 radius, 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);
QVector3D cp;
for (int i = 0; i < seg; i++) {
double a = (double)i / (seg - 1) * M_2PI;
cp.setX(radius * cos(a));
cp.setY(radius * 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(radius, height, ta);
}
v << cp;
t << QVector2D((double)i / (seg - 1), 0.f);
n << coneNormal(radius, height, a);
int si = v.size() - 1;
if (i > 0)
ind << Vector3i(si - 1, si - 2, si);
}
Mesh * cap = Primitive::disc(segments, radius);
cap->flipNormals();
ret->append(cap);
delete cap;
return ret;
}
Mesh * Primitive::cylinder(int segments, float radius, float height, 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());
int seg = qMax(segments + 1, 4);
QVector3D cp, norm;
double eang = deg2rad * end_angle;
for (int i = 0; i < seg; i++) {
double a = (double)i / (seg - 1) * eang;
cp.setX(radius * cos(a));
cp.setY(radius * 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, radius, end_angle);
cap->flipNormals();
ret->append(cap);
cap->translatePoints(QVector3D(0., 0., height));
cap->flipNormals();
ret->append(cap);
delete cap;
if (end_angle < 360.) {
Mesh * cap = Primitive::plane(radius, height);
cap->rotatePoints(90, 1, 0, 0);
//cap->rotatePoints(-90, 0, 0, 1);
cap->translatePoints(radius/2, 0, height/2);
ret->append(cap);
cap->flipNormals();
cap->rotatePoints(end_angle, 0, 0, 1);
ret->append(cap);
delete cap;
}
return ret;
}
Mesh * Primitive::arrow(int segments, float thick, float angle) {
double cone_r = 1.5 * thick;
double cone_h = 2. * cone_r / tan(angle * deg2rad);
Mesh * ret = new Mesh();
Mesh * m = Primitive::cylinder(segments, thick / 2., 1. - cone_h);
ret->append(m);
delete m;
m = Primitive::cone(segments, cone_r, 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();
}
if (end_angle < 360.) {
Mesh * cap = Primitive::disc(segments_second-1, radius_second);
cap->rotatePoints(90, 1, 0, 0);
cap->translatePoints(radius_main, 0, 0);
ret->append(cap);
cap->flipNormals();
cap->rotatePoints(end_angle, 0, 0, 1);
ret->append(cap);
delete cap;
}
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 radius) {
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 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 * radius);
twl = sqrt(1. - ch * ch);
crw = twl * radius;
crl = twl * radius;
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);
n << cp.normalized();
int si = v.size() - 1;
if (j > 0 && i > 0) {
ind << Vector2i(si, si - 1);
ind << Vector2i(si - cvcnt, si);
}
}
}
return ret;
}
Mesh * Primitive::coneFrame(int segments, float radius, 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);
QVector3D cp;
for (int i = 0; i < seg; i++) {
double a = (double)i / (seg - 1) * M_2PI;
cp.setX(radius * cos(a));
cp.setY(radius * 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(radius, height, ta);
}
v << cp;
t << QVector2D((double)i / (seg - 1), 0.f);
n << coneNormal(radius, height, a);
int si = v.size() - 1;
if (i > 0) {
ind << Vector2i(si - 1, si);
ind << Vector2i(si - 2, si);
}
}
return ret;
}
Mesh * Primitive::lineFrame(QVector3D p0, QVector3D p1) {
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());
v << p0 << p1;
n << QVector3D(0,0,1) << QVector3D(0,0,1);
t << QVector2D(0,0) << QVector2D(1,0);
ind << Vector2i(0, 1);
return ret;
}

104
core/glprimitives.h Normal file
View File

@@ -0,0 +1,104 @@
/*
QGL Primitives
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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 radius = 1., float end_angle = 360.);
Mesh * disc(int segments, float radius = 1., float end_angle = 360.);
Mesh * cone(int segments, float radius = 1., float height = 1.);
Mesh * cylinder(int segments, float radius = 1., float height = 1., float end_angle = 360.);
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 * lineFrame(QVector3D p0, QVector3D p1);
Mesh * cubeFrame(float width = 1., float length = 1., float height = 1.);
Mesh * ellipsoidFrame(int segments_wl, int segments_h, float radius = 1.);
Mesh * coneFrame(int segments, float radius = 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

146
core/glshaders.cpp Normal file
View File

@@ -0,0 +1,146 @@
/*
QGLEngineShaders
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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, bool add_qgl, const QString & defs) {
if (type == 0 || content.isEmpty()) {
content.clear();
return true;
}
//qDebug() << "[QGLEngine] Shader" << file << "found" << (QOpenGLShader::ShaderTypeBit)(int)type << "section ...";
if (add_qgl) {
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() << "[QGLEngine] 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, bool add_qgl, const QStringList & defines) {
if (!prog)
prog = new QOpenGLShaderProgram();
prog->removeAllShaders();
QFile f(file);
if (!f.open(QIODevice::ReadOnly)) {
qDebug() << "[QGLEngine] Shader" << file << "Error: can`t open file!";
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, add_qgl, defs)) return false;
type = QOpenGLShader::Vertex;
continue;
}
if (pl == "fragment" || pl == "frag") {
if (!addShader(prog, type, cur_shader, file, add_qgl, defs)) return false;
type = QOpenGLShader::Fragment;
continue;
}
if (pl == "geometry" || pl == "geom") {
if (!addShader(prog, type, cur_shader, file, add_qgl, defs)) return false;
type = QOpenGLShader::Geometry;
continue;
}
if (pl == "tessellation_control") {
if (!addShader(prog, type, cur_shader, file, add_qgl, defs)) return false;
type = QOpenGLShader::TessellationControl;
continue;
}
if (pl == "tessellation_evaluation") {
if (!addShader(prog, type, cur_shader, file, add_qgl, defs)) return false;
type = QOpenGLShader::TessellationEvaluation;
continue;
}
cur_shader.append("\n");
cur_shader.append(line);
}
if (!addShader(prog, type, cur_shader, file, add_qgl, defs)) return false;
if (!prog->link()) {
qDebug() << "[QGLEngine] Shader" << file << "Link error:\n" << prog->log();
return false;
}
qDebug() << "[QGLEngine] Shader" << file << "ok";
return true;
}
bool QGLEngineShaders::loadShaders(QOpenGLShaderProgram *& prog, const QStringList & files, bool add_qgl, const QStringList & defines) {
if (!prog)
prog = new QOpenGLShaderProgram();
prog->removeAllShaders();
QString cur_shader, defs = prepareDefines(defines);
foreach (QString f, files) {
QFileInfo fi(f);
QOpenGLShader::ShaderType type = 0;
if (fi.suffix().toLower() == "vert") type = QOpenGLShader::Vertex ;
if (fi.suffix().toLower() == "frag") type = QOpenGLShader::Fragment;
if (fi.suffix().toLower() == "geom") type = QOpenGLShader::Geometry;
if (type == 0) continue;
QFile file(f);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "[QGLEngine] Shader" << f << "Error: can`t open file!";
return false;
}
cur_shader = file.readAll();
if (!addShader(prog, type, cur_shader, f, add_qgl, defs)) return false;
}
if (!prog->link()) {
qDebug() << "[QGLEngine] Shader" << files << "Link error:\n" << prog->log();
return false;
}
qDebug() << "[QGLEngine] Shader" << files << "ok";
return true;
}

31
core/glshaders.h Normal file
View File

@@ -0,0 +1,31 @@
/*
QGLEngineShaders
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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, bool add_qgl = true, const QStringList & defines = QStringList());
bool loadShaders(QOpenGLShaderProgram *& prog, const QStringList & files, bool add_qgl = true, const QStringList & defines = QStringList());
}
#endif // GLSHADERS_H

127
core/glshaders_headers.h Normal file
View File

@@ -0,0 +1,127 @@
/*
QGLEngineShaders
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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 = 256 ;
const char qgl_common_head[] =
"#version 400 core\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 inverse(mat3(qgl_ViewMatrix * qgl_ModelMatrix));}\n"
"mat3 qgl_getTangentMatrix() {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.rgb = t.rgb * 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_METALNESS 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"
" vec4 decay_intensity;\n"
" vec4 angles;\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[256];\n"
"};\n"
"layout (std140) uniform QGLLightPositionData {\n"
" QGLLightPosition qgl_light_position[256];\n"
"};\n"
"uniform sampler2DArray qgl_texture_array[2];\n"
"";
}
#endif // GLSHADERS_HEADERS_H

97
core/glshaders_types.cpp Normal file
View File

@@ -0,0 +1,97 @@
/*
QGLEngineShaders
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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(1., 1., 1., 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;
map[mtMetalness].amount = 0.25;
}
void QGLEngineShaders::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 QGLEngineShaders::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 QGLEngineShaders::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);
}

144
core/glshaders_types.h Normal file
View File

@@ -0,0 +1,144 @@
/*
QGLEngineShaders
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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,
mtMetalness = 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_emission;
GLfloat transparency;
GLfloat reflectivity;
GLfloat iof;
GLfloat dispersion;
QGLMap map[QGL_MAPS_COUNT];
};
struct QGLLightParameter {
QVector4D color;
QVector4D decay_intensity; // [^0, ^1, ^2, intensity]
QVector4D angles; // [start, cos(start), end, cos(end)]
//GLfloat shadow;
//GLfloat shadowMatrix[16];
};
struct QGLLightPosition {
QVector4D position;
QVector4D direction;
};
#pragma pack(pop)
void prepareDrawGeom(QOpenGLExtraFunctions * f);
void prepareDrawObj (QOpenGLExtraFunctions * f);
void prepareDrawSel (QOpenGLExtraFunctions * f);
}
#endif // GLSHADERS_TYPES_H

96
core/gltexturearray.cpp Normal file
View File

@@ -0,0 +1,96 @@
/*
QGL Texture2DArray
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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_);
}

56
core/gltexturearray.h Normal file
View File

@@ -0,0 +1,56 @@
/*
QGL Texture2DArray
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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

441
core/gltransform.cpp Normal file
View File

@@ -0,0 +1,441 @@
/*
QGL Transform
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gltransform.h"
#include "gltypes.h"
#include <cmath>
inline void composeQMatrix4x4(const QVector3D & position, const QVector3D & orientation, const QVector3D & scale, QMatrix4x4 &m) {
const QMatrix3x3 rot3x3(Transform::toRotationMatrix(orientation));
// set up final matrix with scale, rotation and translation
m(0, 0) = scale.x() * rot3x3(0, 0); m(0, 1) = scale.y() * rot3x3(0, 1); m(0, 2) = scale.z() * rot3x3(0, 2); m(0, 3) = position.x();
m(1, 0) = scale.x() * rot3x3(1, 0); m(1, 1) = scale.y() * rot3x3(1, 1); m(1, 2) = scale.z() * rot3x3(1, 2); m(1, 3) = position.y();
m(2, 0) = scale.x() * rot3x3(2, 0); m(2, 1) = scale.y() * rot3x3(2, 1); m(2, 2) = scale.z() * rot3x3(2, 2); m(2, 3) = position.z();
// no projection term
m(3, 0) = 0.0f; m(3, 1) = 0.0f; m(3, 2) = 0.0f; m(3, 3) = 1.0f;
}
inline void decomposeQMatrix3x3(const QMatrix3x3 & m, QMatrix3x3 & Q, QVector3D & D, QVector3D & U) {
// Factor M = QR = QDU where Q is orthogonal, D is diagonal,
// and U is upper triangular with ones on its diagonal.
// Algorithm uses Gram-Schmidt orthogonalization (the QR algorithm).
//
// If M = [ m0 | m1 | m2 ] and Q = [ q0 | q1 | q2 ], then
// q0 = m0/|m0|
// q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0|
// q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1|
//
// where |V| indicates length of vector V and A*B indicates dot
// product of vectors A and B. The matrix R has entries
//
// r00 = q0*m0 r01 = q0*m1 r02 = q0*m2
// r10 = 0 r11 = q1*m1 r12 = q1*m2
// r20 = 0 r21 = 0 r22 = q2*m2
//
// so D = diag(r00,r11,r22) and U has entries u01 = r01/r00,
// u02 = r02/r00, and u12 = r12/r11.
// Q = rotation
// D = scaling
// U = shear
// D stores the three diagonal entries r00, r11, r22
// U stores the entries U[0] = u01, U[1] = u02, U[2] = u12
// build orthogonal matrix Q
float invLen = 1.0f / std::sqrt(m(0, 0) * m(0, 0) + m(1, 0) * m(1, 0) + m(2, 0) * m(2, 0));
Q(0, 0) = m(0, 0) * invLen;
Q(1, 0) = m(1, 0) * invLen;
Q(2, 0) = m(2, 0) * invLen;
float dot = Q(0, 0) * m(0, 1) + Q(1, 0) * m(1, 1) + Q(2, 0) * m(2, 1);
Q(0, 1) = m(0, 1) - dot * Q(0, 0);
Q(1, 1) = m(1, 1) - dot * Q(1, 0);
Q(2, 1) = m(2, 1) - dot * Q(2, 0);
invLen = 1.0f / std::sqrt(Q(0, 1) * Q(0, 1) + Q(1, 1) * Q(1, 1) + Q(2, 1) * Q(2, 1));
Q(0, 1) *= invLen;
Q(1, 1) *= invLen;
Q(2, 1) *= invLen;
dot = Q(0, 0) * m(0, 2) + Q(1, 0) * m(1, 2) + Q(2, 0) * m(2, 2);
Q(0, 2) = m(0, 2) - dot * Q(0, 0);
Q(1, 2) = m(1, 2) - dot * Q(1, 0);
Q(2, 2) = m(2, 2) - dot * Q(2, 0);
dot = Q(0, 1) * m(0, 2) + Q(1, 1) * m(1, 2) + Q(2, 1) * m(2, 2);
Q(0, 2) -= dot * Q(0, 1);
Q(1, 2) -= dot * Q(1, 1);
Q(2, 2) -= dot * Q(2, 1);
invLen = 1.0f / std::sqrt(Q(0, 2) * Q(0, 2) + Q(1, 2) * Q(1, 2) + Q(2, 2) * Q(2, 2));
Q(0, 2) *= invLen;
Q(1, 2) *= invLen;
Q(2, 2) *= invLen;
// guarantee that orthogonal matrix has determinant 1 (no reflections)
const float det = Q(0, 0) * Q(1, 1) * Q(2, 2) + Q(0, 1) * Q(1, 2) * Q(2, 0) +
Q(0, 2) * Q(1, 0) * Q(2, 1) - Q(0, 2) * Q(1, 1) * Q(2, 0) -
Q(0, 1) * Q(1, 0) * Q(2, 2) - Q(0, 0) * Q(1, 2) * Q(2, 1);
if (det < 0.0f)
Q *= -1.0f;
// build "right" matrix R
QMatrix3x3 R(Qt::Uninitialized);
R(0, 0) = Q(0, 0) * m(0, 0) + Q(1, 0) * m(1, 0) + Q(2, 0) * m(2, 0);
R(0, 1) = Q(0, 0) * m(0, 1) + Q(1, 0) * m(1, 1) + Q(2, 0) * m(2, 1);
R(1, 1) = Q(0, 1) * m(0, 1) + Q(1, 1) * m(1, 1) + Q(2, 1) * m(2, 1);
R(0, 2) = Q(0, 0) * m(0, 2) + Q(1, 0) * m(1, 2) + Q(2, 0) * m(2, 2);
R(1, 2) = Q(0, 1) * m(0, 2) + Q(1, 1) * m(1, 2) + Q(2, 1) * m(2, 2);
R(2, 2) = Q(0, 2) * m(0, 2) + Q(1, 2) * m(1, 2) + Q(2, 2) * m(2, 2);
// the scaling component
D[0] = R(0, 0);
D[1] = R(1, 1);
D[2] = R(2, 2);
// the shear component
U[0] = R(0, 1) / D[0];
U[1] = R(0, 2) / D[0];
U[2] = R(1, 2) / D[1];
}
inline bool hasScale(const QMatrix4x4 &m) {
// If the columns are orthonormal and form a right-handed system, then there is no scale
float t(m.determinant());
if (!qFuzzyIsNull(t - 1.0f))
return true;
t = m(0, 0) * m(0, 0) + m(1, 0) * m(1, 0) + m(2, 0) * m(2, 0);
if (!qFuzzyIsNull(t - 1.0f))
return true;
t = m(0, 1) * m(0, 1) + m(1, 1) * m(1, 1) + m(2, 1) * m(2, 1);
if (!qFuzzyIsNull(t - 1.0f))
return true;
t = m(0, 2) * m(0, 2) + m(1, 2) * m(1, 2) + m(2, 2) * m(2, 2);
if (!qFuzzyIsNull(t - 1.0f))
return true;
return false;
}
inline void decomposeQMatrix4x4(const QMatrix4x4 & m, QVector3D & position, QVector3D & orientation, QVector3D & scale) {
Q_ASSERT(m.isAffine());
const QMatrix3x3 m3x3(m.toGenericMatrix<3, 3>());
QMatrix3x3 rot3x3(Qt::Uninitialized);
if (hasScale(m)) {
decomposeQMatrix3x3(m3x3, rot3x3, scale, position);
} else {
// we know there is no scaling part; no need for QDU decomposition
scale = QVector3D(1.0f, 1.0f, 1.0f);
rot3x3 = m3x3;
}
orientation = Transform::fromRotationMatrix(rot3x3);
position = QVector3D(m(0, 3), m(1, 3), m(2, 3));
}
Transform::Transform(): m_scale(1.0f, 1.0f, 1.0f),
m_translation(), m_eulerRotationAngles(), m_matrixDirty(false) {
}
Transform & Transform::operator =(const Transform & t) {
m_scale = t.m_scale;
m_translation = t.m_translation;
m_eulerRotationAngles = t.m_eulerRotationAngles;
m_matrixDirty = true;
return *this;
}
void Transform::setMatrix(const QMatrix4x4 & m) {
if (m != matrix()) {
m_matrix = m;
m_matrixDirty = false;
QVector3D s;
QVector3D t;
QVector3D r;
decomposeQMatrix4x4(m, t, r, s);
m_scale = s;
m_translation = t;
m_eulerRotationAngles = r;
}
}
void Transform::setRotationX(float r) {
if (m_eulerRotationAngles.x() == r)
return;
m_eulerRotationAngles.setX(r);
m_matrixDirty = true;
}
void Transform::setRotationY(float r) {
if (m_eulerRotationAngles.y() == r)
return;
m_eulerRotationAngles.setY(r);
m_matrixDirty = true;
}
void Transform::setRotationZ(float r) {
if (m_eulerRotationAngles.z() == r)
return;
m_eulerRotationAngles.setZ(r);
m_matrixDirty = true;
}
QMatrix4x4 Transform::matrix() const {
buildMatrix();
return m_matrix;
}
QMatrix4x4 Transform::matrixRotate() const {
buildMatrix();
return m_matrixR;
}
QMatrix4x4 Transform::matrixScale() const {
buildMatrix();
return m_matrixS;
}
QMatrix4x4 Transform::matrixRotateScale() const {
buildMatrix();
return m_matrixWT;
}
QVector3D Transform::direction() const {
return matrixRotate().mapVector(QVector3D(0,0,-1)).normalized();
}
void Transform::buildMatrix() const {
if (m_matrixDirty) {
composeQMatrix4x4(m_translation, m_eulerRotationAngles, m_scale, m_matrix);
composeQMatrix4x4(QVector3D(), m_eulerRotationAngles, m_scale, m_matrixWT);
composeQMatrix4x4(QVector3D(), m_eulerRotationAngles, QVector3D(1,1,1), m_matrixR);
composeQMatrix4x4(QVector3D(), QVector3D(), m_scale, m_matrixS);
m_matrixDirty = false;
}
}
float Transform::rotationX() const {
return m_eulerRotationAngles.x();
}
float Transform::rotationY() const {
return m_eulerRotationAngles.y();
}
float Transform::rotationZ() const {
return m_eulerRotationAngles.z();
}
void Transform::setScale(const QVector3D & s) {
if (s != m_scale) {
m_scale = s;
m_matrixDirty = true;
}
}
void Transform::setScaleX(float s) {
if (s != m_scale.x()) {
m_scale.setX(s);
m_matrixDirty = true;
}
}
void Transform::setScaleY(float s) {
if (s != m_scale.y()) {
m_scale.setY(s);
m_matrixDirty = true;
}
}
void Transform::setScaleZ(float s) {
if (s != m_scale.z()) {
m_scale.setZ(s);
m_matrixDirty = true;
}
}
QVector3D Transform::scale3D() const {
return m_scale;
}
float Transform::scale() const {
return m_scale.x();
}
void Transform::setRotation(const QVector3D & r) {
if (r != m_eulerRotationAngles) {
m_eulerRotationAngles = r;
m_matrixDirty = true;
}
}
QVector3D Transform::rotation() const {
return m_eulerRotationAngles;
}
void Transform::setTranslation(const QVector3D & t) {
if (t != m_translation) {
m_translation = t;
m_matrixDirty = true;
}
}
void Transform::setTranslationX(float t) {
if (t != m_translation.x()) {
m_translation.setX(t);
m_matrixDirty = true;
}
}
void Transform::setTranslationY(float t) {
if (t != m_translation.y()) {
m_translation.setY(t);
m_matrixDirty = true;
}
}
void Transform::setTranslationZ(float t) {
if (t != m_translation.z()) {
m_translation.setZ(t);
m_matrixDirty = true;
}
}
QVector3D Transform::translation() const {
return m_translation;
}
QQuaternion Transform::fromAxisAndAngle(const QVector3D & axis, float angle) {
return QQuaternion::fromAxisAndAngle(axis, angle);
}
QQuaternion Transform::fromAxisAndAngle(float x, float y, float z, float angle) {
return QQuaternion::fromAxisAndAngle(x, y, z, angle);
}
QQuaternion Transform::fromAxesAndAngles(const QVector3D & axis1, float angle1,
const QVector3D & axis2, float angle2) {
const QQuaternion q1 = QQuaternion::fromAxisAndAngle(axis1, angle1);
const QQuaternion q2 = QQuaternion::fromAxisAndAngle(axis2, angle2);
return q2 * q1;
}
QQuaternion Transform::fromAxesAndAngles(const QVector3D & axis1, float angle1,
const QVector3D & axis2, float angle2,
const QVector3D & axis3, float angle3) {
const QQuaternion q1 = QQuaternion::fromAxisAndAngle(axis1, angle1);
const QQuaternion q2 = QQuaternion::fromAxisAndAngle(axis2, angle2);
const QQuaternion q3 = QQuaternion::fromAxisAndAngle(axis3, angle3);
return q3 * q2 * q1;
}
QQuaternion Transform::fromAxes(const QVector3D & xAxis, const QVector3D & yAxis, const QVector3D & zAxis) {
return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
}
QVector3D Transform::fromDirection(QVector3D d, float pitch) {
QVector3D ret;
d.normalize();
ret[0] = M_PI - acos(d.z());
ret[1] = pitch * deg2rad;
ret[2] = -atan2(d.x(), d.y());
normalizeAngleRad(ret[0]);
normalizeAngleRad(ret[2]);
return ret * rad2deg;
}
QVector3D Transform::fromRotationMatrix(const QMatrix3x3 & m) {
//return QQuaternion::fromRotationMatrix(m);
float sy = sqrt(m(0,0) * m(0,0) + m(1,0) * m(1,0));
bool singular = sy < 1.E-6; // If
float x, y, z;
if (!singular) {
x = atan2( m(2,1), m(2,2));
y = atan2(-m(2,0), sy);
z = atan2( m(1,0), m(0,0));
} else {
x = atan2(-m(1,2), m(1,1));
y = atan2(-m(2,0), sy);
z = 0.;
}
return QVector3D(x, y, z) * rad2deg;
}
QMatrix3x3 Transform::toRotationMatrix(const QVector3D & r) {
QMatrix4x4 m;
if (r.z() != 0.f) m.rotate(r.z(), 0., 0., 1.);
if (r.y() != 0.f) m.rotate(r.y(), 0., 1., 0.);
if (r.x() != 0.f) m.rotate(r.x(), 1., 0., 0.);
return m.toGenericMatrix<3,3>();
}
QMatrix4x4 Transform::rotateAround(const QVector3D & point, float angle, const QVector3D & axis) {
QMatrix4x4 m;
m.translate(point);
m.rotate(angle, axis);
m.translate(-point);
return m;
}
QMatrix4x4 Transform::rotateFromAxes(const QVector3D & xAxis, const QVector3D & yAxis, const QVector3D & zAxis) {
return QMatrix4x4(xAxis.x(), yAxis.x(), zAxis.x(), 0.0f,
xAxis.y(), yAxis.y(), zAxis.y(), 0.0f,
xAxis.z(), yAxis.z(), zAxis.z(), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
}

119
core/gltransform.h Normal file
View File

@@ -0,0 +1,119 @@
/*
QGL Transform
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLTRANSFORM_H
#define GLTRANSFORM_H
#include <QMatrix4x4>
#include <QQuaternion>
#include <QVector3D>
#include <chunkstream.h>
class Transform {
friend QDataStream & operator >>(QDataStream & s, Transform & v);
public:
Transform();
Transform & operator =(const Transform & t);
float scale() const;
QVector3D scale3D() const;
QVector3D rotation() const;
QVector3D translation() const;
QMatrix4x4 matrix() const;
QMatrix4x4 matrixRotate() const;
QMatrix4x4 matrixScale() const;
QMatrix4x4 matrixRotateScale() const;
QVector3D direction() const;
float rotationX() const;
float rotationY() const;
float rotationZ() const;
void setScale(float s) {setScale(QVector3D(s, s, s));}
void setScale(const QVector3D & s);
void setScaleX(float s);
void setScaleY(float s);
void setScaleZ(float s);
void setRotation(const QVector3D & r);
void setRotationX(float r);
void setRotationY(float r);
void setRotationZ(float r);
void setTranslation(const QVector3D & t);
void setTranslationX(float t);
void setTranslationY(float t);
void setTranslationZ(float t);
void setMatrix(const QMatrix4x4 & matrix);
void setDirty(bool yes = true) {m_matrixDirty = yes;}
static QQuaternion fromAxisAndAngle(const QVector3D & axis, float angle);
static QQuaternion fromAxisAndAngle(float x, float y, float z, float angle);
static QQuaternion fromAxesAndAngles(const QVector3D & axis1, float angle1,
const QVector3D & axis2, float angle2);
static QQuaternion fromAxesAndAngles(const QVector3D & axis1, float angle1,
const QVector3D & axis2, float angle2,
const QVector3D & axis3, float angle3);
static QQuaternion fromAxes(const QVector3D & xAxis, const QVector3D & yAxis, const QVector3D & zAxis);
static QVector3D fromDirection(QVector3D d, float pitch = 0.f);
static QVector3D fromRotationMatrix(const QMatrix3x3 & m);
static QMatrix3x3 toRotationMatrix(const QVector3D & r);
static QMatrix4x4 rotateAround(const QVector3D & point, float angle, const QVector3D & axis);
static QMatrix4x4 rotateFromAxes(const QVector3D & xAxis, const QVector3D & yAxis, const QVector3D & zAxis);
protected:
void buildMatrix() const;
QVector3D m_scale;
QVector3D m_translation;
QVector3D m_eulerRotationAngles;
mutable QMatrix4x4 m_matrix, m_matrixWT, m_matrixR, m_matrixS;
mutable bool m_matrixDirty;
};
inline QDataStream & operator <<(QDataStream & s, const Transform & v) {
//ChunkStream cs;
//cs.add(1, v.matrix());
//s << cs.data(); return s;
s << v.matrix(); return s;
}
inline QDataStream & operator >>(QDataStream & s, Transform & v) {
//ChunkStream cs(s);
//while (!cs.atEnd()) {
// switch (cs.read()) {
// case 1: v.setMatrix(cs.getData<QMatrix4x4>()); break;
// }
//}
//return s;
QMatrix4x4 m;
s >> m;
v.setMatrix(m);
v.m_matrixDirty = true;
return s;
}
#endif // QT3DCORE_QTRANSFORM_H

373
core/gltypes.cpp Normal file
View File

@@ -0,0 +1,373 @@
/*
QGLView Types
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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_) {
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;
}

303
core/gltypes.h Normal file
View File

@@ -0,0 +1,303 @@
/*
QGLView Types
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser 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>
#include "qglengine_version.h"
//#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 T> inline void piSwap(T & f, T & s) {T 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_);
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 Framebuffer;
class FramebufferMipmap;
class VertexObject;
class Mesh;
class Scene;
class RendererBase;
class Renderer;
class RendererMaterial;
class RendererService;
class RendererSelection;
enum RenderPass {
rpSolid,
rpTransparent,
};
typedef QList<ObjectBase*> ObjectBaseList;
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 void normalizeAngleRad(float & a) {while (a < 0.) a += M_2PI; while (a >= M_2PI) a -= M_2PI;}
inline void normalizeAngleDeg(float & a) {while (a < 0.) a += 360. ; while (a >= 360. ) a -= 360. ;}
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

119
core/glvertexobject.cpp Normal file
View File

@@ -0,0 +1,119 @@
/*
QGL VertexObject
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define GL_GLEXT_PROTOTYPES
#include <QOpenGLExtraFunctions>
#include "glvertexobject.h"
using namespace QGLEngineShaders;
VertexObject::VertexObject():
buffer_obj (GL_ARRAY_BUFFER, GL_STREAM_DRAW),
buffer_sel (GL_ARRAY_BUFFER, GL_STREAM_DRAW) {
vao_ = 0;
buffers_binded = false;
objects_changed = selected_changed = true;
}
VertexObject::~VertexObject() {
}
void VertexObject::init(QOpenGLExtraFunctions * f) {
if (!isInit()) {
buffer_obj.init(f);
buffer_sel.init(f);
f->glGenVertexArrays(1, &vao_);
}
}
void VertexObject::destroy(QOpenGLExtraFunctions * f) {
if (vao_ != 0) {
buffer_obj.destroy(f);
buffer_sel.destroy(f);
f->glDeleteVertexArrays(1, &vao_);
}
vao_ = 0;
}
void VertexObject::bind(QOpenGLExtraFunctions * f) {
//qDebug() << "bind" << target_ << buffer_;
f->glBindVertexArray(vao_);
}
void VertexObject::release(QOpenGLExtraFunctions * f) {
f->glBindVertexArray(0);
}
void VertexObject::bindBuffers(QOpenGLExtraFunctions * f, Buffer & geom, Buffer & elem, bool force) {
if (!force && buffers_binded) return;
buffers_binded = true;
init(f);
bind(f);
geom.bind(f);
prepareDrawGeom(f);
elem.bind(f);
buffer_obj.bind(f);
prepareDrawObj(f);
buffer_sel.bind(f);
prepareDrawSel(f);
release(f);
}
void VertexObject::loadObject(QOpenGLExtraFunctions * f, const Object & object) {
loadBuffer(f, buffer_obj, &object, sizeof(Object));
}
void VertexObject::loadObjects(QOpenGLExtraFunctions * f, const QVector<Object> & objects) {
loadBuffer(f, buffer_obj, objects.constData(), objects.size() * sizeof(Object));
}
void VertexObject::loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels) {
loadBuffer(f, buffer_sel, sels.constData(), sels.size());
}
void VertexObject::draw(QOpenGLExtraFunctions * f, GLenum geom_type, int vert_cout, int obj_count) {
bind(f);
f->glDrawElementsInstanced(geom_type, vert_cout, GL_UNSIGNED_INT, 0, obj_count);
release(f);
}
void VertexObject::loadBuffer(QOpenGLExtraFunctions * f, Buffer & buf, const void * data, int size) {
buf.init(f);
if (!data) return;
buf.bind(f);
buf.resize(f, size);
buf.load(f, data, size);
}

64
core/glvertexobject.h Normal file
View File

@@ -0,0 +1,64 @@
/*
QGL VertexObject
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLVERTEXOBJECT_H
#define GLVERTEXOBJECT_H
#include "glbuffer.h"
#include "glshaders_types.h"
class VertexObject
{
friend class Mesh;
public:
VertexObject();
~VertexObject();
void init (QOpenGLExtraFunctions * f);
void destroy (QOpenGLExtraFunctions * f);
void bind (QOpenGLExtraFunctions * f);
void release (QOpenGLExtraFunctions * f);
void bindBuffers (QOpenGLExtraFunctions * f, Buffer & geom, Buffer & elem, bool force = false);
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);
void draw(QOpenGLExtraFunctions * f, GLenum geom_type, int vert_cout, int obj_count);
GLuint ID() const {return vao_;}
bool isInit() const {return vao_ != 0;}
bool isObjectsChanged() const {return objects_changed;}
bool isSelectionChanged() const {return selected_changed;}
void setObjectsChanged(bool yes = true) {objects_changed = yes;}
void setSelectionChanged(bool yes = true) {selected_changed = yes;}
private:
void loadBuffer(QOpenGLExtraFunctions * f, Buffer & buf, const void * data, int size);
GLuint vao_;
Buffer buffer_obj, buffer_sel;
bool buffers_binded, objects_changed, selected_changed;
};
#endif // GLVERTEXOBJECT_H

125
core/hdr.cpp Normal file
View File

@@ -0,0 +1,125 @@
/*
QGL HDR
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hdr_p.h"
#include "qmath.h"
#define RGBE_DATA_RED 2
#define RGBE_DATA_GREEN 1
#define RGBE_DATA_BLUE 0
/* number of floats per pixel */
#define RGBE_DATA_SIZE 3
void rgbe2float(float *red, float *green, float *blue, uchar rgbe[4]) {
float f;
if (rgbe[3]) {
f = static_cast<float>(ldexp(1.0,rgbe[3]-(int)(128+8)));
*red = rgbe[0] * f;
*green = rgbe[1] * f;
*blue = rgbe[2] * f;
}
else
*red = *green = *blue = 0.0;
}
/* simple read routine. will not correctly handle run length encoding */
bool RGBE_ReadPixels(QDataStream * fp, float * data, int numpixels) {
uchar rgbe[4];
while(numpixels-- > 0) {
if (fp->readRawData((char*)rgbe, sizeof(rgbe)) < 1)
return false;
rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE],rgbe);
data += RGBE_DATA_SIZE;
}
return true;
}
bool RGBE_ReadPixels_RLE(QDataStream * fp, float * data, int scanline_width, int num_scanlines) {
uchar rgbe[4], *ptr, *ptr_end;
int i, count;
uchar buf[2];
QByteArray scanline_buffer;
if ((scanline_width < 8)||(scanline_width > 0x7fff))
/* run length encoding is not allowed so read flat*/
return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines);
scanline_buffer.resize(4*scanline_width);
/* read in each successive scanline */
while(num_scanlines > 0) {
if (fp->readRawData((char*)rgbe,sizeof(rgbe)) < 1) {
return false;
}
if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) {
/* this file is not run length encoded */
rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],&data[RGBE_DATA_BLUE],rgbe);
data += RGBE_DATA_SIZE;
return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1);
}
if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) {
return false;
}
ptr = (uchar*)scanline_buffer.data();
/* read each of the four channels for the scanline into the buffer */
for(i=0;i<4;i++) {
ptr_end = (uchar*)scanline_buffer.data() + ((i+1)*scanline_width);
while(ptr < ptr_end) {
if (fp->readRawData((char*)buf,sizeof(buf[0])*2) < 1) {
return false;
}
if (buf[0] > 128) {
/* a run of the same value */
count = buf[0]-128;
if ((count == 0)||(count > ptr_end - ptr)) {
return false;
}
while(count-- > 0)
*ptr++ = buf[1];
}
else {
/* a non-run */
count = buf[0];
if ((count == 0)||(count > ptr_end - ptr)) {
return false;
}
*ptr++ = buf[1];
if (--count > 0) {
if (fp->readRawData((char*)ptr,sizeof(*ptr)*count) < 1) {
return false;
}
ptr += count;
}
}
}
}
/* now convert data from buffer into floats */
for(i=0;i<scanline_width;i++) {
rgbe[0] = scanline_buffer[i];
rgbe[1] = scanline_buffer[i+scanline_width];
rgbe[2] = scanline_buffer[i+2*scanline_width];
rgbe[3] = scanline_buffer[i+3*scanline_width];
rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
&data[RGBE_DATA_BLUE],rgbe);
data += RGBE_DATA_SIZE;
}
num_scanlines--;
}
return true;
}

26
core/hdr_p.h Normal file
View File

@@ -0,0 +1,26 @@
/*
QGL HDR
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HDR_P_H
#define HDR_P_H
#include <QDataStream>
bool RGBE_ReadPixels_RLE(QDataStream * fp, float * data, int scanline_width, int num_scanlines);
#endif // HDR_P_H

27
core/qglengine_version.h Normal file
View File

@@ -0,0 +1,27 @@
// This file generated by CMake set_version() version 2
#ifndef QGLENGINE_QGLENGINE_VERSION_H
#define QGLENGINE_QGLENGINE_VERSION_H
// Project
#define QGLENGINE_VERSION_MAJOR 1
#define QGLENGINE_VERSION_MINOR 0
#define QGLENGINE_VERSION_REVISION 0
#define QGLENGINE_VERSION_BUILD 9999
#define QGLENGINE_VERSION_SUFFIX "rc"
#define QGLENGINE_VERSION_NAME "1.0.0_rc"
#define QGLENGINE_MAKE_VERSION(major, minor, revision) ((major << 16) | (minor << 8) | revision)
#define QGLENGINE_VERSION QGLENGINE_MAKE_VERSION(QGLENGINE_VERSION_MAJOR, QGLENGINE_VERSION_MINOR, QGLENGINE_VERSION_REVISION)
// Tools
#define QGLENGINE_CMAKE_VERSION "3.17.1"
#define QGLENGINE_CXX_COMPILER "GNU 8.1.0"
#define QGLENGINE_BUILD_DATE "22.08.2020 00:51"
#define QGLENGINE_ARCH "i386"
#endif // QGLENGINE_QGLENGINE_VERSION_H

267
formats/loader_assimp.cpp Normal file
View File

@@ -0,0 +1,267 @@
/*
QGL Loader Assimp
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "loader_assimp.h"
#include "glscene.h"
#include "glmesh.h"
#include "glmaterial.h"
#include "globject.h"
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/material.h>
QString fromAiString (const aiString & s) {return QString::fromLocal8Bit(s.C_Str());}
QColor fromAiColor (const aiColor4D & c) {return QColor::fromRgbF(piClamp(c.r, 0.f, 1.f), piClamp(c.g, 0.f, 1.f), piClamp(c.b, 0.f, 1.f));}
QVector3D fromAiVector3D(const aiVector3D & v) {return QVector3D(v.x, v.y, v.z);}
Vector3i fromAiFace (const aiFace & v) {return Vector3i(v.mIndices[0], v.mIndices[1], v.mIndices[2]);}
QMatrix4x4 fromAiMatrix4D(const aiMatrix4x4 & v) {return QMatrix4x4(v.a1, v.a2, v.a3, v.a4,
v.b1, v.b2, v.b3, v.b4,
v.c1, v.c2, v.c3, v.c4,
v.d1, v.d2, v.d3, v.d4);}
bool isAiMeshTriangles(const aiMesh * m) {return (m->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) == aiPrimitiveType_TRIANGLE;}
Mesh * assimpMesh(const aiMesh * m) {
if (!m) return 0;
if (!isAiMeshTriangles(m)) return 0;
Mesh * ret = new Mesh();
int vcnt = m->mNumVertices, tcnt = m->mNumFaces;
QVector<QVector3D> & v(ret->vertices()); v.resize(vcnt);
QVector<QVector2D> & t(ret->texcoords()); t.resize(vcnt);
QVector< Vector3i> & ind(ret->indicesTriangles());
for (int i = 0; i < vcnt; ++i)
v[i] = fromAiVector3D(m->mVertices[i]);
if (m->HasNormals()) {
QVector<QVector3D> & n(ret->normals());
n.resize(vcnt);
for (int i = 0; i < vcnt; ++i)
n[i] = fromAiVector3D(m->mNormals[i]);
}
if (m->HasTextureCoords(0)) {
for (int i = 0; i < vcnt; ++i)
t[i] = fromAiVector3D(m->mTextureCoords[0][i]).toVector2D();
}
if (m->HasFaces()) {
ind.resize(tcnt);
for (int i = 0; i < tcnt; ++i)
ind[i] = fromAiFace(m->mFaces[i]);
} else {
tcnt = vcnt / 3;
ind.resize(tcnt);
int si = 0;
for (int i = 0; i < tcnt; ++i) {
si = i+i+i;
ind[i] = Vector3i(si, si+1, si+2);
}
}
//qDebug() << "add mesh" << v.size() << ret->normals().size() << ret->texcoords().size() << ret->indices().size();
return ret;
}
QColor aiMatColor(const aiMaterial * m, const char * key, uint s0, uint s1, const QColor & def = Qt::white) {
aiColor4D col;
aiReturn r = m->Get(key, s0, s1, col);
//qDebug() << key << r << col.r << col.g << col.b;
if (r != aiReturn_SUCCESS) return def;
return fromAiColor(col);
}
float aiMatFloat(const aiMaterial * m, const char * key, uint s0, uint s1, float def = 0.f) {
float ret;
aiReturn r = m->Get(key, s0, s1, ret);
if (r != aiReturn_SUCCESS) return def;
return ret;
}
QString aiMatString(const aiMaterial * m, const char * key, uint s0, uint s1) {
aiString p;
aiReturn r = const_cast<aiMaterial*>(m)->Get(key, s0, s1, p);
//qDebug() << "GetTexture" << key << s0 << r << fromAiString(p);
if (r != aiReturn_SUCCESS) return QString();
return fromAiString(p);
}
Material * assimpMaterial(const aiMaterial * m) {
if (!m) return 0;
Material * ret = new Material();
///WARNING: no function GetName() in aiMaterial in stable release
//ret->name = fromAiString(const_cast<aiMaterial*>(m)->GetName());
aiString name;
const_cast<aiMaterial*>(m)->Get(AI_MATKEY_NAME,name);
ret->name = fromAiString(name);
//qDebug() << "mat" << ret->name;
//for (int i = 0; i < m->mNumProperties; ++i) {
// qDebug()<< fromAiString(m->mProperties[i]->mKey);// << "=" << aiMatFloat(m, m->mProperties[i]->mKey.C_Str(), 0, 0);
//}
ret->color_diffuse = aiMatColor(m, AI_MATKEY_COLOR_DIFFUSE);
ret->color_emission = aiMatColor(m, AI_MATKEY_COLOR_EMISSIVE);
float shine = aiMatFloat(m, AI_MATKEY_SHININESS, -1.f);
if (shine >= 0) {
ret->map_roughness.color_amount = 0.8f - (shine / 100.f * 0.6f);
//qDebug() << "shine" << shine;
}
ret->map_diffuse .bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_DIFFUSE(0));
ret->map_normal .bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_NORMALS(0));
//ret->map_metalness.bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_SPECULAR(0));
ret->map_roughness.bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_SHININESS(0));
ret->map_emission .bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_EMISSIVE(0));
ret->transparency = 1.f - aiMatFloat(m, AI_MATKEY_OPACITY, 1.f);
ret->detectMaps();
ret->setTypes();
return ret;
}
Light * assimpLight(const aiLight * l) {
if (!l) return 0;
if (l->mType != aiLightSource_POINT && l->mType != aiLightSource_SPOT)
return 0;
Light * ret = new Light();
ret->setName(fromAiString(l->mName));
ret->setPos(fromAiVector3D(l->mPosition));
ret->setDirection(fromAiVector3D(l->mDirection));
ret->decay_const = l->mAttenuationConstant ;
ret->decay_linear = l->mAttenuationLinear ;
ret->decay_quadratic = l->mAttenuationQuadratic;
ret->angle_start = l->mAngleInnerCone * rad2deg;
ret->angle_end = l->mAngleOuterCone * rad2deg;
if (l->mType == aiLightSource_SPOT)
ret->light_type = Light::Cone;
QVector3D col(l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b);
ret->intensity = col.length();
col /= ret->intensity;
ret->setColor(QColor::fromRgbF(col[0], col[1], col[2]));
return ret;
}
ObjectBaseList assimpObject(const aiNode * n, const QVector<Mesh * > & meshes, aiMesh ** ai_meshes,
const QVector<Material*> & materials,
const QMap<QString, Light * > & light_by_name, QVector<Light*> & out_lights) {
if (!n) return ObjectBaseList();
ObjectBaseList ret;
ObjectBase * obj = 0;
QString name = fromAiString(n->mName);
Light * light = light_by_name.value(name, 0);
if (light) {
obj = light->clone();
out_lights << (Light*)obj;
} else
obj = new ObjectBase();
obj->setName(name);
obj->setMatrix(fromAiMatrix4D(n->mTransformation));
ret << obj;
//qDebug() << "add object" << ret << ret->name();
if (!light) {
//qDebug() << name << "has" << n->mNumMeshes << "meshes";
for (uint i = 0; i < n->mNumMeshes; ++i) {
int mi = n->mMeshes[i];
if (meshes[mi]) {
if (obj->mesh()) {
obj = obj->clone(false);
ret << obj;
}
obj->setMesh(meshes[mi]);
int mati = ai_meshes[mi]->mMaterialIndex;
if (mati >= 0 || mati < materials.size())
obj->setMaterial(materials[mati]);
//ret->setMesh(meshes[mi]);
//qDebug() << "set mesh" << mi << ret->mesh();
//break;
}
}
}
for (uint i = 0; i < n->mNumChildren; ++i) {
ObjectBaseList cl = assimpObject(n->mChildren[i], meshes, ai_meshes, materials, light_by_name, out_lights);
foreach (ObjectBase * c, cl)
obj->addChild(c);
}
return ret;
}
Scene * loadScene(const QString & filepath) {
if (filepath.isEmpty()) return 0;
qDebug() << "[Loader Assimp] Import" << filepath << "...";
Assimp::Importer importer;
const aiScene * ais = importer.ReadFile(filepath.toUtf8(), aiProcess_Triangulate |
aiProcess_SortByPType |
aiProcess_GenUVCoords |
aiProcess_TransformUVCoords);
if (!ais) {
qDebug() << "[Loader Assimp] Error: \"" + QString(importer.GetErrorString()) + "\"";
return 0;
}
qDebug() << "[Loader Assimp] Imported" << ais->mNumMeshes << "meshes";
QVector<Mesh * > meshes;
for (uint i = 0; i < ais->mNumMeshes; ++i)
meshes << assimpMesh(ais->mMeshes[i]);
QVector<Material * > materials;
for (uint i = 0; i < ais->mNumMaterials; ++i)
materials << assimpMaterial(ais->mMaterials[i]);
QVector<Light * > lights;
for (uint i = 0; i < ais->mNumLights; ++i)
lights << assimpLight(ais->mLights[i]);
QMap<QString, Light * > light_by_name;
foreach (Light * l, lights)
if (l)
light_by_name[l->name()] = l;
QVector<Light*> out_lights;
ObjectBaseList rootl = assimpObject(ais->mRootNode, meshes, ais->mMeshes, materials, light_by_name, out_lights);
if (rootl.isEmpty()) return 0;
ObjectBase * root = rootl[0];
root->transferTransformToChildren(true);
ObjectBaseList rcl = root->children(true);
foreach (ObjectBase * c, rcl) {
foreach (Light * l, out_lights) {
if (c->name() == (l->name() + ".Target")) {
l->setDistance((l->worldPos() - c->worldPos()).length());
delete c;
break;
}
}
}
root->cleanTree();
Scene * scene = new Scene();
scene->setName(root->name());
foreach (ObjectBase * o, root->children())
scene->addObject(o);
lights.removeAll(0);
qDeleteAll(lights);
return scene;
}
QStringList supportedFormats() {
Assimp::Importer importer;
aiString ret;
importer.GetExtensionList(ret);
return fromAiString(ret).toLower().split(";");
}

27
formats/loader_assimp.h Normal file
View File

@@ -0,0 +1,27 @@
/*
QGL Loader Assimp
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOADER_ASSIMP_H
#define LOADER_ASSIMP_H
#include "gltypes.h"
Scene * loadScene(const QString & filepath);
QStringList supportedFormats();
#endif // LOADER_ASSIMP_H

67
formats/loader_qgl.cpp Normal file
View File

@@ -0,0 +1,67 @@
/*
QGL Loader QGL
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "loader_qgl.h"
#include "glscene.h"
#include <chunkstream.h>
Scene * loadFromQGLFile(const QString & filepath) {
QFile f(filepath);
if (!f.exists()) {
qDebug() << "[Loader QGL] Error: can`t open \"" + filepath + "\"";
return 0;
}
f.open(QIODevice::ReadOnly);
QDataStream s(&f);
s.setVersion(QDataStream::Qt_5_0);
char sign[4];
s.readRawData(sign, 4);
if ((sign[0] != 'Q') || (sign[1] != 'G') || (sign[2] != 'L') || (sign[3] != 'E')) {
qDebug() << "[Loader QGL] Error: \"" + filepath + "\" is not valid QGLEngine file!";
return 0;
}
ushort version = 0x1;
f.peek((char*)&version, 2);
if (version != 1) {
qDebug() << "[Loader QGL] Error: \"" + filepath + "\" unsupported version!";
return 0;
}
Scene * ret = 0;
s.skipRawData(2);
s >> ret;
//root->buildTransform();
qDebug() << "[Loader QGL] Loaded" << ret->objectsCount(true) << "objects from" << filepath;
return ret;
}
bool saveToQGLFile(const QString & filepath, const Scene * scene) {
QFile f(filepath);
if (!f.open(QIODevice::ReadWrite))
return false;
f.resize(0);
QDataStream s(&f);
s.setVersion(QDataStream::Qt_5_0);
char sign[4] = {'Q', 'G', 'L', 'E'};
ushort version = 0x1;
s.writeRawData(sign, 4);
s.writeRawData((char*)&version, 2);
s << scene;
return true;
}

28
formats/loader_qgl.h Normal file
View File

@@ -0,0 +1,28 @@
/*
QGL Loader QGL
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LOADER_QGL_H
#define LOADER_QGL_H
#include "gltypes.h"
#include <QFileInfo>
Scene * loadFromQGLFile(const QString & filepath);
bool saveToQGLFile(const QString & filepath, const Scene * scene);
#endif // LOADER_QGL_H

110
glcamera.cpp Normal file
View File

@@ -0,0 +1,110 @@
/*
QGL Camera
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gltypes.h"
#include "qglview.h"
Camera::Camera() {
type_ = glCamera;
fov_ = 60.;
roll_ = 0.;
//setRotationX(90.f);
depth_start = 0.1f;
mirror_x = mirror_y = false;
}
QMatrix4x4 Camera::offsetMatrix() const {
QMatrix4x4 ret;
ret.translate(parent_ ? -offset_ : -aim());
return ret;
}
/*
void Camera::localTransform(QMatrix4x4 & m) {
return;
if (parent_)
m *= parent_->worldTransform();
QMatrix4x4 ret;
//qDebug() << "local camera";
ret.translate(0., 0., -distance());
ret.rotate(angles_.y(), 1., 0., 0.);
ret.rotate(angles_.x(), 0., 1., 0.);
ret.rotate(angles_.z(), 0., 0., 1.);
//m *= ret.inverted();
}
*/
void Camera::assign(const Camera & c) {
trans = c.trans;
aim_dist = c.aim_dist;
fov_ = c.fov_;
mirror_x = c.mirror_x;
mirror_y = c.mirror_y;
depth_start = c.depth_start;
buildTransform();
}
ObjectBase * Camera::clone(bool withChildren) {
Camera * o = new Camera(*this);
//GLObjectBase::clone(withChildren);
o->is_init = false;
o->name_ = name_;// + "_copy";
o->scene_ = nullptr;
o->children_.clear();
if (withChildren) {
for (int i = 0; i < children_.size(); ++i)
o->addChild(children_[i]->clone(withChildren));
}
o->trans = trans;
o->aim_dist = aim_dist;
o->fov_ = fov_;
o->roll_ = roll_;
o->mirror_x = mirror_x;
o->mirror_y = mirror_y;
o->depth_start = depth_start;
o->meta = meta;
return o;
}
QMatrix4x4 Camera::viewMatrix() const {
QMatrix4x4 ret;
//qDebug() << pos() << aim();
ret.translate(0., 0., -distance());
ret.rotate(-roll_, 0., 0., 1.);
ret *= trans.matrixRotateScale().inverted();
//ret.rotate(angles_.y(), 1., 0., 0.);
//ret.rotate(angles_.x(), 0., 1., 0.);
//pm.translate(-aim_);
if (parent_) {
QMatrix4x4 pmat = parent_->worldTransform();
offset_ = pmat.column(3).toVector3D();
pmat(0, 3) = pmat(1, 3) = pmat(2, 3) = 0.;
pmat.translate(aim());
ret *= pmat.inverted();
}
return ret;
}
QMatrix4x4 Camera::projectionMatrix(double aspect) const {
return glMatrixPerspective(fov_, aspect, depth_start);
}

70
glcamera.h Normal file
View File

@@ -0,0 +1,70 @@
/*
QGL Camera
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLCAMERA_H
#define GLCAMERA_H
#include "globject.h"
//extern QMatrix4x4 globCameraMatrix;
//extern Camera * currentCamera;
class Camera: public AimedObject
{
friend class QGLView;
friend class GLParticlesSystem;
friend QDataStream & operator <<(QDataStream & s, const ObjectBase * p);
friend QDataStream & operator >>(QDataStream & s, ObjectBase *& p);
public:
Camera();
void setFOV(const float & f) {fov_ = f;}
void setAngles(const QVector3D & a) {setRotation(a);}
void setAngleZ(const float & a) {setRotationZ(a);}
void setAngleXY(const float & a) {setRotationX(a);}
void setAngleRoll(const float & a) {roll_ = a;}
void setDepthStart(const float & d) {depth_start = d;}
void setMirrorX(bool yes) {mirror_x = yes;}
void setMirrorY(bool yes) {mirror_y = yes;}
float FOV() const {return fov_;}
float angleZ() const {return rotationZ();}
float angleXY() const {return rotationX();}
float angleRoll() const {return roll_;}
float depthStart() const {return depth_start;}
bool isMirrorX() const {return mirror_x;}
bool isMirrorY() const {return mirror_y;}
void assign(const Camera & c);
virtual ObjectBase * clone(bool withChildren = true);
QMatrix4x4 viewMatrix() const;
QMatrix4x4 projectionMatrix(double aspect) const;
QMatrix4x4 offsetMatrix() const;
QMatrix4x4 fullViewMatrix() const {return viewMatrix() * offsetMatrix();}
private:
mutable QVector3D offset_;
GLfloat fov_, roll_;
GLfloat depth_start;
bool mirror_x;
bool mirror_y;
};
#endif // GLCAMERA_H

717
globject.cpp Normal file
View File

@@ -0,0 +1,717 @@
/*
QGL ObjectBase & Light
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "globject.h"
#include "glcamera.h"
#include "glscene.h"
#include "glmesh.h"
#include <chunkstream.h>
//static int _count = 0;
ObjectBase::ObjectBase(Mesh * geom, Material * mat) {
type_ = glMesh;
render_mode = View;
prev_pass = rpSolid;
parent_ = nullptr;
color_ = Qt::white;
is_root = is_init = selected_ = false;
visible_ = accept_fog = accept_light = cast_shadow = rec_shadow = select_ = true;
line_width = -1.;
id_ = 0;
blend_src = GL_SRC_ALPHA;
blend_dest = GL_ONE_MINUS_SRC_ALPHA;
type_ = glMesh;
raw_matrix = selected_aim = false;
mat_.setToIdentity();
scene_ = nullptr;
mesh_ = geom;
material_ = mat;
//qDebug() << "ObjectBase, now" << ++_count;
}
ObjectBase::~ObjectBase() {
//qDebug() << "~ObjectBase, now" << --_count;
if (parent_) parent_->children_.removeAll(this);
if (scene_) {
scene_->__objectDeleted(this);
scene_->setTreeChanged();
scene_->setTreeStructChanged();
}
foreach (ObjectBase * c, children_) {
c->parent_ = nullptr;
delete c;
}
}
ObjectBase * ObjectBase::clone(bool withChildren) {
ObjectBase * o = new ObjectBase();
o->prev_pass = prev_pass;
o->is_init = false;
o->accept_light = accept_light;
o->accept_fog = accept_fog;
o->visible_ = visible_;
o->color_ = color_;
o->type_ = type_;
o->raw_matrix = raw_matrix;
o->mat_ = mat_;
o->trans = trans;
o->itransform_ = itransform_;
o->bound = bound;
o->name_ = name_;// + "_copy";
o->blend_src = blend_src;
o->blend_dest = blend_dest;
o->pos_h = pos_h;
o->material_ = material_;
o->mesh_ = mesh_;
o->meta = meta;
o->scene_ = nullptr;
if (withChildren) {
for (int i = 0; i < children_.size(); ++i)
o->addChild(children_[i]->clone(withChildren));
}
return o;
}
void ObjectBase::destroy() {
if (mesh_) delete mesh_;
}
void ObjectBase::init() {
calculateBoundingBox();
//material_.reflection.create();
//qDebug() << "init" << vbo.buffer_;
is_init = true;
}
RenderPass ObjectBase::pass() const {
RenderPass ret = rpSolid;
if (material_)
if (material_->hasTransparency())
ret = rpTransparent;
return ret;
}
void ObjectBase::setScene(Scene * v) {
scene_ = v;
foreach (ObjectBase * c, children_)
c->setScene(v);
}
void ObjectBase::addChild(ObjectBase * o) {
if (o == this) return;
if (o->parent_)
o->parent_->children_.removeAll(o);
children_ << o;
o->parent_ = this;
o->setScene(scene_);
o->buildTransform();
/*if (scene_) {
ObjectBaseList cl = o->children(true);
cl << o;
//foreach (ObjectBase * i, cl) {
// emit view_->objectAdded(i);
//}
}*/
setSceneTreeChanged();
}
void ObjectBase::removeChild(ObjectBase * o) {
if (o == this) return;
children_.removeAll(o);
o->parent_ = nullptr;
o->buildTransform();
setSceneTreeChanged();
}
void ObjectBase::removeChild(int index) {
children_[index]->parent_ = nullptr;
children_[index]->buildTransform();
children_.removeAt(index);
setSceneTreeChanged();
}
void ObjectBase::clearChildren(bool deleteAll) {
foreach (ObjectBase * i, children_) {
i->scene_ = nullptr;
i->parent_ = nullptr;
i->clearChildren(deleteAll);
if (deleteAll) {
delete i;
} else {
i->buildTransform();
}
}
children_.clear();
setSceneTreeChanged();
}
ObjectBase * ObjectBase::child(int index) {
if (index < 0 || index >= children_.size()) return nullptr;
return children_[index];
}
ObjectBase * ObjectBase::child(const QString & name) {
foreach (ObjectBase * i, children_)
if (i->name_ == name) return i;
return nullptr;
}
const ObjectBase * ObjectBase::child(int index) const {
if (index < 0 || index >= children_.size()) return nullptr;
return children_[index];
}
const ObjectBase * ObjectBase::child(const QString & name) const {
foreach (ObjectBase * i, children_)
if (i->name_ == name) return i;
return nullptr;
}
ObjectBaseList ObjectBase::children(bool all_) {
if (!all_) return children_;
ObjectBaseList cl;
addChildren(cl, this);
return cl;
}
bool ObjectBase::isVisible(bool check_parents) const {
if (!check_parents) return visible_;
if (!visible_) return false;
ObjectBase * p = parent_;
while (p) {
if (!p->visible_) return false;
p = p->parent_;
}
return true;
}
void ObjectBase::setVisible(bool v) {
visible_ = v;
setSceneTreeChanged();
}
void ObjectBase::rotateZ(GLfloat a) {
raw_matrix = false;
trans.setRotationZ(trans.rotationZ() + a);
//angles_.setZ(angles_.z() + a);
//while (angles_.z() < -360.f) angles_.setZ(angles_.z() + 360.f);
//while (angles_.z() > 360.f) angles_.setZ(angles_.z() - 360.f);
buildTransform();
}
void ObjectBase::setRotationZ(GLfloat a) {
raw_matrix = false;
trans.setRotationZ(a);
//angles_.setZ(a);
//while (angles_.z() < -360.f) angles_.setZ(angles_.z() + 360.f);
//while (angles_.z() > 360.f) angles_.setZ(angles_.z() - 360.f);
buildTransform();
}
void ObjectBase::setTransform(const Transform & t) {
trans = t;
buildTransform();
}
void ObjectBase::addChildren(ObjectBaseList & list, ObjectBase * where) {
foreach (ObjectBase * i, where->children_) {
list << i;
addChildren(list, i);
}
}
void ObjectBase::calculateBoundingBox() {
bound = Box3D();
if (mesh_) {
bound = mesh_->boundingBox();
QVector<QVector3D> c = bound.corners(), tc;
foreach (QVector3D p, c)
tc << (itransform_ * QVector4D(p, 1)).toVector3D();
bound = Box3D(tc);
}
foreach (ObjectBase * i, children_) {
i->calculateBoundingBox();
bound |= i->boundingBox();
}
}
void ObjectBase::updateTransform() {
buildTransform(true);
}
void ObjectBase::setProperty(const QString & pn, const QVariant & v) {
meta[pn] = v;
}
QVariant ObjectBase::property(const QString & pn, bool * exists) const {
if (exists) *exists = meta.contains(pn);
return meta.value(pn);
}
bool ObjectBase::hasProperty(const QString & pn) const {
return meta.contains(pn);
}
void ObjectBase::removeProperty(const QString & pn) {
meta.remove(pn);
}
void ObjectBase::setMatrix(const QMatrix4x4 & t) {
//raw_matrix = true;
//mat_ = t;
//pos_ = mat_.column(3).toVector3D();
//mat_.setColumn(3, QVector4D(0., 0., 0., 1.));
raw_matrix = false;
trans.setMatrix(t);
buildTransform();
}
QMatrix4x4 ObjectBase::matrix() const {
return trans.matrix();
}
QVector3D ObjectBase::inParentSpace(const QVector3D & v) const {
if (!parent_) return v;
return (parent_->matrix() * QVector4D(v, 1)).toVector3D();
}
void ObjectBase::transferTransformToChildren(bool only_scale) {
QMatrix4x4 m = trans.matrix();
if (only_scale) m = trans.matrixScale();
foreach (ObjectBase * i, children_)
i->trans.setMatrix(m * i->trans.matrix());
if (only_scale) resetScale();
else setMatrix(QMatrix4x4());
}
void ObjectBase::cleanTree() {
for (int i = 0; i < children_.size(); ++i) {
ObjectBase * o = children_[i];
if (!o->hasChildren() && !o->mesh() && (o->type() == glMesh)) {
delete o;
--i;
}
o->cleanTree();
}
}
bool ObjectBase::isSelected(bool check_parents) const {
if (!check_parents) return selected_;
if (selected_) return true;
ObjectBase * p = parent_;
while (p) {
if (p->selected_) return true;
p = p->parent_;
}
return false;
}
void ObjectBase::setSelected(bool yes) {
//qDebug() << "select" << name() << view_;
if (select_)
selected_ = yes;
if (!selected_)
selected_aim = false;
}
ObjectBase * ObjectBase::selectedParent() const {
ObjectBase * p = parent_;
while (p) {
if (p->selected_) return p;
p = p->parent_;
}
return 0;
}
void ObjectBase::setMaterial(Material * m, bool with_children) {
material_ = m;
if (with_children)
foreach (ObjectBase * i, children_) i->setMaterial(m, true);
setObjectsChanged();
if (scene_) scene_->mat_changed = scene_->tree_changed = true;
}
void ObjectBase::setColor(QColor c, bool with_children) {
color_ = c;
if (with_children)
foreach (ObjectBase * i, children_) i->setColor(c, true);
setObjectsChanged();
}
void ObjectBase::setMesh(Mesh * v) {
if (scene_)
v = scene_->attachMesh(v);
mesh_ = v;
setSceneTreeChanged();
setObjectsChanged();
}
void ObjectBase::buildTransform(bool force) {
if (force) trans.setDirty();
itransform_.setToIdentity();
ObjectBase * p = parent_;
if (p)
itransform_ = p->itransform_;
//if (raw_matrix) {
// itransform_.translate(pos_);
// itransform_ *= mat_;
// //qDebug() << "raw_matrix" << itransform_;
//} else
localTransform(itransform_);
//qDebug() << name_ << itransform_;
foreach (ObjectBase * i, children_)
i->buildTransform(force);
setObjectsChanged();
}
void ObjectBase::initInternal() {
init();
foreach (ObjectBase * i, children_) i->initInternal();
}
void ObjectBase::localTransform(QMatrix4x4 & m) {
m *= trans.matrix();
}
void ObjectBase::setSceneTreeChanged() {
if (scene_) {
scene_->setTreeChanged();
scene_->setTreeStructChanged();
}
setObjectsChanged();
}
void ObjectBase::setObjectsChanged() {
int p = pass();
if (mesh_) {
mesh_->setObjectsChanged (p, true);
mesh_->setSelectionChanged(p, true);
if (prev_pass != p) {
mesh_->setObjectsChanged (prev_pass, true);
mesh_->setSelectionChanged(prev_pass, true);
}
}
prev_pass = p;
}
QMatrix4x4 ObjectBase::worldMatrix(QMatrix4x4 parent) const {
QMatrix4x4 mat;
//mat.translate(pos_);
//if (raw_matrix) {
// mat *= mat_;
//} else {
// if (angles_.z() != 0.f) mat.rotate(angles_.z(), 0., 0., 1.);
// if (angles_.y() != 0.f) mat.rotate(angles_.y(), 0., 1., 0.);
// if (angles_.x() != 0.f) mat.rotate(angles_.x(), 1., 0., 0.);
// mat.scale(scale_);
//}
mat = trans.matrix();
return parent * mat;
}
AimedObject::AimedObject() {
aim_dist = 1.;
}
QVector3D AimedObject::worldAim() const {
QVector3D ret = worldPos() + worldDirection() * aim_dist;
return ret;
}
void AimedObject::setAim(const QVector3D & p) {
QVector3D dir = p - pos();
trans.setRotation(Transform::fromDirection(dir, trans.rotationY()));
aim_dist = dir.length();
buildTransform();
//if (!p.isNull())
//qDebug() << "setAim" << p << aim() << worldAim();
}
QVector3D AimedObject::direction() const {
return trans.direction();
}
void AimedObject::setDirection(const QVector3D & d) {
//double len = qMax(aim_.length(), 0.001f);
//aim_ = d.normalized() * len;
buildTransform();
}
void AimedObject::flyCloser(double s) {
double tl = 1. / (1. + s);
move(direction() * aim_dist * (1. - tl));
aim_dist *= tl;
}
void AimedObject::flyFarer(double s) {
double tl = 1. * (1. + s);
move(direction() * aim_dist * (1. - tl));
aim_dist *= tl;
}
void AimedObject::flyToDistance(double d) {
move(direction() * (aim_dist - d));
aim_dist = d;
//qDebug() << d << (aim() - pos()).length() << aim();
}
void AimedObject::moveForward(const float & x, bool withZ) {
QVector3D dv = itransform_.mapVector(QVector3D(0, 0, -x));
if (!withZ) dv[2] = 0.;
move(dv);
}
void AimedObject::moveLeft(const float & x, bool withZ) {
QVector3D dv = itransform_.mapVector(QVector3D(-x, 0, 0));
if (!withZ) dv[2] = 0.;
move(dv);
}
void AimedObject::moveUp(const float & x, bool onlyZ) {
QVector3D dv = itransform_.mapVector(QVector3D(0, x, 0));
if (onlyZ) dv[0] = dv[1] = 0.;
move(dv);
}
void AimedObject::orbitZ(const float & a) {
QVector3D pa = aim();
rotateZ(-a);
move(pa - aim());
}
void AimedObject::orbitXY(const float & a) {
QVector3D pa = aim();
rotateX(-a);
move(pa - aim());
}
void AimedObject::transformChanged() {
}
Light::Light(): AimedObject(), shadow_map(0, true, GL_R16F) {
type_ = glLight;
light_type = Omni;
intensity = 1.;
angle_start = angle_end = 180.;
decay_linear = decay_quadratic = decay_start = 0.;
decay_const = decay_end = 1.;
setDirection(0, 0, -1.);
}
Light::Light(const QVector3D & p, const QColor & c, float i): AimedObject(), shadow_map(0, true, GL_R16F) {
type_ = glLight;
light_type = Omni;
intensity = i;
color_ = c;
angle_start = angle_end = 180.;
decay_linear = decay_quadratic = decay_start = 0.;
decay_const = decay_end = 1.;
setPos(p);
setDirection(0, 0, -1.);
}
ObjectBase * Light::clone(bool withChildren) {
Light * o = new Light(*this);
//GLObjectBase::clone(withChildren);
o->is_init = false;
o->name_ = name_;// + "_copy";
o->scene_ = nullptr;
o->children_.clear();
if (withChildren) {
for (int i = 0; i < children_.size(); ++i)
o->addChild(children_[i]->clone(withChildren));
}
o->color_ = color_;
o->light_type = light_type;
o->trans = trans;
o->aim_dist = aim_dist;
o->angle_start = angle_start;
o->angle_end = angle_end;
o->intensity = intensity;
o->decay_const = decay_const;
o->decay_linear = decay_linear;
o->decay_quadratic = decay_quadratic;
o->meta = meta;
return o;
}
void Light::apply() {
if (scene_) scene_->setLightsChanged();
}
QDataStream & operator <<(QDataStream & s, const ObjectBase * p) {
ChunkStream cs;
//qDebug() << "place" << p->name() << "...";
cs.add(1, int(p->type_)).add(2, p->accept_light).add(3, p->accept_fog).add(4, p->visible_)
.add(5, p->cast_shadow).add(6, p->rec_shadow).add(7, p->raw_matrix).add(8, p->line_width)
.add(9, int(p->render_mode)).add(14, p->mat_).add(16, p->children_.size())
.add(17, p->name_).add(18, p->meta).add(19, p->color_).add(20, p->trans);
//qDebug() << "place self done";
if (p->type_ == ObjectBase::glLight) {
//qDebug() << "place light ...";
const Light * l = (const Light*)p;
cs.add(101, l->angle_start).add(102, l->angle_end).add(103, l->intensity)
.add(104, l->decay_const).add(105, l->decay_linear).add(106, l->decay_quadratic)
.add(107, l->decay_start).add(108, l->decay_end).add(109, int(l->light_type))
.add(111, l->distance());
}
if (p->type_ == ObjectBase::glCamera) {
//qDebug() << "place camera ...";
const Camera * c = (const Camera*)p;
cs.add(200, c->aim()).add(201, c->fov_).add(202, c->depth_start)
.add(206, c->mirror_x).add(207, c->mirror_y).add(208, c->distance())
.add(209, c->roll_);
}
//qDebug() << "place" << p->name() << cs.data().size() << s.device()->size();
s << cs.data();
foreach (const ObjectBase * c, p->children_)
s << c;
return s;
}
QDataStream & operator >>(QDataStream & s, ObjectBase *& p) {
ChunkStream cs(s);
p = nullptr;
int ccnt = 0;
Light * l = nullptr;
Camera * c = nullptr;
//qDebug() << "read obj ...";
while (!cs.atEnd()) {
switch (cs.read()) {
case 1: {
ObjectBase::Type type = (ObjectBase::Type)cs.getData<int>();
switch (type) {
case ObjectBase::glMesh: p = new ObjectBase(); break;
case ObjectBase::glLight: p = new Light(); l = (Light*)p; break;
case ObjectBase::glCamera: p = new Camera(); c = (Camera*)p; break;
default : break;
}
if (p) p->type_ = type;
} break;
case 2: if (p) p->accept_light = cs.getData<bool>(); break;
case 3: if (p) p->accept_fog = cs.getData<bool>(); break;
case 4: if (p) p->visible_ = cs.getData<bool>(); break;
case 5: if (p) p->cast_shadow = cs.getData<bool>(); break;
case 6: if (p) p->rec_shadow = cs.getData<bool>(); break;
case 7: if (p) p->raw_matrix = cs.getData<bool>(); break;
case 8: if (p) p->line_width = cs.getData<float>(); break;
case 9: if (p) p->render_mode = (ObjectBase::RenderMode)cs.getData<int>(); break;
case 14: if (p) p->mat_ = cs.getData<QMatrix4x4>(); break;
case 16: if (p) ccnt = cs.getData<int>(); break;
case 17: if (p) p->name_ = cs.getData<QString>(); break;
case 18: if (p) p->meta = cs.getData<QVariantMap>(); break;
case 19: if (p) p->color_ = cs.getData<QColor>(); break;
case 20: if (p) p->trans = cs.getData<Transform>(); break;
case 100: if (l) l->setAim(cs.getData<QVector3D>()); break;
case 101: if (l) l->angle_start = cs.getData<GLfloat>(); break;
case 102: if (l) l->angle_end = cs.getData<GLfloat>(); break;
case 103: if (l) l->intensity = cs.getData<GLfloat>(); break;
case 104: if (l) l->decay_const = cs.getData<GLfloat>(); break;
case 105: if (l) l->decay_linear = cs.getData<GLfloat>(); break;
case 106: if (l) l->decay_quadratic = cs.getData<GLfloat>(); break;
case 107: if (l) l->decay_start = cs.getData<GLfloat>(); break;
case 108: if (l) l->decay_end = cs.getData<GLfloat>(); break;
case 109: if (l) l->light_type = (Light::Type)cs.getData<int>(); break;
case 111: if (l) l->setDistance(cs.getData<double>()); break;
case 200: if (c) c->setAim(cs.getData<QVector3D>()); break;
case 201: if (c) c->setFOV(cs.getData<GLfloat>()); break;
case 202: if (c) c->setDepthStart(cs.getData<GLfloat>()); break;
case 206: if (c) c->mirror_x = cs.getData<bool>(); break;
case 207: if (c) c->mirror_y = cs.getData<bool>(); break;
case 208: if (c) c->setDistance(cs.getData<double>()); break;
case 209: if (c) c->roll_ = cs.getData<GLfloat>(); break;
}
}
//qDebug() << p->name() << ccnt;
for (int i = 0; i < ccnt; ++i) {
ObjectBase * c = nullptr;
s >> c;
if (!c) continue;
c->parent_ = p;
p->children_ << c;
}
p->buildTransform();
return s;
}

313
globject.h Normal file
View File

@@ -0,0 +1,313 @@
/*
QGL ObjectBase & Light
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLOBJECT_H
#define GLOBJECT_H
#include "glframebuffer.h"
#include "glmaterial.h"
#include "gltypes.h"
#include "gltransform.h"
class ObjectBase
{
friend class QGLView;
friend class Scene;
friend class Renderer;
friend class RendererService;
friend class RendererSelection;
friend class MouseController;
friend QDataStream & operator <<(QDataStream & s, const ObjectBase * p);
friend QDataStream & operator >>(QDataStream & s, ObjectBase *& p);
friend QDataStream & operator >>(QDataStream & s, Scene *& p);
public:
enum Type {glMesh, glLight, glCamera, glParticlesSystem};
enum RenderMode {View = 0, Point = GL_POINT, Line = GL_LINE, Fill = GL_FILL};
explicit ObjectBase(Mesh * geom = 0, Material * mat = 0);
virtual ~ObjectBase();
virtual ObjectBase * clone(bool withChildren = true);
void destroy();
QString name() const {return name_;}
void setName(const QString & name) {name_ = name;}
virtual void init();
virtual void update() {}
bool isInit() const {return is_init;}
Type type() const {return type_;}
RenderPass pass() const;
RenderMode renderMode() const {return render_mode;}
void setRenderMode(RenderMode mode) {render_mode = mode;}
float lineWidth() const {return line_width;}
void setLineWidth(const float & width) {line_width = width;}
ObjectBase * parent() {return parent_;}
void setParent(ObjectBase * o) {parent_ = o;}
bool hasParent() const {return parent_ != nullptr;}
bool hasChildren() const {return !children_.isEmpty();}
void setScene(Scene * v);
void addChild(ObjectBase * o);
void removeChild(ObjectBase * o);
void removeChild(int index);
void clearChildren(bool deleteAll = false);
int childCount() const {return children_.size();}
ObjectBase * child(int index);
ObjectBase * child(const QString & name);
const ObjectBase * child(int index) const;
const ObjectBase * child(const QString & name) const;
ObjectBaseList children(bool all_ = false);
bool isVisible(bool check_parents = false) const;
bool isHidden(bool check_parents = false) const {return !isVisible(check_parents);}
void setVisible(bool v);
void setHidden(bool v) {setVisible(!v);}
void show() {setVisible(true);}
void hide() {setVisible(false);}
bool isReceiveShadows() const {return rec_shadow;}
bool isCastShadows() const {return cast_shadow;}
void setReceiveShadows(bool on) {rec_shadow = on;}
void setCastShadows(bool on) {cast_shadow = on;}
void move(const QVector3D & dv) {trans.setTranslation(pos() + dv); buildTransform();}
void moveTo(const QVector3D & dv) {trans.setTranslation(dv); buildTransform();}
void move(GLfloat dx, GLfloat dy, GLfloat dz = 0.) {move(QVector3D(dx, dy, dz)); buildTransform();}
void moveTo(GLfloat dx, GLfloat dy, GLfloat dz = 0.) {moveTo(QVector3D(dx, dy, dz)); buildTransform();}
void moveX(GLfloat o) {trans.setTranslationX(posX() + o); buildTransform();}
void moveY(GLfloat o) {trans.setTranslationY(posY() + o); buildTransform();}
void moveZ(GLfloat o) {trans.setTranslationZ(posZ() + o); buildTransform();}
void setPosX(GLfloat o) {trans.setTranslationX(o); buildTransform();}
void setPosY(GLfloat o) {trans.setTranslationY(o); buildTransform();}
void setPosZ(GLfloat o) {trans.setTranslationZ(o); buildTransform();}
void setPos(GLfloat x, GLfloat y, GLfloat z) {trans.setTranslation(QVector3D(x, y, z)); buildTransform();}
void setPos(const QVector3D & p) {trans.setTranslation(p); buildTransform();}
void resetPos() {trans.setTranslation(QVector3D()); buildTransform();}
QVector3D pos() const {return trans.translation();}
float posX() const {return trans.translation().x();}
float posY() const {return trans.translation().y();}
float posZ() const {return trans.translation().z();}
QVector3D worldPos() const {return (itransform_ * QVector4D(0, 0, 0, 1.)).toVector3D();}
QMatrix4x4 worldTransform() const {return itransform_;}
QVector3D rotation() const {return trans.rotation();}
float rotationX() const {return rotation().x();}
float rotationY() const {return rotation().y();}
float rotationZ() const {return rotation().z();}
void rotateX(GLfloat a) {raw_matrix = false; trans.setRotationX(trans.rotationX() + a); buildTransform();}
void rotateY(GLfloat a) {raw_matrix = false; trans.setRotationY(trans.rotationY() + a); buildTransform();}
void rotateZ(GLfloat a);
void setRotationX(GLfloat a) {raw_matrix = false; trans.setRotationX(a); buildTransform();}
void setRotationY(GLfloat a) {raw_matrix = false; trans.setRotationY(a); buildTransform();}
void setRotationZ(GLfloat a);
void setRotation(const QVector3D & a) {raw_matrix = false; trans.setRotation(a); buildTransform();}
void resetRotation() {raw_matrix = false; trans.setRotation(QVector3D()); buildTransform();}
QVector3D scale() {return trans.scale3D();}
float scaleX() {return trans.scale3D().x();}
float scaleY() {return trans.scale3D().y();}
float scaleZ() {return trans.scale3D().z();}
void scale(const QVector3D & sv) {raw_matrix = false; trans.setScale(trans.scale3D() * sv); buildTransform();}
void scale(GLfloat sx, GLfloat sy, GLfloat sz) {raw_matrix = false; scale(QVector3D(sx, sy, sz)); buildTransform();}
void scale(GLfloat sx, GLfloat sy) {raw_matrix = false; scale(QVector3D(sx, sy, sy)); buildTransform();}
void scale(GLfloat sx) {raw_matrix = false; scale(QVector3D(sx, sx, sx)); buildTransform();}
void scaleX(GLfloat a) {raw_matrix = false; trans.setScaleX(trans.scale3D().x() + a); buildTransform();}
void scaleY(GLfloat a) {raw_matrix = false; trans.setScaleY(trans.scale3D().y() + a); buildTransform();}
void scaleZ(GLfloat a) {raw_matrix = false; trans.setScaleZ(trans.scale3D().z() + a); buildTransform();}
void setScale(const QVector3D & a) {raw_matrix = false; trans.setScale(a); buildTransform();}
void setScale(GLfloat a) {raw_matrix = false; trans.setScale(a); buildTransform();}
void setScaleX(GLfloat a) {raw_matrix = false; trans.setScaleX(a); buildTransform();}
void setScaleY(GLfloat a) {raw_matrix = false; trans.setScaleY(a); buildTransform();}
void setScaleZ(GLfloat a) {raw_matrix = false; trans.setScaleZ(a); buildTransform();}
void resetScale() {raw_matrix = false; trans.setScale(1.f); buildTransform();}
Transform transform() {return trans;}
void setTransform(const Transform & t);
void setMatrix(const QMatrix4x4 & t);
QMatrix4x4 matrix() const;
bool isRawMatrix() {return raw_matrix;}
QVector3D inParentSpace(const QVector3D & v) const;
void transferTransformToChildren(bool only_scale = false);
void cleanTree();
bool isAcceptLight() const {return accept_light;}
void setAcceptLight(bool yes) {accept_light = yes;}
bool isAcceptFog() const {return accept_fog;}
void setAcceptFog(bool yes) {accept_fog = yes;}
bool isSelected(bool check_parents = false) const;
void setSelected(bool yes);
void select() {setSelected(true);}
void deselect() {setSelected(false);}
ObjectBase * selectedParent() const;
bool isSelectable() const {return select_;}
void setSelectable(bool yes) {select_ = yes;}
/*
bool isWriteDepth() const {return write_depth_;}
void setWriteDepth(bool yes) {write_depth_ = yes;}*/
GLenum srcAlpha() const {return blend_src;}
GLenum destAlpha() const {return blend_dest;}
void setSrcAlpha(GLenum mode) {blend_src = mode;}
void setDestAlpha(GLenum mode) {blend_dest = mode;}
void setMaterial(Material * m, bool with_children = false);
Material * material() {return material_;}
void setColor(QColor c, bool with_children = false);
QColor color() {return color_;}
const Box3D & boundingBox() const {return bound;}
void setMesh(Mesh * v);
Mesh * mesh() {return mesh_;}
void calculateBoundingBox();
void updateTransform();
void setProperty(const QString & pn, const QVariant & v);
QVariant property(const QString & pn, bool * exists = 0) const;
bool hasProperty(const QString & pn) const;
void removeProperty(const QString & pn);
QVector3D pos_h;
//QVector<GLfloat> d_vertices, d_normals, d_uvs;
protected:
virtual void transformChanged() {}
void addChildren(ObjectBaseList & list, ObjectBase * where);
void buildTransform(bool force = false);
void initInternal();
void setSceneTreeChanged();
void setObjectsChanged();
void localTransform(QMatrix4x4 & m);
QMatrix4x4 worldMatrix(QMatrix4x4 parent) const;
int prev_pass; // Pass
bool is_init, accept_light, accept_fog, visible_, cast_shadow, rec_shadow, select_, selected_, raw_matrix;
bool is_root, selected_aim;
float line_width;
QColor color_;
uint id_;
Type type_;
RenderMode render_mode;
Box3D bound;
Transform trans;
ObjectBaseList children_;
QMatrix4x4 itransform_, mat_;
QString name_;
GLenum blend_src, blend_dest;
ObjectBase * parent_;
Scene * scene_;
Material * material_;
Mesh * mesh_;
QVariantMap meta;
};
inline bool operator <(const ObjectBase & f, const ObjectBase & s) {return f.pos_h.z() < s.pos_h.z();}
class AimedObject: public ObjectBase {
friend class QGLView;
friend class GLRendererBase;
friend class Light;
friend class Camera;
public:
AimedObject();
~AimedObject() {}
QVector3D aim() const {return pos() + (direction() * aim_dist);}
QVector3D worldAim() const;
void setAim(const QVector3D & p);
QVector3D direction() const;
QVector3D worldDirection() const {return (itransform_ * QVector4D(QVector3D(0,0,-1), 0.)).toVector3D().normalized();}
void setDirection(const QVector3D & d);
void setDirection(double x, double y, double z) {setDirection(QVector3D(x, y, z));}
double distance() const {return aim_dist;}
void setDistance(double d) {aim_dist = d;}
void flyCloser(double s);
void flyFarer(double s);
void flyToDistance(double d);
void moveForward(const float & x, bool withZ = true);
void moveBackward(const float & x, bool withZ = true) {moveForward(-x, withZ);}
void moveLeft(const float & x, bool withZ = true);
void moveRight(const float & x, bool withZ = true) {moveLeft(-x, withZ);}
void moveUp(const float & x, bool onlyZ = false);
void moveDown(const float & x, bool onlyZ = false) {moveUp(-x, onlyZ);}
void rotateRoll(const float & a) {rotateY(a);}
void orbitZ(const float & a);
void orbitXY(const float & a);
protected:
void transformChanged() override;
double aim_dist;
};
class Light: public AimedObject {
friend class QGLView;
friend class RendererBase;
public:
enum Type {Omni, Cone, Directional};
Light();
Light(const QVector3D & p, const QColor & c = Qt::white, float i = 1.);
virtual ObjectBase * clone(bool withChildren = true);
virtual void init() {shadow_map.resize(512, 512); is_init = true;}
void apply();
float angle_start;
float angle_end;
float intensity;
float decay_const;
float decay_linear;
float decay_quadratic;
float decay_start;
float decay_end;
Type light_type;
Framebuffer shadow_map;
QMatrix4x4 shadow_matrix;
protected:
};
template <class T>
inline T globject_cast(ObjectBase * object) {return reinterpret_cast<T>(object);}
template <class T>
inline T globject_cast(const ObjectBase * object) {return reinterpret_cast<T>(object);}
QDataStream & operator <<(QDataStream & s, const ObjectBase * p);
QDataStream & operator >>(QDataStream & s, ObjectBase *& p);
inline ObjectBaseList lights2objectList(const QList<Light*> & v) {ObjectBaseList ret; foreach (Light*i, v) ret << (ObjectBase*)i; return ret;}
inline ObjectBaseList cameras2objectList(const QList<Camera*> & v) {ObjectBaseList ret; foreach (Camera*i, v) ret << (ObjectBase*)i; return ret;}
#endif // GLOBJECT_H

294
glrendererbase.cpp Normal file
View File

@@ -0,0 +1,294 @@
/*
QGL GLRendererBase
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "glrendererbase.h"
#include "globject.h"
#include "qglview.h"
GLRendererBase::GLRendererBase(QGLView * view_): view(view_) {
white_image = QImage(1, 1, QImage::Format_ARGB32);
white_image.fill(0xFFFFFFFF);
white_image_id = 0;
violent_image = QImage(1, 1, QImage::Format_ARGB32);
violent_image.fill(QColor(127, 127, 255));
violent_image_id = 0;
}
void GLRendererBase::setupLight(const Light & l, int inpass_index, int gl_index) {
QVector3D lp = l.worldPos(), ld;// = (l.itransform_ * QVector4D(l.direction, 0.)).toVector3D().normalized();
GLfloat pos[] = {0.f, 0.f, 0.f, 0.f};
GLfloat dir[] = {0.f, 0.f, 0.f};
GLfloat col[] = {0.f, 0.f, 0.f};
pos[0] = l.light_type == Light::Directional ? -l.direction().x() : lp.x();
pos[1] = l.light_type == Light::Directional ? -l.direction().y() : lp.y();
pos[2] = l.light_type == Light::Directional ? -l.direction().z() : lp.z();
pos[3] = l.light_type == Light::Directional ? 0. : 1.;
dir[0] = ld.x();
dir[1] = ld.y();
dir[2] = ld.z();
//col[0] = l.visible_ ? l.color().redF() * l.intensity : 0.f;
//col[1] = l.visible_ ? l.color().greenF() * l.intensity : 0.f;
//col[2] = l.visible_ ? l.color().blueF() * l.intensity : 0.f;
glEnable(gl_index);
//glLightfv(gl_index, GL_AMBIENT, ambient);
glLightfv(gl_index, GL_DIFFUSE, col);
glLightfv(gl_index, GL_SPECULAR, col);
glLightfv(gl_index, GL_POSITION, pos);
glLightf(gl_index, GL_CONSTANT_ATTENUATION, l.decay_const);
glLightf(gl_index, GL_LINEAR_ATTENUATION, l.decay_linear);
glLightf(gl_index, GL_QUADRATIC_ATTENUATION, l.decay_quadratic);
if (l.light_type == Light::Cone) {
glLightfv(gl_index, GL_SPOT_DIRECTION, dir);
glLightf(gl_index, GL_SPOT_CUTOFF, l.angle_end / 2.f);
glLightf(gl_index, GL_SPOT_EXPONENT, (1.f - piClamp<float>((l.angle_end - l.angle_start) / (l.angle_end + 0.001f), 0., 1.f)) * 128.f);
} else {
glLightf(gl_index, GL_SPOT_CUTOFF, 180.);
}
}
void GLRendererBase::setupAmbientLight(const QColor & a, bool first_pass) {
GLfloat ambient[] = {0.0f, 0.0f, 0.0f, 1.f};
if (first_pass) {
ambient[0] = view->ambientColor_.redF();
ambient[1] = view->ambientColor_.greenF();
ambient[2] = view->ambientColor_.blueF();
}
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
}
void GLRendererBase::setupShadersLights(int lights_count) {
/*foreach (QOpenGLShaderProgram * i, view->shaders_ppl) {
i->bind();
i->setUniformValue("lightsCount", lights_count);
i->setUniformValue("acc_light", lights_count > 0);
//i->setUniformValue("mat", mvm);
}*/
}
#define BIND_TEXTURE(ch, map) if (rp.prev_tex[ch] != mat.map.bitmap_id) { \
rp.prev_tex[ch] = mat.map.bitmap_id; \
glActiveTexture(GL_TEXTURE0 + ch); glBindTexture(GL_TEXTURE_2D, mat.map.bitmap_id); \
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, view->feature(QGLView::qglAnisotropicLevel).toInt());}
void GLRendererBase::setupTextures(ObjectBase & o, GLRendererBase::RenderingParameters & rp, bool first_object) {
}
#undef BIND_TEXTURE
void GLRendererBase::setupLights(int pass, int lights_per_pass) {
/*int light_start, light_end, lmax;
light_start = pass * lights_per_pass;
light_end = qMin<int>((pass + 1) * lights_per_pass, view->lights_.size());
setupAmbientLight(view->ambientColor_, pass == 0);
if (!view->lights_.isEmpty()) {
setupShadersLights(light_end - light_start);
for (int i = light_start; i < light_end; ++i)
setupLight(*view->lights_[i], i - light_start, GL_LIGHT0 + i - light_start);
lmax = light_start + 8;
for (int i = light_end; i < lmax; ++i)
glDisable(GL_LIGHT0 + i - light_start);
} else {
setupShadersLights(0);
for (int i = 0; i < 8; ++i)
glDisable(GL_LIGHT0 + i);
}*/
}
void GLRendererBase::applyFilteringParameters() {
/*if (view->isFeatureEnabled(QGLView::qglLinearFiltering)) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
} else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
}*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, view->feature(QGLView::qglAnisotropicLevel).toInt());
}
void GLRendererBase::renderObjects(int pass, int light_pass, void * shaders, bool textures, bool light, bool fog) {
/*RenderingParameters rpl;
rpl.pass = pass;
rpl.light_pass = light_pass;
rpl.shaders = shaders;
rpl.textures = textures;
rpl.light = rpl.prev_light = light;
rpl.fog = rpl.prev_fog = fog;
rpl.view_matrix = rp.view_matrix;
rpl.prev_view_matrix = rp.prev_view_matrix;
rpl.proj_matrix = rp.proj_matrix;
rpl.prev_proj_matrix = rp.prev_proj_matrix;
rpl.cam_offset_matrix = view->camera()->offsetMatrix();
//qDebug() << "view:" << rp.view_matrix;
for (int i = 0; i < 32; ++i) rpl.prev_tex[i] = 0;
setupTextures(view->objects_, rpl, true);
glSetCapEnabled(GL_TEXTURE_2D, rpl.textures);
glSetCapEnabled(GL_BLEND, pass == ObjectBase::Transparent);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_CUBE_MAP);
glPushMatrix();
renderSingleObject(view->objects_, rpl);
glPopMatrix();*/
}
void GLRendererBase::renderSingleObject(ObjectBase & o, RenderingParameters & rpl) {
// if (!o.isInit())
// o.init();
// if (!o.isTexturesLoaded())
// o.loadTextures();
// if (!o.visible_) return;
// if (rpl.pass == o.pass_) {
// //Material & mat(o.material_);
// QMatrix4x4 curview = rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_, prevview = rpl.prev_view_matrix * rpl.cam_offset_matrix * o.itransform_;
// //setupTextures(o, rpl, false);
// //mat.apply((QOpenGLShaderProgram*)rpl.shaders);
// glSetPolygonMode(o.render_mode != ObjectBase::View ? o.render_mode : (view->rmode != ObjectBase::View ? view->rmode : GL_FILL));
// glLineWidth(o.line_width > 0.f ? o.line_width : view->lineWidth_);
// glPointSize(o.line_width > 0.f ? o.line_width : view->lineWidth_);
// o.update();
// /*if (o.pass_ == GLObjectBase::Transparent) {
// glActiveTexture(GL_TEXTURE0 + 3);
// if (mat.reflectivity > 0.f) {
// glEnable(GL_TEXTURE_CUBE_MAP);
// //if (!mat.map_reflection.isEmpty()) mat.map_reflection.bind();
// //else glDisable(GL_TEXTURE_CUBE_MAP);
// } else glDisable(GL_TEXTURE_CUBE_MAP);
// if (rpl.light_pass > 0) glDisable(GL_TEXTURE_CUBE_MAP);
// GLfloat gm[16], bc[4] = {mat.reflectivity, mat.reflectivity, mat.reflectivity, mat.reflectivity};
// glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
// glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
// glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_CONSTANT);
// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR);
// glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, bc);
// glGetFloatv(GL_MODELVIEW_MATRIX, gm);
// glMatrixMode(GL_TEXTURE);
// ///glLoadTransposeMatrixf(gm);
// glScalef(-1., -1., -1.);
// glMatrixMode(GL_MODELVIEW);
// glActiveTexture(GL_TEXTURE0);
// }*/
// if (rpl.shaders) {
// //qDebug() << o.name() << curview << curview->determinant();
// //setUniformMatrices((QOpenGLShaderProgram*)rpl.shaders, rpl.proj_matrix, curview, rpl.prev_proj_matrix, prevview);
// } else {
// glMatrixMode(GL_MODELVIEW);
// //setGLMatrix(curview);
// }
// o.draw((QOpenGLShaderProgram*)rpl.shaders);
// }
// foreach (ObjectBase * i, o.children_)
// renderSingleObject(*i, rpl);
}
void GLRendererBase::renderShadow(Light * l, QOpenGLShaderProgram * prog, QMatrix4x4 mat) {
Camera cam;
QVector3D wp = l->worldPos();
cam.setPos(wp);
cam.setAim(wp + (l->worldTransform() * QVector4D(l->direction())).toVector3D());
cam.setDepthStart(view->camera()->depthStart());
cam.setFOV(l->angle_end);
//cam.apply(1.);
/*cam.rotateXY(l->angle_end);
QVector3D rdir = l->direction * cos(l->angle_end / 2. * deg2rad);
l->dir0 = cam.direction() - rdir;
cam.rotateXY(-l->angle_end);
cam.rotateZ(l->angle_end);
l->dir1 = cam.direction() - rdir;*/
//qDebug() << rdir << l->dir0 << l->dir1;
RenderingParameters rpl;
rpl.shaders = prog;
rpl.textures = rpl.light = rpl.fog = false;
//rpl.view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX);
//rpl.proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX);
rpl.cam_offset_matrix = cam.offsetMatrix();
QMatrix4x4 mbias;
mbias.scale(0.5, 0.5, 0.5);
mbias.translate(1., 1., 1.);
l->shadow_matrix = mbias*rpl.proj_matrix*rpl.view_matrix*rpl.cam_offset_matrix*mat;//;// * mbias;
//qDebug() << mbias;
//glPushMatrix();
//renderSingleShadow(view->objects_, rpl);
//glPopMatrix();
}
void GLRendererBase::renderSingleShadow(ObjectBase & o, RenderingParameters & rpl) {
// if (!o.isInit())
// o.init();
// if (!o.visible_) return;
// if (rpl.shaders) {
// //qDebug() << o.name() << curview << curview->determinant();
// //setUniformMatrices((QOpenGLShaderProgram*)rpl.shaders, rpl.proj_matrix, rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_);
// } else {
// glMatrixMode(GL_MODELVIEW);
// //setGLMatrix(rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_);
// }
// glPolygonMode(GL_FRONT_AND_BACK, o.render_mode != ObjectBase::View ? o.render_mode : (view->rmode != ObjectBase::View ? view->rmode : GL_FILL));
// glLineWidth(o.line_width > 0.f ? o.line_width : view->lineWidth_);
// glPointSize(o.line_width > 0.f ? o.line_width : view->lineWidth_);
// o.draw((QOpenGLShaderProgram*)rpl.shaders, true);
// foreach (ObjectBase * i, o.children_)
// renderSingleShadow(*i, rpl);
}
GLRendererBase::RenderingParameters::RenderingParameters() {
shaders = nullptr;
cur_shader = nullptr;
}
void GLRendererBase::RenderingParameters::prepare() {
//proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX);
//view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX);
viewproj_matrix = proj_matrix * view_matrix;
normal_matrix = view_matrix.normalMatrix();
proj_matrix_i = proj_matrix.inverted();
view_matrix_i = view_matrix.inverted();
viewproj_matrix_i = viewproj_matrix.inverted();
}
void GLRendererBase::RenderingParameters::setUniform(QOpenGLShaderProgram * prog) {
if (!prog) return;
prog->setUniformValue("qgl_ModelViewMatrix", view_matrix);
prog->setUniformValue("qgl_ProjectionMatrix", proj_matrix);
prog->setUniformValue("qgl_ModelViewProjectionMatrix", viewproj_matrix);
prog->setUniformValue("qgl_NormalMatrix", normal_matrix);
prog->setUniformValue("qgl_ModelViewMatrixInverse", view_matrix_i);
prog->setUniformValue("qgl_ProjectionMatrixInverse", proj_matrix_i);
prog->setUniformValue("qgl_ModelViewProjectionMatrixInverse", viewproj_matrix_i);
prog->setUniformValue("qgl_ModelViewMatrixTranspose", view_matrix.transposed());
prog->setUniformValue("qgl_ProjectionMatrixTranspose", proj_matrix.transposed());
prog->setUniformValue("qgl_ModelViewProjectionMatrixTranspose", viewproj_matrix.transposed());
prog->setUniformValue("qgl_ModelViewMatrixInverseTranspose", view_matrix_i.transposed());
prog->setUniformValue("qgl_ProjectionMatrixInverseTranspose", proj_matrix_i.transposed());
prog->setUniformValue("qgl_ModelViewProjectionMatrixInverseTranspose", viewproj_matrix_i.transposed());
}

80
glrendererbase.h Normal file
View File

@@ -0,0 +1,80 @@
/*
QGL GLRendererBase
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLRENDERERBASE_H
#define GLRENDERERBASE_H
#include "glcamera.h"
#include "glshaders.h"
class GLRendererBase: public QObject , protected QOpenGLExtraFunctions
{
friend class QGLView;
Q_OBJECT
public:
GLRendererBase(QGLView * view_);
virtual void prepareScene() {;}
virtual void renderScene() = 0;
struct RenderingParameters {
RenderingParameters();
void prepare();
void setUniform(QOpenGLShaderProgram * prog);
int pass;
int light_pass;
bool light;
bool fog;
bool textures;
bool prev_light;
bool prev_fog;
GLuint prev_tex[32];
void * shaders;
QMatrix4x4 view_matrix, view_matrix_i, prev_view_matrix;
QMatrix4x4 proj_matrix, proj_matrix_i, prev_proj_matrix;
QMatrix4x4 viewproj_matrix, viewproj_matrix_i;
QMatrix3x3 normal_matrix;
QMatrix4x4 cam_offset_matrix;
QOpenGLShaderProgram * cur_shader;
};
RenderingParameters rp;
protected:
virtual void setupLight(const Light & l, int inpass_index, int gl_index);
virtual void setupAmbientLight(const QColor & a, bool first_pass);
virtual void setupShadersLights(int lights_count);
virtual void setupTextures(ObjectBase & object, GLRendererBase::RenderingParameters & rp, bool first_object = false);
virtual void setupShadersTextures(ObjectBase & object, GLRendererBase::RenderingParameters & rp) {}
virtual void reloadShaders() {}
virtual void init(int width, int height) {}
virtual void resize(int width, int height) {}
void setupLights(int pass, int lights_per_pass);
inline void applyFilteringParameters();
void renderObjects(int pass, int light_pass, void * shaders = 0, bool textures = true, bool light = true, bool fog = true);
void renderSingleObject(ObjectBase & o, RenderingParameters & rpl);
void renderShadow(Light * l, QOpenGLShaderProgram * prog = 0, QMatrix4x4 mat = QMatrix4x4());
void renderSingleShadow(ObjectBase & o, RenderingParameters & rpl);
QGLView * view;
QImage white_image, violent_image;
GLuint white_image_id, violent_image_id;
};
#endif // GLRENDERERBASE_H

540
glscene.cpp Normal file
View File

@@ -0,0 +1,540 @@
/*
QGL Scene
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "glscene.h"
#include "glcamera.h"
#include "glmesh.h"
#include "qglview.h"
#include <chunkstream.h>
Scene::Scene() {
root_ = new ObjectBase();
root_->setScene(this);
tree_changed = mat_changed = lights_changed = true;
destroying = false;
need_reload_materials = tree_struct_changed = true;
sel_mode_ = smSingleSelection;
}
Scene::~Scene() {
destroying = true;
clear();
delete root_;
}
Scene * Scene::clone() {
Scene * ret = new Scene();
ObjectBase * o = root_->clone();
foreach (ObjectBase * co, o->children())
ret->addObject(co);
o->clearChildren();
delete o;
return ret;
}
void Scene::addObject(ObjectBase * o) {
ObjectBaseList aol = o->children(true);
attachObject(o);
foreach (ObjectBase * c, aol)
attachObject(c);
root_->addChild(o);
tree_changed = tree_struct_changed = true;
}
void Scene::addScene(const Scene * s) {
if (!s) return;
//qDebug() << "addScene clone ...";
ObjectBase * o = s->root_->clone();
o->setName(s->name());
//qDebug() << "addScene clone ok" << o << o->children(true).size();
addObject(o);
makeMaterialsUniqueNames();
//qDebug() << "addScene add ok" << o;
}
void Scene::assignFrom(const Scene * s) {
clear();
setName(s->name());
foreach (Material * m, s->materials) {
Material * nm = new Material();
*nm = *m;
nm->_changed = true;
nm->setMapsChanged();
materials << nm;
}
for (int i = 0; i < s->root_->childCount(); ++i) {
addObject(s->root_->child(i)->clone());
//qDebug() << i << o->child(i)->pos();
}
tree_changed = mat_changed = lights_changed = need_reload_materials = tree_struct_changed = true;
}
void Scene::clear() {
selected_.clear();
selected_top.clear();
emitSelectionChanged();
root_->clearChildren(true);
td_geometries << geometries;
qDeleteAll(materials);
geometries.clear();
materials.clear();
emit __destroyed();
emit treeChanged();}
void Scene::objectsCountInternal(int * cnt, ObjectBase * where) {
++(*cnt);
foreach (ObjectBase * i, where->children_)
objectsCountInternal(cnt, i);
}
int Scene::objectsCount(bool all) {
if (!all) return root_->childCount();
int cnt = 0;
objectsCountInternal(&cnt, root_);
return cnt;
}
void Scene::removeObjectInternal(ObjectBase * o, ObjectBase * where) {
if (destroying) return;
foreach (ObjectBase * i, where->children_) {
if (o == i) {
where->removeChild(i);
setObjectMeshChanged(i);
} else
removeObjectInternal(o, i);
}
}
void Scene::emitSelectionChanged() {
selected_top.clear();
foreach (ObjectBase * o, selected_) {
ObjectBase * po = o->selectedParent();
if (!po) po = o;
if (!selected_top.contains(po))
selected_top << po;
}
foreach (Mesh * m, geometries)
m->setAllSelectionChanged(true);
selectionChanged();
}
QString Scene::uniqueName(QString n, const QSet<QString> & names) {
if (!names.contains(n))
return n;
QString num;
while (!n.isEmpty()) {
if (n.right(1)[0].isDigit()) {
num.push_front(n.right(1));
n.chop(1);
} else break;
}
if (!n.endsWith('_')) n += '_';
int in = num.toInt() + 1;
QString nn = n + QString::number(in).rightJustified(3, '0');
while (names.contains(nn))
nn = n + QString::number(++in).rightJustified(3, '0');
return nn;
}
void Scene::removeObject(ObjectBase * o, bool inChildren) {
if (destroying) return;
o->setScene(nullptr);
setObjectMeshChanged(o);
if (inChildren)
removeObjectInternal(o, root_);
else
root_->removeChild(o);
__objectDeleted(o);
setTreeStructChanged();
}
void Scene::removeObject(ObjectBase & o, bool inChildren) {
if (destroying) return;
removeObject(&o, inChildren);
}
void Scene::clearObjects(bool deleteAll) {
root_->clearChildren(deleteAll);
cleanUnused();
setTreeStructChanged();
emitSelectionChanged();
}
void Scene::selectObject(ObjectBase * o, bool add_to_selection) {
//qDebug() << "selectObject" << o << add_to_selection;
if (!add_to_selection || (sel_mode_ == smSingleSelection)) clearSelection();
if (o) {
if (!add_to_selection) o->setSelected(true);
else o->setSelected(!o->isSelected());
gatherSelection();
}
emitSelectionChanged();
}
void Scene::selectObjects(ObjectBaseList ol, bool add_to_selection) {
if (!add_to_selection || (sel_mode_ == smSingleSelection)) clearSelection();
foreach (ObjectBase * o, ol) {
if (!o) continue;
o->setSelected(true);
}
gatherSelection();
emitSelectionChanged();
}
void Scene::selectObjectsByMesh() {
ObjectBaseList csl = selected_;
QSet<Mesh*> sml;
foreach (ObjectBase * o, csl)
if (o->mesh())
sml << o->mesh();
ObjectBaseList ol = root_->children(true);
foreach (ObjectBase * o, ol) {
if (sml.contains(o->mesh()))
o->setSelected(true);
}
gatherSelection();
emitSelectionChanged();
}
void Scene::selectObjectsByMaterial() {
ObjectBaseList csl = selected_;
QSet<Material*> sml;
foreach (ObjectBase * o, csl)
if (o->material())
sml << o->material();
ObjectBaseList ol = root_->children(true);
foreach (ObjectBase * o, ol) {
if (sml.contains(o->material()))
o->setSelected(true);
}
gatherSelection();
emitSelectionChanged();
}
void Scene::clearSelection() {
selected_.clear();
selected_top.clear();
ObjectBaseList ol = root_->children(true);
foreach (ObjectBase * o, ol) {
o->selected_ = o->selected_aim = false;
}
emitSelectionChanged();
}
ObjectBaseList Scene::selectedObjects(bool top_only) const {
return top_only ? selected_top : selected_;
}
ObjectBase * Scene::selectedObject() const {
if (selected_.isEmpty()) return 0;
return selected_[0];
}
void gatherMeshes(ObjectBase * o, QSet<Mesh*> & ums) {
if (o->mesh()) ums << o->mesh();
for (int i = 0; i < o->childCount(); ++i)
gatherMeshes(o->child(i), ums);
}
void Scene::cleanUnused() {
QSet<Mesh*> ums;
gatherMeshes(root_, ums);
/*QMapIterator<int, QMap<Mesh*, ObjectBaseList>> it(geometries_used);
while (it.hasNext())
ums |= it.next().value().keys().toSet();*/
for (int i = 0; i < geometries.size(); ++i) {
if (ums.contains(geometries[i])) continue;
td_geometries << geometries[i];
geometries.removeAt(i);
--i;
}
}
const Box3D & Scene::boundingBox() const {
root_->calculateBoundingBox();
return root_->boundingBox();
}
Material * Scene::newMaterial(const QString & name) {
materials << new Material(name);
makeMaterialsUniqueNames();
mat_changed = true;
return materials.back();
}
void Scene::removeMaterial(Material * m) {
if (!m || !materials.contains(m)) return;
ObjectBaseList ol = root_->children(true);
foreach (ObjectBase * o, ol)
if (o->material_ == m)
o->setMaterial(0);
materials.removeAll(m);
changed_materials.removeAll(m);
mat_changed = true;
}
void Scene::makeMaterialsUniqueNames() {
QSet<QString> names;
foreach (Material * m, materials) {
if (m->name.isEmpty()) m->name = "default_000";
m->name = uniqueName(m->name, names);
names << m->name;
}
}
ObjectBaseList Scene::objects(bool all) {
return root_->children(all);
}
void Scene::removeLight(Light * l) {
removeObject(l);
}
void Scene::dump() {
qDebug() << "Scene" << name();
qDebug() << "Meshes:" << geometries.size();
qDebug() << "Objects:" << root_->children(true).size();
}
void Scene::gatherSelection() {
selected_.clear();
ObjectBaseList ol = root_->children(true);
foreach (ObjectBase * o, ol)
if (o->selected_)
selected_ << o;
}
void Scene::attachObject(ObjectBase * o) {
if (!o) return;
o->setScene(this);
if (o->mesh()) { // search suitable mesh in this scene
o->mesh_ = attachMesh(o->mesh());
setObjectMeshChanged(o);
}
if (o->material()) { // search suitable material in this scene
uint ohash = o->material()->hash();
bool need_new = true;
foreach (Material * m, materials) {
if (m == o->material()) { // already exists by ptr
need_new = false;
break;
}
if (m->hash() == ohash) { // already exists by hash
need_new = false;
o->setMaterial(m);
break;
}
}
if (need_new) { // need to clone material and add to scene
Material * nmat = new Material();
*nmat = *(o->material());
nmat->setMapsChanged();
o->setMaterial(nmat);
materials << nmat;
}
}
setTreeStructChanged();
}
Mesh * Scene::attachMesh(Mesh * mesh) {
if (!mesh) return 0;
uint mhash = mesh->hash();
foreach (Mesh * m, geometries) {
if (m == mesh) { // already exists by ptr
return m;
}
if (m->hash() == mhash) { // already exists by hash
return m;
}
}
// need to clone mesh and add to scene
Mesh * nmesh = mesh->clone();
geometries << nmesh;
return nmesh;
}
void Scene::setTreeChanged() {
if (destroying) return;
tree_changed = true;
foreach (Mesh * m, geometries) {
m->setAllObjectsChanged(true);
m->setAllSelectionChanged(true);
}
gatherSelection();
}
void Scene::setTreeStructChanged() {
tree_struct_changed = true;
}
void Scene::setObjectMeshChanged(ObjectBase * o) {
if (o) o->setObjectsChanged();
}
void Scene::prepareTree(ObjectBase * o) {
foreach (ObjectBase * co, o->children_) {
if (co->isHidden()) continue;
switch (co->type_) {
case ObjectBase::glLight: {
Light * l = globject_cast<Light * >(co);
lights_used[l->light_type] << l;
} break;
case ObjectBase::glMesh:
if (co->mesh()) {
geometries_used[co->pass()][co->mesh()] << co;
co->setObjectsChanged();
}
break;
case ObjectBase::glCamera:
cameras_used << globject_cast<Camera * >(co);
break;
default: break;
}
prepareTree(co);
}
}
bool Scene::prepare() {
changed_materials.clear();
foreach (Material * m, materials) {
if (m->_changed) {
need_reload_materials = tree_changed = true;
changed_materials << m;
}
}
ObjectBaseList aol;
if (!tree_changed && !mat_changed) return false;
aol = root_->children(true);
if (tree_changed) {
geometries_used[rpSolid ].clear();
geometries_used[rpTransparent].clear();
lights_used.clear();
cameras_used.clear();
prepareTree(root_);
if (tree_struct_changed) {
tree_struct_changed = false;
cleanUnused();
QMetaObject::invokeMethod(this, "treeChanged", Qt::QueuedConnection);
}
tree_changed = false;
lights_changed = true;
}
mat_changed = false;
return true;
}
void Scene::destroy(QOpenGLExtraFunctions * f) {
foreach (Mesh * g, geometries)
g->destroy(f);
}
void Scene::destroyUnused(QOpenGLExtraFunctions * f) {
//if (!td_geometries.isEmpty()) qDebug() << "destroyUnused" << td_geometries.size();
foreach (Mesh * i, td_geometries)
i->destroy(f);
qDeleteAll(td_geometries);
td_geometries.clear();
}
QDataStream & operator <<(QDataStream & s, const Scene * p) {
ChunkStream cs;
//qDebug() << "place" << p->name() << "...";
QVector<short> geom_ind, mat_ind;
ObjectBaseList cl = p->root_->children(true);
geom_ind.reserve(cl.size());
mat_ind.reserve(cl.size());
foreach (ObjectBase * c, cl) {
geom_ind << p->geometries.indexOf(c->mesh());
mat_ind << p->materials.indexOf(c->material());
}
cs.add(1, p->name_).add(10, p->geometries).add(11, p->materials)
.add(20, p->root_).add(21, geom_ind).add(22, mat_ind);
s << qCompress(cs.data());
//s << cs.data();
return s;
}
QDataStream & operator >>(QDataStream & s, Scene *& p) {
p = new Scene();
//ChunkStream cs(s);
QByteArray ba;
s >> ba;
ba = qUncompress(ba);
ChunkStream cs(ba);
QVector<short> geom_ind, mat_ind;
while (!cs.atEnd()) {
switch (cs.read()) {
case 1 : cs.get(p->name_); break;
case 10: cs.get(p->geometries); break;
case 11: cs.get(p->materials); break;
case 20: cs.get(p->root_); p->root_->setScene(p); break;
case 21: cs.get(geom_ind); break;
case 22: cs.get(mat_ind); break;
}
}
p->makeMaterialsUniqueNames();
ObjectBaseList cl = p->root_->children(true);
int cnt = qMin(qMin(cl.size(), geom_ind.size()), mat_ind.size());
for (int i = 0; i < cnt; ++i) {
ObjectBase * c(cl[i]);
if (geom_ind[i] >= 0) c->mesh_ = p->geometries[geom_ind[i]];
if (mat_ind [i] >= 0) c->material_ = p->materials [mat_ind [i]];
}
return s;
}

146
glscene.h Normal file
View File

@@ -0,0 +1,146 @@
/*
QGL Scene
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLSCENE_H
#define GLSCENE_H
#include "gltypes.h"
class Scene: public QObject {
Q_OBJECT
friend class QGLView;
friend class RendererBase;
friend class Renderer;
friend class RendererMaterial;
friend class RendererService;
friend class RendererSelection;
friend class ObjectBase;
friend class Light;
friend QDataStream & operator <<(QDataStream & s, const Scene * p);
friend QDataStream & operator >>(QDataStream & s, Scene *& p);
public:
explicit Scene();
virtual ~Scene();
enum SelectionMode {
smNoSelection,
smSingleSelection,
smMultiSelection,
};
Q_ENUMS(SelectionMode)
QString name() const {return name_;}
void setName(const QString & name) {name_ = name;}
bool prepare();
Scene * clone();
/// Add object \"o\" to scene and take its ownership
/// All materials and geometries used by \"o\" tree
/// copied into this scene
void addObject(ObjectBase * o);
void addScene(const Scene * s);
void assignFrom(const Scene * s);
void clear();
int objectsCount(bool all = false);
ObjectBaseList objects(bool all = false);
ObjectBase * rootObject() {return root_;}
void removeObject(ObjectBase * o, bool inChildren = true);
void removeObject(ObjectBase & o, bool inChildren = true);
void clearObjects(bool deleteAll = false);
SelectionMode selectionMode() const {return sel_mode_;}
void setSelectionMode(SelectionMode mode) {sel_mode_ = mode;}
void selectObject(ObjectBase * o, bool add_to_selection = false);
void selectObjects(ObjectBaseList ol, bool add_to_selection = false);
void selectObjectsByMesh();
void selectObjectsByMaterial();
void clearSelection();
ObjectBaseList selectedObjects(bool top_only = false) const;
ObjectBase * selectedObject() const;
void cleanUnused();
const Box3D & boundingBox() const;
QVector<Material*> getMaterials() const {return materials;}
Material * newMaterial(const QString & name = QString());
void removeMaterial(Material * m);
void makeMaterialsUniqueNames();
void removeLight(Light * l);
void dump();
void destroy(QOpenGLExtraFunctions * f);
void destroyUnused(QOpenGLExtraFunctions * f);
protected:
void prepareTree(ObjectBase * o);
void gatherSelection();
void objectsCountInternal(int * cnt, ObjectBase * where);
void removeObjectInternal(ObjectBase * o, ObjectBase * where);
void emitSelectionChanged();
QString uniqueName(QString n, const QSet<QString> & names);
void attachObject(ObjectBase * o);
Mesh * attachMesh(Mesh * mesh);
void setTreeChanged();
void setTreeStructChanged();
void setMaterialsChanged() {mat_changed = true;}
void setLightsChanged() {lights_changed = tree_changed = true;}
void setObjectMeshChanged(ObjectBase * o);
QString name_;
ObjectBase * root_;
bool tree_changed, mat_changed, lights_changed, destroying;
bool need_reload_materials, tree_struct_changed;
QVector<bool> mat_map_changed;
QVector<Mesh*> geometries, td_geometries;
QVector<Material*> materials;
QMap<int, QMap<Mesh*, ObjectBaseList>> geometries_used; // [pass][mesh] = ObjectBaseList
QMap<int, QList<Light*>> lights_used; // by Light::Type
QList<Camera*> cameras_used;
QVector<Material*> changed_materials;
SelectionMode sel_mode_;
ObjectBaseList selected_, selected_top;
protected slots:
signals:
void __objectDeleted(ObjectBase * o);
void __destroyed();
void treeChanged();
//void treeStructChanged();
void selectionChanged();
};
QDataStream & operator <<(QDataStream & s, const Scene * p);
QDataStream & operator >>(QDataStream & s, Scene *& p);
#endif // GLSCENE_H

201
gltexture_manager.cpp Normal file
View File

@@ -0,0 +1,201 @@
/*
QGL TextureManager
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gltexture_manager.h"
#include "gltypes.h"
QStringList TextureManager::search_pathes(".");
QVector3D colorVector(QRgb c) {
return QVector3D(((uchar*)(&c))[0] / 255.f, ((uchar*)(&c))[1] / 255.f, ((uchar*)(&c))[2] / 255.f);
}
QString TextureManager::findFile(const QString & path) {
return ::findFile(path, search_pathes);
}
GLuint TextureManager::loadTexture(const QString & path, bool ownership, bool bump) {
QString p = findFile(path);
if (p.isEmpty()) return 0;
int tid = textureID(p, bump);
if (tid > 0) {
//qDebug() << "[TextureManager] Found" << path << "as" << tid;
return tid;
}
QImage image(p);
if (bump) convertToNormal(image);
//qDebug() << p << image.width() << image.height() << image.format() << bump;
GLuint tid_ = tid;
createGLTexture(f, tid_, image);///currentQGLView->bindTexture(image, GL_TEXTURE_2D);
tid = tid_;
if (tid == 0) {
qDebug() << "[TextureManager] Can`t load" << p;
return tid;
}
qDebug() << "[TextureManager] Loaded" << p << "as" << tid;
if (ownership) {
tex_ids[bump ? 1 : 0].insert(p, tid);
tex_im [bump ? 1 : 0].insert(p, image);
}
return tid;
}
GLuint TextureManager::loadTexture(const QImage & im, bool ownership, bool bump) {
if (im.isNull()) return 0;
QImage image(im);
if (bump) convertToNormal(image);
GLuint tid = 0;
createGLTexture(f, tid, im);///currentQGLView->bindTexture(image, GL_TEXTURE_2D);
if (tid == 0) {
qDebug() << "[TextureManager] Can`t load image";
return tid;
}
//qDebug() << "[TextureManager] Loaded image as" << tid;
return tid;
}
QImage TextureManager::loadTextureImage(const QString & path, bool bump) {
QString p = findFile(path);
if (p.isEmpty()) return QImage();
QImage ret = tex_im[bump ? 1 : 0].value(p);
if (!ret.isNull()) return ret;
ret = QImage(p);
if (bump) convertToNormal(ret);
tex_im[bump ? 1 : 0].insert(p, ret);
return ret;
}
void TextureManager::reloadTexture(GLuint tid, const QString & path) {
QString p = findFile(path);
if (p.isEmpty() || (tid == 0)) return;
QImage image(p);
createGLTexture(f, tid, image);
if (tid == 0) {
qDebug() << "[TextureManager] Can`t load" << p;
return;
}
qDebug() << "[TextureManager] Reloaded" << p << "as" << tid;
}
void TextureManager::reloadTexture(GLuint tid, const QImage & im) {
if (im.isNull() || (tid == 0)) return;
QImage image(im);
createGLTexture(f, tid, image);
qDebug() << "[TextureManager] Reloaded" << tid;
}
void TextureManager::convertToNormal(QImage & im) {
if (im.isNull()) return;
QImage sim = im.convertToFormat(QImage::Format_ARGB32);
float sum[3] = {0., 0., 0.};
llong a = 0;
const uchar * sd = sim.constBits();
for (int i = 0; i < sim.height(); i++) {
for (int j = 0; j < sim.width(); j++) {
sum[2] += sd[a] / 255.f - 0.5f; ++a;
sum[1] += sd[a] / 255.f - 0.5f; ++a;
sum[0] += sd[a] / 255.f - 0.5f; ++a;
++a;
}
}
float wh = sim.width() * sim.height();
sum[0] /= wh;
sum[1] /= wh;
sum[2] /= wh;
//qDebug() << sum[0] << sum[1] << sum[2];
if ((qAbs(sum[0]) <= 0.05f) && (qAbs(sum[1]) <= 0.05f) && (sum[2] >= 0.25f)) /// already normal
return;
//qDebug() << "convert to normal";
QImage dim = QImage(sim.width(), sim.height(), QImage::Format_ARGB32);
int tx, ty, w = sim.width(), h = sim.height();
a = 0;
uchar * dd = dim.bits();
for (int i = 0; i < sim.height(); i++) {
for (int j = 0; j < sim.width(); j++) {
tx = j - 1;
tx = tx < 0 ? w + tx : tx % w;
ty = i - 1;
ty = ty < 0 ? h + ty : ty % h;
QVector3D p[3], res;
p[0] = colorVector(sim.pixel(j, i));
p[1] = colorVector(sim.pixel(j, ty));
p[2] = colorVector(sim.pixel(tx, i));
res.setY(piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f));
res.setX(piClamp(0.5f - (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f));
tx = (j + 1) % w;
ty = (i + 1) % h;
p[1] = colorVector(sim.pixel(j, ty));
p[2] = colorVector(sim.pixel(tx, i));
res.setY(piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f));
res.setX(piClamp(0.5f - (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f));
res.setZ(1.f);
dd[a] = res.z() * 255; ++a;
dd[a] = res.y() * 255; ++a;
dd[a] = res.x() * 255; ++a;
dd[a] = 255; ++a;
}
}
im = dim;
//im.save("_normal.png");
}
bool TextureManager::loadTextures() {
QFileInfoList fil;
foreach (const QString & i, tex_pathes)
loadTexture(i, true);
tex_pathes.clear();
return true;
}
void TextureManager::deleteTextures() {
for (int i = 0; i < 2; ++i) {
QList<GLuint> texs = tex_ids[i].values();
qDebug() << "[TextureManager] Delete" << texs.size() << "textures";
if (!texs.isEmpty()) f->glDeleteTextures(texs.size(), &texs[0]);
tex_ids[i].clear();
tex_im [i].clear();
}
}
void TextureManager::deleteTexture(const QString & name) {
for (int i = 0; i < 2; ++i) {
if (tex_ids[i].contains(name)) {
GLuint id = tex_ids[i][name];
f->glDeleteTextures(1, &id);
tex_ids[i].remove(name);
tex_im [i].remove(name);
}
}
}
void TextureManager::clearImageCache() {
tex_im[0].clear();
tex_im[1].clear();
}

65
gltexture_manager.h Normal file
View File

@@ -0,0 +1,65 @@
/*
QGL TextureManager
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLTEXTUREMANAGER_H
#define GLTEXTUREMANAGER_H
#include <QOpenGLExtraFunctions>
#include <QDir>
#include <QMap>
#include <QFileInfo>
#include <QImage>
class TextureManager {
public:
TextureManager(QOpenGLExtraFunctions * f_): f(f_) {}
virtual ~TextureManager() {}
static void addSearchPath(const QString & path) {if (!search_pathes.contains(path)) search_pathes << path;}
static void clearSearchPathes() {search_pathes.clear();}
static QStringList searchPathes() {return search_pathes;}
static QString findFile(const QString & path);
GLuint loadTexture(const QString & path, bool ownership = true, bool bump = false);
GLuint loadTexture(const QImage & image, bool ownership = true, bool bump = false);
QImage loadTextureImage(const QString & path, bool bump = false);
void reloadTexture(GLuint tid, const QString & path);
void reloadTexture(GLuint tid, const QImage & image);
int textureID (const QString & path, bool bump = false) {return tex_ids[bump ? 1 : 0][path];}
QImage textureImage(const QString & path, bool bump = false) {return tex_im [bump ? 1 : 0][path];}
void addTexture(const QString & path) {tex_pathes << path;}
bool loadTextures();
void deleteTextures();
void deleteTexture(const QString & name);
void clearImageCache();
protected:
static void convertToNormal(QImage & im);
static QStringList search_pathes;
QMap<QString, GLuint> tex_ids[2];
QMap<QString, QImage> tex_im [2];
QStringList tex_pathes;
QOpenGLExtraFunctions * f;
};
#endif // GLTEXTUREMANAGER_H

240
glwidget.cpp Normal file
View File

@@ -0,0 +1,240 @@
/*
QGL GLWidget
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "glwidget.h"
#include "qglview.h"
#include <QVBoxLayout>
GLWidget::GLWidget(QWidget *parent) : QWidget(parent) {
view_ = new QGLView();
view_->setFlags(windowFlags() | Qt::FramelessWindowHint);
container = QWidget::createWindowContainer(view_, this);
lay = new QVBoxLayout(this);
lay->addWidget(container);
lay->setContentsMargins(0, 0, 0, 0);
lay->setSpacing(0);
setMouseTracking(true);
setWindowIcon(QIcon("://icons/qglview.png"));
connect(view_, &QGLView::doubleClick, this, &GLWidget::viewDoubleClicked);
}
QColor GLWidget::backColor() const {
return view_->backColor();
}
qreal GLWidget::lineWidth() const {
return view_->lineWidth();
}
qreal GLWidget::FOV() const {
return view_->FOV();
}
qreal GLWidget::depthStart() const {
return view_->depthStart();
}
QColor GLWidget::ambientColor() const {
return view_->ambientColor();
}
bool GLWidget::isLightEnabled() const {
return view_->isLightEnabled();
}
bool GLWidget::isGrabMouseEnabled() const {
return view_->isGrabMouseEnabled();
}
bool GLWidget::isMouseRotateEnabled() const {
return view_->isMouseRotateEnabled();
}
bool GLWidget::isMouseSelectionEnabled() const {
return view_->isMouseSelectionEnabled();
}
bool GLWidget::isCameraOrbit() const
{
return view_->isCameraOrbit();
}
bool GLWidget::isHoverHaloEnabled() const {
return view_->isHoverHaloEnabled();
}
QColor GLWidget::hoverHaloColor() const {
return view_->hoverHaloColor();
}
qreal GLWidget::hoverHaloFillAlpha() const {
return view_->hoverHaloFillAlpha();
}
bool GLWidget::isSelectionHaloEnabled() const {
return view_->isSelectionHaloEnabled();
}
QColor GLWidget::selectionHaloColor() const {
return view_->selectionHaloColor();
}
qreal GLWidget::selectionHaloFillAlpha() const {
return view_->selectionHaloFillAlpha();
}
Scene * GLWidget::scene() {
return view_->scene();
}
void GLWidget::addObject(ObjectBase * o) {
view_->scene()->addObject(o);
}
QByteArray GLWidget::saveCamera() {
return view_->saveCamera();
}
void GLWidget::restoreCamera(const QByteArray &ba) {
view_->restoreCamera(ba);
}
void GLWidget::stop() {
view_->stop();
}
void GLWidget::start(float freq) {
view_->start(freq);
}
void GLWidget::setBackColor(const QColor & c) {
view_->setBackColor(c);
}
void GLWidget::setLineWidth(const qreal & arg) {
view_->setLineWidth(arg);
}
void GLWidget::setFOV(const qreal & arg) {
view_->setFOV(arg);
}
void GLWidget::setDepthStart(const qreal & arg) {
view_->setDepthStart(arg);
}
void GLWidget::setAmbientColor(const QColor & arg) {
view_->setAmbientColor(arg);
}
void GLWidget::setLightEnabled(const bool & arg) {
view_->setLightEnabled(arg);
}
void GLWidget::setGrabMouseEnabled(const bool & arg) {
view_->setGrabMouseEnabled(arg);
}
void GLWidget::setMouseRotateEnabled(const bool & arg) {
view_->setMouseRotateEnabled(arg);
}
void GLWidget::setMouseSelectionEnabled(const bool & arg) {
view_->setMouseSelectionEnabled(arg);
}
void GLWidget::setCameraOrbit(const bool & arg) {
view_->setCameraOrbit(arg);
}
void GLWidget::setHoverHaloEnabled(const bool & arg) {
view_->setHoverHaloEnabled(arg);
}
void GLWidget::setHoverHaloColor(const QColor & arg) {
view_->setHoverHaloColor(arg);
}
void GLWidget::setHoverHaloFillAlpha(const qreal & arg) {
view_->setHoverHaloFillAlpha(arg);
}
void GLWidget::setSelectionHaloEnabled(const bool & arg) {
view_->setSelectionHaloEnabled(arg);
}
void GLWidget::setSelectionHaloColor(const QColor & arg) {
view_->setSelectionHaloColor(arg);
}
void GLWidget::setSelectionHaloFillAlpha(const qreal & arg) {
view_->setSelectionHaloFillAlpha(arg);
}
void GLWidget::viewDoubleClicked() {
if (view_->windowState() == Qt::WindowFullScreen) {
view_->showNormal();
container = QWidget::createWindowContainer(view_, this);
lay->addWidget(container);
container->show();
} else {
view_->setParent(nullptr);
view_->showFullScreen();
lay->removeWidget(container);
}
}

104
glwidget.h Normal file
View File

@@ -0,0 +1,104 @@
/*
QGL GLWidget
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef GLWIDGET_H
#define GLWIDGET_H
#include <QWidget>
class QGLView;
class ObjectBase;
class Scene;
class GLWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY (QColor backColor READ backColor WRITE setBackColor)
Q_PROPERTY (qreal lineWidth READ lineWidth WRITE setLineWidth)
Q_PROPERTY (qreal FOV READ FOV WRITE setFOV)
Q_PROPERTY (qreal depthStart READ depthStart WRITE setDepthStart)
Q_PROPERTY (QColor ambientColor READ ambientColor WRITE setAmbientColor)
Q_PROPERTY (bool grabMouse READ isGrabMouseEnabled WRITE setGrabMouseEnabled)
Q_PROPERTY (bool mouseRotate READ isMouseRotateEnabled WRITE setMouseRotateEnabled)
Q_PROPERTY (bool mouseSelection READ isMouseSelectionEnabled WRITE setMouseSelectionEnabled)
Q_PROPERTY (bool cameraOrbit READ isCameraOrbit WRITE setCameraOrbit)
Q_PROPERTY (bool hoverHalo READ isHoverHaloEnabled WRITE setHoverHaloEnabled)
Q_PROPERTY (QColor hoverHaloColor READ hoverHaloColor WRITE setHoverHaloColor)
Q_PROPERTY (qreal hoverHaloFillAlpha READ hoverHaloFillAlpha WRITE setHoverHaloFillAlpha)
Q_PROPERTY (bool selectionHalo READ isSelectionHaloEnabled WRITE setSelectionHaloEnabled)
Q_PROPERTY (QColor selectionHaloColor READ selectionHaloColor WRITE setSelectionHaloColor)
Q_PROPERTY (qreal selectionHaloFillAlpha READ selectionHaloFillAlpha WRITE setSelectionHaloFillAlpha)
public:
explicit GLWidget(QWidget *parent = nullptr);
QGLView * view() {return view_;}
QColor backColor() const;
qreal lineWidth() const;
qreal FOV() const;
qreal depthStart() const;
QColor ambientColor() const;
bool isLightEnabled() const;
bool isGrabMouseEnabled() const;
bool isMouseRotateEnabled() const;
bool isMouseSelectionEnabled() const;
bool isCameraOrbit() const;
bool isHoverHaloEnabled() const;
QColor hoverHaloColor() const;
qreal hoverHaloFillAlpha() const;
bool isSelectionHaloEnabled() const;
QColor selectionHaloColor() const;
qreal selectionHaloFillAlpha() const;
Scene * scene();
void addObject(ObjectBase * o);
QByteArray saveCamera();
void restoreCamera(const QByteArray & ba);
public slots:
void stop();
void start(float freq = 60.0);
void setBackColor(const QColor & c);
void setLineWidth(const qreal & arg);
void setFOV(const qreal & arg);
void setDepthStart(const qreal & arg);
void setAmbientColor(const QColor & arg);
void setLightEnabled(const bool & arg);
void setGrabMouseEnabled(const bool & arg);
void setMouseRotateEnabled(const bool & arg);
void setMouseSelectionEnabled(const bool & arg);
void setCameraOrbit(const bool & arg);
void setHoverHaloEnabled(const bool & arg);
void setHoverHaloColor(const QColor & arg);
void setHoverHaloFillAlpha(const qreal & arg);
void setSelectionHaloEnabled(const bool & arg);
void setSelectionHaloColor(const QColor & arg);
void setSelectionHaloFillAlpha(const qreal & arg);
private slots:
void viewDoubleClicked();
private:
QWidget * container;
QGLView * view_;
QLayout * lay;
signals:
};
#endif // GLWIDGET_H

BIN
icons/add-type-camera.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
icons/add-type-empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
icons/add-type-geo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
icons/add-type-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
icons/alpha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

BIN
icons/application-exit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
icons/collapse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
icons/configure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
icons/dialog-cancel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
icons/dialog-close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
icons/document-edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
icons/document-import.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
icons/document-new.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
icons/document-open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
icons/document-save-all.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
icons/document-save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
icons/edit-clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
icons/edit-copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
icons/edit-delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
icons/edit-find.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
icons/edit-paste.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
icons/edit-rename.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
icons/expand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
icons/format-fill-color.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
icons/go-jump.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
icons/go-top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
icons/group.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
icons/layer-visible-off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
icons/layer-visible-on.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
icons/light-+.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
icons/list-add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
icons/picker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
icons/qglview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
icons/qglview.xcf Normal file

Binary file not shown.

BIN
icons/transform-move.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
icons/transform-rotate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
icons/transform-scale.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
icons/type-camera.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
icons/type-empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
icons/type-geo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
icons/type-light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
icons/view-refresh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

289
mouse_controller.cpp Normal file
View File

@@ -0,0 +1,289 @@
/*
QGL MouseController
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mouse_controller.h"
#include "glmesh.h"
#include "qglview.h"
#include <qad_types.h>
#include <QApplication>
#include <QKeyEvent>
using namespace QGLEngineShaders;
MouseController::MouseController(QGLView * view_): view(view_) {
app_scale = 1;
lastPos = QPoint(-1, -1);
cur_action = RendererService::haNoAction;
sel_button = Qt::LeftButton;
sel_mod = Qt::ControlModifier;
mouse_first = mouseSelect_ = mouseRotate_ = cameraOrbit_ = canSelect_ = true;
grabMouse_ = mouse_sec = selecting_ = customMouseMove_ = false;
}
MouseController::~MouseController() {
}
void MouseController::resize() {
mouse_first = true;
app_scale = appScale();
}
void MouseController::mouseReleaseEvent(QMouseEvent * e) {
if (cur_action != RendererService::haNoAction) {
mouseMoveEvent(e);
return;
}
bool add_ts = e->modifiers().testFlag(sel_mod);
if (selecting_) {
selecting_ = false;
canSelect_ = true;
view->renderer_.mouse_rect = QRect();
view->scene_->selectObjects(hov_objects.toList(), add_ts);
return;
}
if (canSelect_ && mouseSelect_) {
if ((lastPos - downPos).manhattanLength() < QApplication::startDragDistance()) {
if (e->button() == Qt::LeftButton) {
//qDebug() << hov_objects << hov_aims;
if (hov_objects.isEmpty() && hov_aims.isEmpty()) {
view->scene()->clearSelection();
} else {
if (!hov_objects.isEmpty())
view->scene_->selectObject(hov_objects[0], add_ts);
if (!hov_aims.isEmpty()) {
view->scene_->selectObject(hov_aims[0], add_ts);
hov_aims[0]->selected_aim = true;
}
}
}
if (e->button() == Qt::RightButton) {
if (view->renderer_.edit_mode && !view->scene()->selectedObjects().isEmpty())
view->context_menu.popup(e->globalPos());
}
}
}
canSelect_ = e->buttons() == 0;
emit view->glMouseReleaseEvent(e);
}
void MouseController::mousePressEvent(QMouseEvent * e) {
downPos = e->pos();
if (cur_action != RendererService::haNoAction && e->buttons() == Qt::LeftButton) {
return;
}
if (selecting_) {
selecting_ = false;
view->renderer_.mouse_rect = QRect();
return;
}
if (!QRect(QPoint(), view->size()).contains(e->pos())) return;
lastPos = e->pos();
downPos = e->pos();
emit view->glMousePressEvent(e);
}
void MouseController::mouseMoveEvent(QMouseEvent * e) {
QPoint cpos = e->pos();
if (cur_action != RendererService::haNoAction && (e->buttons() == Qt::LeftButton)) {
RendererService & rs(view->renderer_.rend_service);
ObjectBaseList objects = view->scene_->selectedObjects(true);
QVector<int> axis;
switch (cur_action) {
case RendererService::haMove:
if (cur_handle.testFlag(RendererService::hmMoveX)) axis << 0;
if (cur_handle.testFlag(RendererService::hmMoveY)) axis << 1;
if (cur_handle.testFlag(RendererService::hmMoveZ)) axis << 2;
break;
case RendererService::haRotate:
if (cur_handle.testFlag(RendererService::hmRotateX)) axis << 0;
if (cur_handle.testFlag(RendererService::hmRotateY)) axis << 1;
if (cur_handle.testFlag(RendererService::hmRotateZ)) axis << 2;
break;
case RendererService::haScale:
if (cur_handle.testFlag(RendererService::hmScaleX)) axis << 0;
if (cur_handle.testFlag(RendererService::hmScaleY)) axis << 1;
if (cur_handle.testFlag(RendererService::hmScaleZ)) axis << 2;
break;
default: break;
}
QVector<QVector3D> scales;
foreach (int a, axis) {
QVector3D axe_vector; axe_vector[a] = 1.;
QMatrix4x4 axis_mat = view->camera()->fullViewMatrix() * rs.axis_mat;
QVector3D center_screen = axis_mat * rs.selection_center;
QVector3D axe_screen = ((axis_mat * (rs.selection_center + axe_vector)) - center_screen).normalized();
QVector3D mouse_vector(cpos - lastPos);
mouse_vector[1] *= -1.;
if (cur_action == RendererService::haMove) {
double len_scl = 1. / QVector3D(axe_screen.x(), axe_screen.y(), 1.E-6).length();
mouse_vector /= QVector3D(view->width(), view->height(), 1);
mouse_vector *= -center_screen.z() * len_scl;
axe_vector *= QVector3D::dotProduct(axe_screen, mouse_vector);
QMatrix4x4 pmat;
foreach (ObjectBase * o, objects) {
pmat.setToIdentity();
if (o->parent())
pmat = o->parent()->worldTransform().inverted();
QVector3D dv = pmat.mapVector(axe_vector);
if (o->selected_aim) {
AimedObject * ao = (AimedObject*)o;
ao->setAim(ao->aim() + dv);
} else
o->move(dv);
}
}
if (cur_action == RendererService::haRotate) {
axe_screen.setZ(0.);
axe_screen.normalize();
QVector3D norm = QVector3D(axe_screen.y(), -axe_screen.x(), 0.);
axe_vector *= QVector3D::dotProduct(mouse_vector, norm) / 2. / app_scale;
foreach (ObjectBase * o, objects)
o->setRotation(o->rotation() + axe_vector);
}
if (cur_action == RendererService::haScale) {
mouse_vector /= QVector3D(view->width(), view->height(), 1);
mouse_vector *= 3. / app_scale;
axe_vector *= QVector3D::dotProduct(axe_screen, mouse_vector);
scales << axe_vector;
}
}
//if (cur_handle >= RendererService::htScaleX && cur_handle <= RendererService::htScaleZ ) cs = Qt::SplitHCursor;
if (cur_action == RendererService::haScale) {
double sc = 0., max = 0.;
foreach (const QVector3D & s, scales) {
double v = QVector3D::dotProduct(s, QVector3D(1,1,1));
sc += v;
max = qMax(max, qAbs(v));
}
sc = max * (sc > 0. ? 1. : -1);
QVector3D axe_vector;
foreach (int a, axis)
axe_vector[a] = 1.;
foreach (ObjectBase * o, objects)
o->scale(QVector3D(1,1,1) + (axe_vector * sc));
QCursor::setPos(view->mapToGlobal(downPos));
} else
lastPos = e->pos();
emit view->objectsPositionChanged();
return;
}
if (selecting_) {
view->renderer_.mouse_rect = QRect(downPos, cpos).normalized();
return;
}
if (e->buttons().testFlag(Qt::LeftButton)) {
if ((cpos - downPos).manhattanLength() >= QApplication::startDragDistance()) {
selecting_ = true;
canSelect_ = false;
}
return;
}
QRect g_rect(QPoint(), view->size());
if (mouseRotate_) {
float dx = e->x() - lastPos.x();
float dy = e->y() - lastPos.y();
if (e->buttons().testFlag(Qt::MidButton)) {
if (cameraOrbit_) {
view->camera()->orbitZ (dx / 4.f);
view->camera()->orbitXY(dy / 4.f);
} else {
view->camera()->rotateZ(-dx / 4.f);
view->camera()->rotateX(-dy / 4.f);
}
emit view->cameraPosChanged(view->camera()->pos());
} else if (e->buttons().testFlag(Qt::RightButton)) {
float ad = view->camera()->distance();
view->camera()->moveLeft(dx / 1000.f * ad);
view->camera()->moveUp (dy / 1000.f * ad);
emit view->cameraPosChanged(view->camera()->pos());
}
}
if (customMouseMove_) emit view->customMouseMoveEvent(e->pos(), lastPos, e->buttons());
lastPos = e->pos();
if (e->buttons() == 0) {
cur_handle = 0;
cur_action = RendererService::haNoAction;
Qt::CursorShape cs = Qt::CrossCursor;
if (view->renderer_.edit_mode) {
uint hid = view->renderer_.rend_selection.id_hover;
cur_handle = (RendererService::HandleMesh)hid;
if (hid >= RendererService::hmMoveX && hid <= RendererService::hmMaxMove ) {
cur_action = RendererService::haMove;
cs = Qt::SizeAllCursor;
}
if (hid >= RendererService::hmRotateX && hid <= RendererService::hmMaxRotate) {
cur_action = RendererService::haRotate;
cs = Qt::PointingHandCursor;
}
if (hid >= RendererService::hmScaleX && hid <= RendererService::hmMaxScale ) {
cur_action = RendererService::haScale;
cs = Qt::SplitHCursor;
}
}
if (cur_action == RendererService::haNoAction)
cur_handle = 0;
view->setCursor(cs);
view->renderer_.rend_service.current_handle = cur_handle;
}
if (grabMouse_) {
QCursor::setPos(view->mapToGlobal(QRect(QPoint(), view->size()).center()));
if (mouse_sec) {
mouse_sec = false;
return;
}
if (mouse_first) {
mouse_first = false;
mouse_sec = true;
return;
}
lastPos = g_rect.center();
int dx = e->x() - lastPos.x();
int dy = e->y() - lastPos.y();
emit view->glMouseMoveEvent(new QMouseEvent(QEvent::MouseMove, QPoint(dx, dy), e->button(), e->buttons(), e->modifiers()));
return;
}
emit view->glMouseMoveEvent(e);
}
void MouseController::wheelEvent(QWheelEvent * e) {
if (mouseRotate_) {
if (e->delta() > 0) view->camera()->flyCloser(0.1f);
if (e->delta() < 0) view->camera()->flyFarer(0.1f);
emit view->cameraPosChanged(view->camera()->pos());
}
emit view->glWheelEvent(e);
}
void MouseController::leaveEvent(QEvent * ) {
lastPos = QPoint(-1, -1);
//qDebug() << lastPos;
}
void MouseController::mouseDoubleClickEvent(QMouseEvent * e) {
if (e->buttons().testFlag(Qt::MidButton))
emit view->doubleClick();
}

84
mouse_controller.h Normal file
View File

@@ -0,0 +1,84 @@
/*
QGL MouseController
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MOUSE_CONTROLLER_H
#define MOUSE_CONTROLLER_H
#include "glprimitives.h"
#include "glcamera.h"
#include "renderer_service.h"
#include <QMouseEvent>
#include <QTime>
class MouseController: public QObject
{
friend class QGLView;
friend class RendererSelection;
Q_OBJECT
public:
MouseController(QGLView * view_);
virtual ~MouseController();
bool isGrabMouseEnabled() const {return grabMouse_;}
bool isMouseRotateEnabled() const {return mouseRotate_;}
bool isMouseSelectionEnabled() const {return mouseSelect_;}
bool isCameraOrbit() const {return cameraOrbit_;}
Qt::MouseButton selectionButton() const {return sel_button;}
Qt::KeyboardModifier selectionModifier() const {return sel_mod;}
void setSelectionButton(Qt::MouseButton v) {sel_button = v;}
void setSelectionModifier(Qt::KeyboardModifier v) {sel_mod = v;}
protected:
void resize();
void mousePressEvent(QMouseEvent * e);
void mouseMoveEvent(QMouseEvent * e);
void mouseReleaseEvent(QMouseEvent * e);
void wheelEvent(QWheelEvent * e);
void leaveEvent(QEvent * );
void mouseDoubleClickEvent(QMouseEvent * e);
private:
QGLView * view;
QPoint lastPos, downPos;
QSet<int> keys_;
QVector<ObjectBase * > hov_objects, hov_aims;
Qt::MouseButton sel_button;
Qt::KeyboardModifier sel_mod;
RendererService::HandleAction cur_action;
QFlags<RendererService::HandleMesh> cur_handle;
float app_scale;
bool grabMouse_, mouse_first, mouseRotate_, mouseSelect_, customMouseMove_, canSelect_;
bool cameraOrbit_, selecting_, mouse_sec;
private slots:
public slots:
void setGrabMouseEnabled(const bool & arg) {grabMouse_ = arg; mouse_first = true;}
void setMouseRotateEnabled(const bool & arg) {mouseRotate_ = arg;}
void setMouseSelectionEnabled(const bool & arg) {mouseSelect_ = arg;}
void setCustomMouseMove(const bool & arg) {customMouseMove_ = arg;}
void setCameraOrbit(const bool & arg) {cameraOrbit_ = arg;}
signals:
};
#endif // QGLVIEW_H

113
openglwindow.cpp Normal file
View File

@@ -0,0 +1,113 @@
/*
QGL OpenGLWindow
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "openglwindow.h"
#include <QCoreApplication>
#include <QOpenGLContext>
#include <QOpenGLPaintDevice>
#include <QPainter>
OpenGLWindow::OpenGLWindow(QWindow *parent)
: QWindow(parent)
, m_context(nullptr)
, m_device(nullptr)
{
setFlags(flags() | Qt::FramelessWindowHint);
setSurfaceType(QWindow::OpenGLSurface);
QSurfaceFormat format = QSurfaceFormat::defaultFormat();
// qDebug() << format;
#ifdef QT_OPENGL_ES_2
format.setRenderableType(QSurfaceFormat::OpenGLES);
#else
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
format.setVersion(4, 0);
format.setProfile(QSurfaceFormat::CoreProfile);
}
#endif
format.setDepthBufferSize(24);
format.setSamples(8);
// format.setStencilBufferSize(8);
setFormat(format);
QSurfaceFormat::setDefaultFormat(format);
}
OpenGLWindow::~OpenGLWindow() {
delete m_device;
}
void OpenGLWindow::render(QPainter *painter) {
}
void OpenGLWindow::initialize() {
}
void OpenGLWindow::render() {
// if (!m_device) m_device = new QOpenGLPaintDevice;
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// m_device->setSize(size() * devicePixelRatio());
// m_device->setDevicePixelRatio(devicePixelRatio());
// QPainter painter(m_device);
// render(&painter);
}
void OpenGLWindow::renderLater() {
requestUpdate();
}
bool OpenGLWindow::event(QEvent *event) {
switch (event->type()) {
case QEvent::UpdateRequest:
renderNow();
return true;
default:
return QWindow::event(event);
}
}
void OpenGLWindow::exposeEvent(QExposeEvent *event) {
if (isExposed()) renderNow();
}
void OpenGLWindow::renderNow() {
if (!isExposed())
return;
bool needsInitialize = false;
if (!m_context) {
m_context = new QOpenGLContext(this);
m_context->setFormat(requestedFormat());
m_context->create();
needsInitialize = true;
}
m_context->makeCurrent(this);
if (needsInitialize) {
initializeOpenGLFunctions();
initialize();
}
render();
m_context->swapBuffers(this);
}

54
openglwindow.h Normal file
View File

@@ -0,0 +1,54 @@
/*
QGL OpenGLWindow
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QWindow>
#include <QOpenGLExtraFunctions>
class QPainter;
class QOpenGLContext;
class QOpenGLPaintDevice;
class OpenGLWindow: public QWindow, protected QOpenGLExtraFunctions
{
Q_OBJECT
public:
explicit OpenGLWindow(QWindow *parent = nullptr);
~OpenGLWindow();
virtual void render(QPainter *painter);
virtual void render();
virtual void initialize();
QOpenGLContext * context() {return m_context;}
public slots:
void renderLater();
void renderNow();
protected:
bool event(QEvent *event) override;
void exposeEvent(QExposeEvent *event) override;
private:
QOpenGLContext *m_context;
QOpenGLPaintDevice *m_device;
};

Some files were not shown because too many files have changed in this diff Show More