initial commit
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/.svn
|
||||||
|
CMakeLists.txt.user*
|
||||||
2
AUTHORS.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
peri4 = Пелипенко Иван <peri4ko@yandex.ru>
|
||||||
|
andrey = Бычков Андрей <andrey@signalmodelling.ru>
|
||||||
55
CMakeLists.txt
Normal 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
@@ -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
|
After Width: | Height: | Size: 32 KiB |
88
core/glbuffer.cpp
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
50
core/glframebuffer_mipmap.cpp
Normal 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);
|
||||||
|
}
|
||||||
50
core/glframebuffer_mipmap.h
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
After Width: | Height: | Size: 14 KiB |
BIN
icons/add-type-empty.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
icons/add-type-geo.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
icons/add-type-light.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
icons/alpha.png
Normal file
|
After Width: | Height: | Size: 158 B |
BIN
icons/application-exit.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
icons/collapse.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
icons/configure.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
icons/dialog-cancel.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
icons/dialog-close.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
icons/document-edit.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
icons/document-import.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
icons/document-new.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
icons/document-open.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
icons/document-save-all.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
icons/document-save.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
icons/edit-clear-locationbar-rtl.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
icons/edit-clear.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
icons/edit-copy.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
icons/edit-delete.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
icons/edit-find.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
icons/edit-paste.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
icons/edit-rename.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
icons/expand.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
icons/format-fill-color.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
icons/go-jump.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
icons/go-top.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
icons/group.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
icons/layer-visible-off.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
icons/layer-visible-on.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
icons/light-+.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
icons/list-add.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
icons/object-flip-horizontal.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
icons/object-flip-vertical.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
icons/picker.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
icons/qglview.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
icons/qglview.xcf
Normal file
BIN
icons/transform-move.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
icons/transform-rotate.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
icons/transform-scale.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
icons/type-camera.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
icons/type-empty.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
icons/type-geo.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
icons/type-light.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
icons/view-refresh.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
289
mouse_controller.cpp
Normal 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
@@ -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
@@ -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
@@ -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;
|
||||||
|
};
|
||||||
|
|
||||||