commit 3e29fd4373e5895454594d77a81fed2820826457 Author: Ivan Pelipenko Date: Tue Aug 25 21:57:31 2020 +0300 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8a2cb3e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.svn +CMakeLists.txt.user* diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100644 index 0000000..7dd5dfb --- /dev/null +++ b/AUTHORS.txt @@ -0,0 +1,2 @@ +peri4 = Пелипенко Иван +andrey = Бычков Андрей diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d852ec4 --- /dev/null +++ b/CMakeLists.txt @@ -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() diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. diff --git a/coeffs_brdf.png b/coeffs_brdf.png new file mode 100644 index 0000000..ec34d28 Binary files /dev/null and b/coeffs_brdf.png differ diff --git a/core/glbuffer.cpp b/core/glbuffer.cpp new file mode 100644 index 0000000..cd2b503 --- /dev/null +++ b/core/glbuffer.cpp @@ -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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#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); +} diff --git a/core/glbuffer.h b/core/glbuffer.h new file mode 100644 index 0000000..2cf4c5f --- /dev/null +++ b/core/glbuffer.h @@ -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 . +*/ + +#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 diff --git a/core/glcubemap.cpp b/core/glcubemap.cpp new file mode 100644 index 0000000..19b8750 --- /dev/null +++ b/core/glcubemap.cpp @@ -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 . +*/ + +#include "gltypes.h" +#include "glcubemap.h" +#include "hdr_p.h" + +using namespace QGLEngineShaders; + + +QVector loadFileHDR(const QString & path, QSize * size) { + if (size) *size = QSize(); + QVector 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 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 faceHDR(const QVector & data, QSize sz, QSize & fsz, int face) { + QVector 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 & data, QSize sz) { + bind(); + QSize fsz; + QVector 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 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(); +} +*/ diff --git a/core/glcubemap.h b/core/glcubemap.h new file mode 100644 index 0000000..6f5ed5d --- /dev/null +++ b/core/glcubemap.h @@ -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 . +*/ + +#ifndef GLCUBEMAP_H +#define GLCUBEMAP_H + +#include "glshaders_types.h" +#include "chunkstream.h" + +QVector 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 & 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 pathes; +}; + + +#endif // GLCUBEMAP_H diff --git a/core/glframebuffer.cpp b/core/glframebuffer.cpp new file mode 100644 index 0000000..88e5c92 --- /dev/null +++ b/core/glframebuffer.cpp @@ -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 . +*/ + +#include +#include "glframebuffer.h" +#include + + +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 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 Framebuffer::getPointsByte() const { + QVector 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 Framebuffer::getPointsFloat() const { + QVector 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 Framebuffer::grabF(int index) const { + QVector 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 buffers; + for (int i = 0; i < count; ++i) + buffers << GL_COLOR_ATTACHMENT0 + indeces[i]; + f->glDrawBuffers(buffers.size(), buffers.constData()); +} + + +void Framebuffer::setWriteBuffers() { + QVector buffers; + for (int i = 0; i < colors.size(); ++i) + buffers << GL_COLOR_ATTACHMENT0 + i; + f->glDrawBuffers(buffers.size(), buffers.constData()); +} + + +void Framebuffer::unsetWriteBuffers() { + QVector 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; +} diff --git a/core/glframebuffer.h b/core/glframebuffer.h new file mode 100644 index 0000000..cc6549e --- /dev/null +++ b/core/glframebuffer.h @@ -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 . +*/ + +#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 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 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 getPointsByte() const; + QVector 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 & 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 colors; + QVector color_formats; + GLenum target_; + GLuint fbo, drbo, tex_d; + GLint prev_view[4], wid, hei; + +}; + +#endif // GLFRAMEBUFFER_H diff --git a/core/glframebuffer_mipmap.cpp b/core/glframebuffer_mipmap.cpp new file mode 100644 index 0000000..6a38962 --- /dev/null +++ b/core/glframebuffer_mipmap.cpp @@ -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 . +*/ + +#include +#include "glframebuffer_mipmap.h" +#include + + +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); +} diff --git a/core/glframebuffer_mipmap.h b/core/glframebuffer_mipmap.h new file mode 100644 index 0000000..1bd9781 --- /dev/null +++ b/core/glframebuffer_mipmap.h @@ -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 . +*/ + +#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 fbo; + +}; + +#endif // GLFRAMEBUFFER_MIPMAP_H diff --git a/core/glmaterial.cpp b/core/glmaterial.cpp new file mode 100644 index 0000000..7afb341 --- /dev/null +++ b/core/glmaterial.cpp @@ -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 . +*/ + +#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(); +} diff --git a/core/glmaterial.h b/core/glmaterial.h new file mode 100644 index 0000000..661d3dc --- /dev/null +++ b/core/glmaterial.h @@ -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 . +*/ + +#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 diff --git a/core/glmesh.cpp b/core/glmesh.cpp new file mode 100644 index 0000000..1b2079d --- /dev/null +++ b/core/glmesh.cpp @@ -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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "glmesh.h" +#include "globject.h" +#include + +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 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 & objects, int type) { + VertexObject * vao = vaoByType(type); + vao->loadObjects(f, objects); +} + + +void Mesh::loadSelections(QOpenGLExtraFunctions * f, const QVector & 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(this))->vaoByType(type)->isObjectsChanged(); +} + + +bool Mesh::isSelectionChanged(int type) const { + return (const_cast(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 it(vao_map); + while (it.hasNext()) + it.next().value()->setObjectsChanged(yes); +} + + +void Mesh::setAllSelectionChanged(bool yes) { + QMapIterator 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 tri = m->triangles_; + for (int i = 0; i < tri.size(); ++i) + tri[i] += vcnt; + triangles_.append(tri); + QVector 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(); break; + } + } + m->changed = true; + return s; +} diff --git a/core/glmesh.h b/core/glmesh.h new file mode 100644 index 0000000..e87f336 --- /dev/null +++ b/core/glmesh.h @@ -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 . +*/ + +#ifndef GLMESH_H +#define GLMESH_H + +#include +#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 & objects, int type = 0); + void loadSelections(QOpenGLExtraFunctions * f, const QVector & 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 & vertices () {changed = hash_changed = true; return vertices_;} + QVector & normals () {changed = hash_changed = true; return normals_;} + QVector & 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 vertices_, normals_, tangents_, bitangents_; + QVector texcoords_; + QVector< Vector3i> triangles_; + QVector< Vector2i> lines_; + + QVector data_; + GLenum geom_type; + Buffer buffer_geom, buffer_ind; + QMap 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 diff --git a/core/glprimitives.cpp b/core/glprimitives.cpp new file mode 100644 index 0000000..6f6579b --- /dev/null +++ b/core/glprimitives.cpp @@ -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 . +*/ + +#include "glprimitives.h" +#include "glmesh.h" + + +Mesh * Primitive::plane(float width, float length) { + Mesh * ret = new Mesh(); + QVector & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & t(ret->texcoords()); + QVector< Vector3i> & ind(ret->indicesTriangles()); + + QVector cv, cn; + QVector 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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 & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & 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; +} diff --git a/core/glprimitives.h b/core/glprimitives.h new file mode 100644 index 0000000..7129bb1 --- /dev/null +++ b/core/glprimitives.h @@ -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 . +*/ + +#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 diff --git a/core/glshaders.cpp b/core/glshaders.cpp new file mode 100644 index 0000000..4fff0d2 --- /dev/null +++ b/core/glshaders.cpp @@ -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 . +*/ + +#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; +} diff --git a/core/glshaders.h b/core/glshaders.h new file mode 100644 index 0000000..57fc6a9 --- /dev/null +++ b/core/glshaders.h @@ -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 . +*/ + +#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 diff --git a/core/glshaders_headers.h b/core/glshaders_headers.h new file mode 100644 index 0000000..dac5c6e --- /dev/null +++ b/core/glshaders_headers.h @@ -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 . +*/ + +#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 diff --git a/core/glshaders_types.cpp b/core/glshaders_types.cpp new file mode 100644 index 0000000..fe57b1b --- /dev/null +++ b/core/glshaders_types.cpp @@ -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 . +*/ + +#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); + +} diff --git a/core/glshaders_types.h b/core/glshaders_types.h new file mode 100644 index 0000000..55f44bc --- /dev/null +++ b/core/glshaders_types.h @@ -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 . +*/ + +#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 diff --git a/core/gltexturearray.cpp b/core/gltexturearray.cpp new file mode 100644 index 0000000..1149ad6 --- /dev/null +++ b/core/gltexturearray.cpp @@ -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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#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_); +} diff --git a/core/gltexturearray.h b/core/gltexturearray.h new file mode 100644 index 0000000..f8b32b7 --- /dev/null +++ b/core/gltexturearray.h @@ -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 . +*/ + +#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 diff --git a/core/gltransform.cpp b/core/gltransform.cpp new file mode 100644 index 0000000..d92ebb7 --- /dev/null +++ b/core/gltransform.cpp @@ -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 . +*/ + + +#include "gltransform.h" +#include "gltypes.h" +#include + + +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); +} diff --git a/core/gltransform.h b/core/gltransform.h new file mode 100644 index 0000000..f1e42de --- /dev/null +++ b/core/gltransform.h @@ -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 . +*/ + +#ifndef GLTRANSFORM_H +#define GLTRANSFORM_H + +#include +#include +#include +#include + + +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()); break; + // } + //} + //return s; + QMatrix4x4 m; + s >> m; + v.setMatrix(m); + v.m_matrixDirty = true; + return s; +} + +#endif // QT3DCORE_QTRANSFORM_H diff --git a/core/gltypes.cpp b/core/gltypes.cpp new file mode 100644 index 0000000..22493de --- /dev/null +++ b/core/gltypes.cpp @@ -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 . +*/ + +#include "glcamera.h" +#include "qglview.h" +#include "gltexture_manager.h" + +#include + +//__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 & 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(ix, points[i].x()); ax = qMax(ax, points[i].x()); + iy = qMin(iy, points[i].y()); ay = qMax(ay, points[i].y()); + iz = qMin(iz, points[i].z()); az = qMax(az, points[i].z()); + } + x = ix; + y = iy; + z = iz; + length = ax - ix; + width = ay - iy; + height = az - iz; +} + + +QVector Box3D::corners() const { + QVector 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(&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; +} diff --git a/core/gltypes.h b/core/gltypes.h new file mode 100644 index 0000000..5550643 --- /dev/null +++ b/core/gltypes.h @@ -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 . +*/ + +#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 +//#ifndef WINDOWS +//# ifdef MAC +//# include +//# include +//# include +//# else +//# include +//# include +//# include +//# endif +//#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QNX +# include +# include +#else +# include +# include +#endif +#include +#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 inline void piSwap(T & f, T & s) {T t(f); f = s; s = t;} +template inline Type piMin(const Type & f, const Type & s) {return (f > s) ? s : f;} +template inline Type piMin(const Type & f, const Type & s, const Type & t) {return (f < s && f < t) ? f : ((s < t) ? s : t);} +template inline Type piMax(const Type & f, const Type & s) {return (f < s) ? s : f;} +template inline Type piMax(const Type & f, const Type & s, const Type & t) {return (f > s && f > t) ? f : ((s > t) ? s : t);} +template 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(c.red() * v, 0, 255), piClamp(c.green() * v, 0, 255), piClamp(c.blue() * v, 0, 255), piClamp(c.alpha() * v, 0, 255));} +inline QColor operator /(const QColor & c, float v) {return QColor(piClamp(c.red() / v, 0, 255), piClamp(c.green() / v, 0, 255), piClamp(c.blue() / v, 0, 255), piClamp(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 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 & 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 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 diff --git a/core/glvertexobject.cpp b/core/glvertexobject.cpp new file mode 100644 index 0000000..e4a5437 --- /dev/null +++ b/core/glvertexobject.cpp @@ -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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#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 & objects) { + loadBuffer(f, buffer_obj, objects.constData(), objects.size() * sizeof(Object)); +} + + +void VertexObject::loadSelections(QOpenGLExtraFunctions * f, const QVector & 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); +} diff --git a/core/glvertexobject.h b/core/glvertexobject.h new file mode 100644 index 0000000..58b0894 --- /dev/null +++ b/core/glvertexobject.h @@ -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 . +*/ + +#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 & objects); + void loadSelections(QOpenGLExtraFunctions * f, const QVector & 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 diff --git a/core/hdr.cpp b/core/hdr.cpp new file mode 100644 index 0000000..d1e2248 --- /dev/null +++ b/core/hdr.cpp @@ -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 . +*/ + +#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(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. +*/ + +#ifndef HDR_P_H +#define HDR_P_H + +#include + +bool RGBE_ReadPixels_RLE(QDataStream * fp, float * data, int scanline_width, int num_scanlines); + +#endif // HDR_P_H diff --git a/core/qglengine_version.h b/core/qglengine_version.h new file mode 100644 index 0000000..0895067 --- /dev/null +++ b/core/qglengine_version.h @@ -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 diff --git a/formats/loader_assimp.cpp b/formats/loader_assimp.cpp new file mode 100644 index 0000000..fe53b77 --- /dev/null +++ b/formats/loader_assimp.cpp @@ -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 . +*/ + +#include "loader_assimp.h" +#include "glscene.h" +#include "glmesh.h" +#include "glmaterial.h" +#include "globject.h" +#include +#include +#include +#include +#include + +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 & v(ret->vertices()); v.resize(vcnt); + QVector & 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 & 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(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(m)->GetName()); + aiString name; + const_cast(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 & meshes, aiMesh ** ai_meshes, + const QVector & materials, + const QMap & light_by_name, QVector & 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 meshes; + for (uint i = 0; i < ais->mNumMeshes; ++i) + meshes << assimpMesh(ais->mMeshes[i]); + QVector materials; + for (uint i = 0; i < ais->mNumMaterials; ++i) + materials << assimpMaterial(ais->mMaterials[i]); + QVector lights; + for (uint i = 0; i < ais->mNumLights; ++i) + lights << assimpLight(ais->mLights[i]); + QMap light_by_name; + foreach (Light * l, lights) + if (l) + light_by_name[l->name()] = l; + + QVector 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(";"); +} diff --git a/formats/loader_assimp.h b/formats/loader_assimp.h new file mode 100644 index 0000000..348c32f --- /dev/null +++ b/formats/loader_assimp.h @@ -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 . +*/ + +#ifndef LOADER_ASSIMP_H +#define LOADER_ASSIMP_H + +#include "gltypes.h" + +Scene * loadScene(const QString & filepath); +QStringList supportedFormats(); + +#endif // LOADER_ASSIMP_H diff --git a/formats/loader_qgl.cpp b/formats/loader_qgl.cpp new file mode 100644 index 0000000..9e04190 --- /dev/null +++ b/formats/loader_qgl.cpp @@ -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 . +*/ + +#include "loader_qgl.h" +#include "glscene.h" +#include + + +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; +} diff --git a/formats/loader_qgl.h b/formats/loader_qgl.h new file mode 100644 index 0000000..72aed12 --- /dev/null +++ b/formats/loader_qgl.h @@ -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 . +*/ + +#ifndef LOADER_QGL_H +#define LOADER_QGL_H + +#include "gltypes.h" +#include + +Scene * loadFromQGLFile(const QString & filepath); +bool saveToQGLFile(const QString & filepath, const Scene * scene); + +#endif // LOADER_QGL_H diff --git a/glcamera.cpp b/glcamera.cpp new file mode 100644 index 0000000..d7dda9a --- /dev/null +++ b/glcamera.cpp @@ -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 . +*/ + +#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); +} diff --git a/glcamera.h b/glcamera.h new file mode 100644 index 0000000..c8743f8 --- /dev/null +++ b/glcamera.h @@ -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 . +*/ + +#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 diff --git a/globject.cpp b/globject.cpp new file mode 100644 index 0000000..bad61ee --- /dev/null +++ b/globject.cpp @@ -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 . +*/ + +#include "globject.h" +#include "glcamera.h" +#include "glscene.h" +#include "glmesh.h" +#include +//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 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(); + 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(); break; + case 3: if (p) p->accept_fog = cs.getData(); break; + case 4: if (p) p->visible_ = cs.getData(); break; + case 5: if (p) p->cast_shadow = cs.getData(); break; + case 6: if (p) p->rec_shadow = cs.getData(); break; + case 7: if (p) p->raw_matrix = cs.getData(); break; + case 8: if (p) p->line_width = cs.getData(); break; + case 9: if (p) p->render_mode = (ObjectBase::RenderMode)cs.getData(); break; + case 14: if (p) p->mat_ = cs.getData(); break; + case 16: if (p) ccnt = cs.getData(); break; + case 17: if (p) p->name_ = cs.getData(); break; + case 18: if (p) p->meta = cs.getData(); break; + case 19: if (p) p->color_ = cs.getData(); break; + case 20: if (p) p->trans = cs.getData(); break; + case 100: if (l) l->setAim(cs.getData()); break; + case 101: if (l) l->angle_start = cs.getData(); break; + case 102: if (l) l->angle_end = cs.getData(); break; + case 103: if (l) l->intensity = cs.getData(); break; + case 104: if (l) l->decay_const = cs.getData(); break; + case 105: if (l) l->decay_linear = cs.getData(); break; + case 106: if (l) l->decay_quadratic = cs.getData(); break; + case 107: if (l) l->decay_start = cs.getData(); break; + case 108: if (l) l->decay_end = cs.getData(); break; + case 109: if (l) l->light_type = (Light::Type)cs.getData(); break; + case 111: if (l) l->setDistance(cs.getData()); break; + case 200: if (c) c->setAim(cs.getData()); break; + case 201: if (c) c->setFOV(cs.getData()); break; + case 202: if (c) c->setDepthStart(cs.getData()); break; + case 206: if (c) c->mirror_x = cs.getData(); break; + case 207: if (c) c->mirror_y = cs.getData(); break; + case 208: if (c) c->setDistance(cs.getData()); break; + case 209: if (c) c->roll_ = cs.getData(); 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; +} diff --git a/globject.h b/globject.h new file mode 100644 index 0000000..bfd9958 --- /dev/null +++ b/globject.h @@ -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 . +*/ + +#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 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 +inline T globject_cast(ObjectBase * object) {return reinterpret_cast(object);} + +template +inline T globject_cast(const ObjectBase * object) {return reinterpret_cast(object);} + + +QDataStream & operator <<(QDataStream & s, const ObjectBase * p); +QDataStream & operator >>(QDataStream & s, ObjectBase *& p); + +inline ObjectBaseList lights2objectList(const QList & v) {ObjectBaseList ret; foreach (Light*i, v) ret << (ObjectBase*)i; return ret;} +inline ObjectBaseList cameras2objectList(const QList & v) {ObjectBaseList ret; foreach (Camera*i, v) ret << (ObjectBase*)i; return ret;} + +#endif // GLOBJECT_H diff --git a/glrendererbase.cpp b/glrendererbase.cpp new file mode 100644 index 0000000..edb92b6 --- /dev/null +++ b/glrendererbase.cpp @@ -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 . +*/ + +#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((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((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()); +} diff --git a/glrendererbase.h b/glrendererbase.h new file mode 100644 index 0000000..7076d7a --- /dev/null +++ b/glrendererbase.h @@ -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 . +*/ + +#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 diff --git a/glscene.cpp b/glscene.cpp new file mode 100644 index 0000000..1f51bc7 --- /dev/null +++ b/glscene.cpp @@ -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 . +*/ + +#include "glscene.h" +#include "glcamera.h" +#include "glmesh.h" +#include "qglview.h" +#include + + +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 & 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 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 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 & ums) { + if (o->mesh()) ums << o->mesh(); + for (int i = 0; i < o->childCount(); ++i) + gatherMeshes(o->child(i), ums); +} +void Scene::cleanUnused() { + QSet ums; + gatherMeshes(root_, ums); + /*QMapIterator> 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 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(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(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 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 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; +} diff --git a/glscene.h b/glscene.h new file mode 100644 index 0000000..e9e39c8 --- /dev/null +++ b/glscene.h @@ -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 . +*/ + +#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 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 & 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 mat_map_changed; + + QVector geometries, td_geometries; + QVector materials; + + QMap> geometries_used; // [pass][mesh] = ObjectBaseList + QMap> lights_used; // by Light::Type + QList cameras_used; + QVector 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 diff --git a/gltexture_manager.cpp b/gltexture_manager.cpp new file mode 100644 index 0000000..fb226b1 --- /dev/null +++ b/gltexture_manager.cpp @@ -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 . +*/ + +#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 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(); +} diff --git a/gltexture_manager.h b/gltexture_manager.h new file mode 100644 index 0000000..95af8b9 --- /dev/null +++ b/gltexture_manager.h @@ -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 . +*/ + +#ifndef GLTEXTUREMANAGER_H +#define GLTEXTUREMANAGER_H + +#include +#include +#include +#include +#include + + +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 tex_ids[2]; + QMap tex_im [2]; + QStringList tex_pathes; + + QOpenGLExtraFunctions * f; + +}; + + +#endif // GLTEXTUREMANAGER_H diff --git a/glwidget.cpp b/glwidget.cpp new file mode 100644 index 0000000..29bde34 --- /dev/null +++ b/glwidget.cpp @@ -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 . +*/ + +#include "glwidget.h" +#include "qglview.h" +#include + + +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); + } +} diff --git a/glwidget.h b/glwidget.h new file mode 100644 index 0000000..edef74a --- /dev/null +++ b/glwidget.h @@ -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 . +*/ + +#ifndef GLWIDGET_H +#define GLWIDGET_H + +#include + + +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 diff --git a/icons/add-type-camera.png b/icons/add-type-camera.png new file mode 100644 index 0000000..13b5588 Binary files /dev/null and b/icons/add-type-camera.png differ diff --git a/icons/add-type-empty.png b/icons/add-type-empty.png new file mode 100644 index 0000000..df5f5b3 Binary files /dev/null and b/icons/add-type-empty.png differ diff --git a/icons/add-type-geo.png b/icons/add-type-geo.png new file mode 100644 index 0000000..b983991 Binary files /dev/null and b/icons/add-type-geo.png differ diff --git a/icons/add-type-light.png b/icons/add-type-light.png new file mode 100644 index 0000000..a896c1a Binary files /dev/null and b/icons/add-type-light.png differ diff --git a/icons/alpha.png b/icons/alpha.png new file mode 100644 index 0000000..5435669 Binary files /dev/null and b/icons/alpha.png differ diff --git a/icons/application-exit.png b/icons/application-exit.png new file mode 100644 index 0000000..8adbcba Binary files /dev/null and b/icons/application-exit.png differ diff --git a/icons/collapse.png b/icons/collapse.png new file mode 100644 index 0000000..ec329aa Binary files /dev/null and b/icons/collapse.png differ diff --git a/icons/configure.png b/icons/configure.png new file mode 100644 index 0000000..2a81957 Binary files /dev/null and b/icons/configure.png differ diff --git a/icons/dialog-cancel.png b/icons/dialog-cancel.png new file mode 100644 index 0000000..ace88ab Binary files /dev/null and b/icons/dialog-cancel.png differ diff --git a/icons/dialog-close.png b/icons/dialog-close.png new file mode 100644 index 0000000..6072634 Binary files /dev/null and b/icons/dialog-close.png differ diff --git a/icons/document-edit.png b/icons/document-edit.png new file mode 100644 index 0000000..1ecb10c Binary files /dev/null and b/icons/document-edit.png differ diff --git a/icons/document-import.png b/icons/document-import.png new file mode 100644 index 0000000..8cc3eb0 Binary files /dev/null and b/icons/document-import.png differ diff --git a/icons/document-new.png b/icons/document-new.png new file mode 100644 index 0000000..e4d8b47 Binary files /dev/null and b/icons/document-new.png differ diff --git a/icons/document-open.png b/icons/document-open.png new file mode 100644 index 0000000..63380e4 Binary files /dev/null and b/icons/document-open.png differ diff --git a/icons/document-save-all.png b/icons/document-save-all.png new file mode 100644 index 0000000..73405f7 Binary files /dev/null and b/icons/document-save-all.png differ diff --git a/icons/document-save.png b/icons/document-save.png new file mode 100644 index 0000000..aee3e22 Binary files /dev/null and b/icons/document-save.png differ diff --git a/icons/edit-clear-locationbar-rtl.png b/icons/edit-clear-locationbar-rtl.png new file mode 100644 index 0000000..0e6d645 Binary files /dev/null and b/icons/edit-clear-locationbar-rtl.png differ diff --git a/icons/edit-clear.png b/icons/edit-clear.png new file mode 100644 index 0000000..cae930b Binary files /dev/null and b/icons/edit-clear.png differ diff --git a/icons/edit-copy.png b/icons/edit-copy.png new file mode 100644 index 0000000..a8178ca Binary files /dev/null and b/icons/edit-copy.png differ diff --git a/icons/edit-delete.png b/icons/edit-delete.png new file mode 100644 index 0000000..38f11cd Binary files /dev/null and b/icons/edit-delete.png differ diff --git a/icons/edit-find.png b/icons/edit-find.png new file mode 100644 index 0000000..140e581 Binary files /dev/null and b/icons/edit-find.png differ diff --git a/icons/edit-paste.png b/icons/edit-paste.png new file mode 100644 index 0000000..a9aac10 Binary files /dev/null and b/icons/edit-paste.png differ diff --git a/icons/edit-rename.png b/icons/edit-rename.png new file mode 100644 index 0000000..4b47837 Binary files /dev/null and b/icons/edit-rename.png differ diff --git a/icons/expand.png b/icons/expand.png new file mode 100644 index 0000000..6a963b6 Binary files /dev/null and b/icons/expand.png differ diff --git a/icons/format-fill-color.png b/icons/format-fill-color.png new file mode 100644 index 0000000..99412e9 Binary files /dev/null and b/icons/format-fill-color.png differ diff --git a/icons/go-jump.png b/icons/go-jump.png new file mode 100644 index 0000000..7437eff Binary files /dev/null and b/icons/go-jump.png differ diff --git a/icons/go-top.png b/icons/go-top.png new file mode 100644 index 0000000..489674e Binary files /dev/null and b/icons/go-top.png differ diff --git a/icons/group.png b/icons/group.png new file mode 100644 index 0000000..5108637 Binary files /dev/null and b/icons/group.png differ diff --git a/icons/layer-visible-off.png b/icons/layer-visible-off.png new file mode 100644 index 0000000..b1f04a9 Binary files /dev/null and b/icons/layer-visible-off.png differ diff --git a/icons/layer-visible-on.png b/icons/layer-visible-on.png new file mode 100644 index 0000000..9117bea Binary files /dev/null and b/icons/layer-visible-on.png differ diff --git a/icons/light-+.png b/icons/light-+.png new file mode 100644 index 0000000..e7d8d80 Binary files /dev/null and b/icons/light-+.png differ diff --git a/icons/list-add.png b/icons/list-add.png new file mode 100644 index 0000000..a15dd10 Binary files /dev/null and b/icons/list-add.png differ diff --git a/icons/object-flip-horizontal.png b/icons/object-flip-horizontal.png new file mode 100644 index 0000000..2c0579e Binary files /dev/null and b/icons/object-flip-horizontal.png differ diff --git a/icons/object-flip-vertical.png b/icons/object-flip-vertical.png new file mode 100644 index 0000000..7a9ee82 Binary files /dev/null and b/icons/object-flip-vertical.png differ diff --git a/icons/picker.png b/icons/picker.png new file mode 100644 index 0000000..da411da Binary files /dev/null and b/icons/picker.png differ diff --git a/icons/qglview.png b/icons/qglview.png new file mode 100644 index 0000000..21bc50b Binary files /dev/null and b/icons/qglview.png differ diff --git a/icons/qglview.xcf b/icons/qglview.xcf new file mode 100644 index 0000000..50d33a6 Binary files /dev/null and b/icons/qglview.xcf differ diff --git a/icons/transform-move.png b/icons/transform-move.png new file mode 100644 index 0000000..b20fc44 Binary files /dev/null and b/icons/transform-move.png differ diff --git a/icons/transform-rotate.png b/icons/transform-rotate.png new file mode 100644 index 0000000..5063158 Binary files /dev/null and b/icons/transform-rotate.png differ diff --git a/icons/transform-scale.png b/icons/transform-scale.png new file mode 100644 index 0000000..9ff1d44 Binary files /dev/null and b/icons/transform-scale.png differ diff --git a/icons/type-camera.png b/icons/type-camera.png new file mode 100644 index 0000000..e7fcde0 Binary files /dev/null and b/icons/type-camera.png differ diff --git a/icons/type-empty.png b/icons/type-empty.png new file mode 100644 index 0000000..337f0de Binary files /dev/null and b/icons/type-empty.png differ diff --git a/icons/type-geo.png b/icons/type-geo.png new file mode 100644 index 0000000..d6b825c Binary files /dev/null and b/icons/type-geo.png differ diff --git a/icons/type-light.png b/icons/type-light.png new file mode 100644 index 0000000..5301173 Binary files /dev/null and b/icons/type-light.png differ diff --git a/icons/view-refresh.png b/icons/view-refresh.png new file mode 100644 index 0000000..66f0ceb Binary files /dev/null and b/icons/view-refresh.png differ diff --git a/mouse_controller.cpp b/mouse_controller.cpp new file mode 100644 index 0000000..b9add13 --- /dev/null +++ b/mouse_controller.cpp @@ -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 . +*/ + +#include "mouse_controller.h" +#include "glmesh.h" +#include "qglview.h" +#include +#include +#include + +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 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 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(); +} diff --git a/mouse_controller.h b/mouse_controller.h new file mode 100644 index 0000000..310fe45 --- /dev/null +++ b/mouse_controller.h @@ -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 . +*/ + +#ifndef MOUSE_CONTROLLER_H +#define MOUSE_CONTROLLER_H + +#include "glprimitives.h" +#include "glcamera.h" +#include "renderer_service.h" +#include +#include + + +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 keys_; + QVector hov_objects, hov_aims; + Qt::MouseButton sel_button; + Qt::KeyboardModifier sel_mod; + RendererService::HandleAction cur_action; + QFlags 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 diff --git a/openglwindow.cpp b/openglwindow.cpp new file mode 100644 index 0000000..96c0b4b --- /dev/null +++ b/openglwindow.cpp @@ -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 . +*/ + +#include "openglwindow.h" +#include +#include +#include +#include + + +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); +} + diff --git a/openglwindow.h b/openglwindow.h new file mode 100644 index 0000000..47501f7 --- /dev/null +++ b/openglwindow.h @@ -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 . +*/ + +#include +#include + +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; +}; + diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt new file mode 100644 index 0000000..355566a --- /dev/null +++ b/plugin/CMakeLists.txt @@ -0,0 +1,18 @@ +if (DESIGNER_PLUGINS) + if (NOT Qt5) + message(WARNING "Building ${PROJECT_NAME} available only on Qt5!") + else() + project(qglengine_plugin) + include_directories("..") + add_definitions(-DQT_PLUGIN) + add_definitions(-DQT_NO_DEBUG) + add_definitions(-DQT_SHARED) + add_definitions(-DQDESIGNER_EXPORT_WIDGETS) + find_qt(Qt5 Core Designer Gui Widgets OpenGL) + qt_sources(SRC) + qt_wrap(${SRC} CPPS out_CPP QMS out_QM) + qt_add_library(${PROJECT_NAME} SHARED out_CPP) + qt_target_link_libraries(${PROJECT_NAME} qglengine) + qt_install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION QtPlugins/designer) + endif() +endif() diff --git a/plugin/qglview_designerplugin.cpp b/plugin/qglview_designerplugin.cpp new file mode 100644 index 0000000..708776b --- /dev/null +++ b/plugin/qglview_designerplugin.cpp @@ -0,0 +1,14 @@ +#include "qglview_designerplugin.h" +#include "qglviewplugin.h" + + +QGLViewDesignerPlugin::QGLViewDesignerPlugin(QObject * parent): QObject(parent) +{ + m_widgets.append(new QGLViewPlugin(this)); +} + + +QList QGLViewDesignerPlugin::customWidgets() const { + return m_widgets; +} + diff --git a/plugin/qglview_designerplugin.h b/plugin/qglview_designerplugin.h new file mode 100644 index 0000000..db6fc67 --- /dev/null +++ b/plugin/qglview_designerplugin.h @@ -0,0 +1,22 @@ +#ifndef QGLVIEW_DESIGNERPLUGIN_H +#define QGLVIEW_DESIGNERPLUGIN_H + +#include +#include + + +class QGLViewDesignerPlugin: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "qad.qglview") + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +public: + QGLViewDesignerPlugin(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QGLVIEW_DESIGNERPLUGIN_H diff --git a/plugin/qglviewplugin.cpp b/plugin/qglviewplugin.cpp new file mode 100644 index 0000000..97db1e4 --- /dev/null +++ b/plugin/qglviewplugin.cpp @@ -0,0 +1,95 @@ +#include "../glwidget.h" +#include "qglviewplugin.h" +#include +#include "glprimitives.h" +#include "qglview.h" + + +QGLViewPlugin::QGLViewPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QGLViewPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QGLViewPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QGLViewPlugin::createWidget(QWidget * parent) { + GLWidget * w = new GLWidget(parent); + if (m_initialized) { + auto axis = new GLObjectBase(); + GLObjectBase * obj; + float al = 1.; + obj = new GLPrimitiveLine(QVector3D(0, 0, -al), QVector3D(0, 0, al)); + obj->material().color_diffuse = Qt::darkBlue; obj->setAcceptLight(false); + obj->setSelectable(false); + axis->addChild(obj); + obj = new GLPrimitiveLine(QVector3D(-al, 0, 0), QVector3D(al, 0, 0)); + obj->material().color_diffuse = Qt::darkRed; obj->setAcceptLight(false); + obj->setSelectable(false); + axis->addChild(obj); + obj = new GLPrimitiveLine(QVector3D(0, -al, 0), QVector3D(0, al, 0)); + obj->material().color_diffuse = Qt::darkGreen; obj->setAcceptLight(false); + obj->setSelectable(false); + axis->addChild(obj); + w->view()->addObject(axis); + auto cam_light = new Light(); + cam_light->intensity = 0.5; + cam_light->setName("Camera_Light"); + w->view()->camera()->addChild(cam_light); + w->start(); + } + return w; +} + + +QString QGLViewPlugin::name() const { + return QLatin1String("GLWidget"); +} + + +QString QGLViewPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon QGLViewPlugin::icon() const { + return QIcon("://icons/qglview.png"); +} + + +QString QGLViewPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString QGLViewPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool QGLViewPlugin::isContainer() const { + return false; +} + + +QString QGLViewPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QGLViewPlugin::includeFile() const { + return QLatin1String("glwidget.h"); +} + diff --git a/plugin/qglviewplugin.h b/plugin/qglviewplugin.h new file mode 100644 index 0000000..6516afb --- /dev/null +++ b/plugin/qglviewplugin.h @@ -0,0 +1,33 @@ +#ifndef QGLVIEWPLUGIN_H +#define QGLVIEWPLUGIN_H + +#include +#include + + +class QGLViewPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + explicit QGLViewPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif //QGLVIEWPLUGIN_H diff --git a/qglview.cpp b/qglview.cpp new file mode 100644 index 0000000..d48d666 --- /dev/null +++ b/qglview.cpp @@ -0,0 +1,299 @@ +/* + QGLView + 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 . +*/ + +#include "qglview.h" +#include "glmesh.h" +#include "gltexture_manager.h" +#include +#include +#include +#include +#include + +using namespace QGLEngineShaders; + + +QGLView::QGLView(): OpenGLWindow(), renderer_(this), mouse(this) { + setIcon(QIcon(":/icons/qglview.png")); + deleting_ = false; + timer = 0; + need_init_ = is_first_draw = true; + backColor_ = Qt::darkGray; + hoverHaloColor_ = QColor(195, 140, 255); + selectionHaloColor_ = QColor(175, 255, 140); + ambientColor_ = QColor(10, 10, 10); + lineWidth_ = 1.; + max_anisotropic = 1; + max_texture_chanels = 8; + lightEnabled_ = true; + shaders_supported = false; + fps_cnt = 0; + fps_tm = fps_ = 0.; + fogColor_ = Qt::darkGray; + fogDensity_ = 0.; + fogDecay_ = 10.; + hoverHaloFill_ = selectionHaloFill_ = 0.15f; + //lmode = Simple; + setFeature(qglFXAA, false); + setFeature(qglAnisotropicLevel, 8); + setFeature(qglEyeAccomodationEnabled, false); + setFeature(qglEyeAccomodationTime, 16.); + setFeature(qglEyeAccomodationMaxSpeed, 0.2); + setFeature(qglBloomEnabled, false); + setFeature(qglBloomThreshold, 0.9); + setFeature(qglBloomFactor, 1.); + setFeature(qglBloomRadius, 8); + setFeature(qglMotionBlurEnabled, false); + setFeature(qglMotionBlurFactor, 1.); + setFeature(qglMotionBlurSteps, 8); + setFeature(qglShadowsEnabled, false); + setFeature(qglShadowsMapSize, 512); + setFeature(qglShadowsSoftEnabled, true); + setFeature(qglReflectionsEnabled, false); + setFeature(qglReflectionsBlur, true); + setFeature(qglSSAOEnabled, false); + setFeature(qglSSAORadius, 5); + setFeature(qglDepthOfFieldEnabled, false); + setFeature(qglDepthOfFieldAutoFocusEnabled, true); + setFeature(qglDepthOfFieldAutoFocusSpeed, 0.1); + setFeature(qglDepthOfFieldFocus, 1.); + setFeature(qglDepthOfFieldDiaphragm, 8.); + hoverHalo_ = selectionHalo_ = true; + fogEnabled_ = is_init = shaders_bind = changed_ = false; + rmode = ObjectBase::Fill; +// sel_pen = QPen(Qt::black, 1, Qt::DashLine); +// sel_brush = QBrush(QColor(170, 100, 255, 120)); + scene_ = new Scene(); + connect(scene_, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged())); + connect(scene_, SIGNAL(__destroyed()), this, SLOT(__destroyed())); + connect(scene_, SIGNAL(__objectDeleted(ObjectBase*)), this, SLOT(__objectDeleted(ObjectBase*))); + default_camera = new Camera(); + default_camera->setPos(QVector3D(2, 2, 2)); + default_camera->setAim(QVector3D()); + camera_ = default_camera; +// qDebug() << camera_->aim(); + default_camera->setName("Camera"); + emit cameraPosChanged(default_camera->pos()); + //camera().aim_ = camera().pos_; + ktm_.restart(); + + //Mesh * m = Primitive::torus(100, 40, 1., 0.4, 360); + Mesh * m = Primitive::cube(10, 10, 10); + m->flipNormals(); + //QMatrix4x4 mat; + //mat.rotate(90, 0,1,0); + //mat.translate(0, 0, 2); + //mat.rotate(180, 1,0,0); + //m->transformPoints(mat); + ObjectBase * o = new ObjectBase(m); + o->setColor(Qt::cyan); + scene()->addObject(o); + delete m; + +} + + +QGLView::~QGLView() { + deleting_ = true; + stop(); + scene_->clear(); + delete scene_; + delete default_camera; +} + + +void QGLView::stop() { + if (timer) killTimer(timer); +} + + +void QGLView::start(float freq) { + timer = startTimer(freq <= 0.f ? 0 : int(1000.f / freq)); +} + + +QList QGLView::selectedLights() const { + QList ret; + ObjectBaseList sol = scene_->selectedObjects(); + foreach (ObjectBase * o, sol) + if (o->type() == ObjectBase::glLight) + ret << (Light*)o; + return ret; +} + + +QList QGLView::selectedCameras() const { + QList ret; + ObjectBaseList sol = scene_->selectedObjects(); + foreach (ObjectBase * o, sol) + if (o->type() == ObjectBase::glCamera) + ret << (Camera*)o; + return ret; +} + + + +void QGLView::resizeEvent(QResizeEvent * e) { + renderLater(); + mouse.resize(); +} + + +void QGLView::timerEvent(QTimerEvent *) { + renderNow(); + //if (ktm_.elapsed() < QApplication::keyboardInputInterval()) return; + Qt::KeyboardModifiers km = QApplication::keyboardModifiers(); + foreach (int i, keys_) + emit keyEvent((Qt::Key)i, km); +} + + +void QGLView::render() { + resizeGL(width(), height()); + emit glBeginPaint(); + renderer_.mouse_pos = mapFromGlobal(QCursor::pos()); + renderer_.renderScene(); + emit glEndPaint(); + fps_tm += time.elapsed(); + time.restart(); + fps_cnt++; + if (fps_tm < 1000.) return; + fps_ = fps_cnt / fps_tm * 1000.; + fps_tm = 0.; + fps_cnt = 0; +} + + +void QGLView::initialize() { + checkCaps(); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glDisable(GL_MULTISAMPLE); + glDisable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_CUBE_MAP); + glEnable(GL_TEXTURE_MAX_ANISOTROPY_EXT); + glEnable(GL_CULL_FACE); + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + glCullFace(GL_BACK); + renderer_.reloadShaders(); + renderer_.init(width(), height()); + is_init = true; + need_init_ = false; + emit glInitializeDone(); +} + + +void QGLView::checkCaps() { + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropic); + shaders_supported = QOpenGLShaderProgram::hasOpenGLShaderPrograms(); +} + + +void QGLView::__destroyed() { + renderer_.rend_mat.mat_thumbnails.clear(); + mouse.hov_objects.clear(); +} + + +void QGLView::__objectDeleted(ObjectBase * o) { + if (o == camera_) + setDefaultCamera(); +} + + +void QGLView::resizeGL(int width, int height) { + if (!is_init) return; + if (width <= 0 || height <= 0) return; + if (prev_size == QSize(width, height)) return; + prev_size = QSize(width, height); + aspect = float(width) / float(height); + renderer_.resize(width, height); + //qDebug() << "resize" << width << height; + iaspect = (aspect == 0.f) ? 0. : 1 / aspect; + glViewport(0, 0, width, height); + emit glResize(width, height); +} + + +void QGLView::keyPressEvent(QKeyEvent * e) { + emit glKeyPressEvent(e); + if (e->key() > 0) keys_.insert(e->key()); + if (e->key() == Qt::Key_F11) { + emit doubleClick(); + } +} + + +void QGLView::keyReleaseEvent(QKeyEvent * e) { + emit glKeyReleaseEvent(e); + keys_.remove(e->key()); +} + + +void QGLView::focusOutEvent(QFocusEvent *) { + keys_.clear(); +} + + +void QGLView::focusOn(const Box3D & bb) { + if (bb.isEmpty() || !camera()) return; + double size = qMax(qMax(bb.width, bb.length), bb.height); + camera()->setAim(bb.center()); + camera()->flyToDistance(size * 1.25); +} + + +QByteArray QGLView::saveCamera() { + ChunkStream cs; + const Camera * c = camera(); + cs.add(1, c->pos()).add(2, c->aim()).add(3, c->rotation()).add(4, c->FOV()); + return cs.data(); +} + + +void QGLView::restoreCamera(const QByteArray & ba) { + if (ba.isEmpty()) return; + Camera * c = camera(); + QVector3D pos(c->pos()), aim(c->aim()), ang(c->rotation()); + float fov(c->FOV()); + ChunkStream cs(ba); + cs.readAll(); + cs.get(1, pos).get(2, aim).get(3, ang).get(4, fov); + camera()->setPos(pos); + camera()->setAim(aim); + camera()->setAngles(ang); + camera()->setFOV(fov); +} + + +QByteArray QGLView::saveFeatures() { + QByteArray ba; + QDataStream ds(&ba, QIODevice::WriteOnly); + ds << features_; + return ba; +} + + +void QGLView::restoreFeatures(const QByteArray & ba) { + QHash f; + QDataStream ds(ba); + ds >> f; + features_ = f; +} diff --git a/qglview.h b/qglview.h new file mode 100644 index 0000000..66a64a5 --- /dev/null +++ b/qglview.h @@ -0,0 +1,293 @@ +/* + QGLView + 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 . +*/ + +#ifndef QGLVIEW_H +#define QGLVIEW_H + +#include "openglwindow.h" +#include "glframebuffer.h" +#include "glprimitives.h" +#include "glcamera.h" +#include "glrendererbase.h" +#include "glscene.h" +#include "renderer.h" +#include "mouse_controller.h" +#include +#include + + +class QGLView: public OpenGLWindow +{ + friend class MouseController; + friend class GLRendererBase; + friend class TextureManager; + friend class ObjectBase; + friend class Scene; + friend class RendererBase; + friend class Renderer; + friend class RendererMaterial; + friend class RendererService; + friend class RendererSelection; + friend class TonemappingProc; + Q_OBJECT + Q_PROPERTY (QColor backColor READ backColor WRITE setBackColor) + Q_PROPERTY (float lineWidth READ lineWidth WRITE setLineWidth) + Q_PROPERTY (float FOV READ FOV WRITE setFOV) + Q_PROPERTY (float depthStart READ depthStart WRITE setDepthStart) + Q_PROPERTY (float gamma READ gamma WRITE setGamma) + Q_PROPERTY (bool autoExposure READ autoExposure WRITE setAutoExposure) + Q_PROPERTY (QColor ambientColor READ ambientColor WRITE setAmbientColor) + Q_PROPERTY (QColor fogColor READ fogColor WRITE setFogColor) + Q_PROPERTY (bool fogEnabled READ isFogEnabled WRITE setFogEnabled) + Q_PROPERTY (float fogDensity READ fogDensity WRITE setFogDensity) + Q_PROPERTY (float fogDecay READ fogDecay WRITE setFogDecay) + Q_PROPERTY (int renderMode READ renderMode WRITE setRenderMode) + 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 (float hoverHaloFillAlpha READ hoverHaloFillAlpha WRITE setHoverHaloFillAlpha) + Q_PROPERTY (bool selectionHalo READ isSelectionHaloEnabled WRITE setSelectionHaloEnabled) + Q_PROPERTY (QColor selectionHaloColor READ selectionHaloColor WRITE setSelectionHaloColor) + Q_PROPERTY (float selectionHaloFillAlpha READ selectionHaloFillAlpha WRITE setSelectionHaloFillAlpha) + Q_PROPERTY (Qt::MouseButton selectionButton READ selectionButton WRITE setSelectionButton) + Q_PROPERTY (Qt::KeyboardModifier selectionModifier READ selectionModifier WRITE setSelectionModifier) + Q_PROPERTY (Scene::SelectionMode selectionMode READ selectionMode WRITE setSelectionMode) + +public: + QGLView(); + virtual ~QGLView(); + + enum CameraLightMode { + clmOff, + clmAuto, + clmOn, + }; + + enum Feature { + qglFXAA, + qglAnisotropicLevel, + qglEyeAccomodationEnabled, + qglEyeAccomodationTime, + qglEyeAccomodationMaxSpeed, + qglBloomEnabled, + qglBloomThreshold, + qglBloomFactor, + qglBloomRadius, + qglMotionBlurEnabled, + qglMotionBlurFactor, + qglMotionBlurSteps, + qglShadowsEnabled, + qglShadowsMapSize, + qglShadowsSoftEnabled, + qglReflectionsEnabled, + qglReflectionsBlur, + qglSSAOEnabled, + qglSSAORadius, + qglDepthOfFieldEnabled, + qglDepthOfFieldAutoFocusEnabled, + qglDepthOfFieldAutoFocusSpeed, + qglDepthOfFieldFocus, + qglDepthOfFieldDiaphragm + }; + + Q_ENUM(CameraLightMode) + + void stop(); + void start(float freq = 60.); + + QColor backColor() const {return backColor_;} + float lineWidth() const {return lineWidth_;} + float FOV() const {return camera()->fov_;} + float depthStart() const {return camera()->depth_start;} + float currentFPS() const {return fps_;} + float gamma() const {return renderer_.gamma_;} + bool autoExposure() const {return renderer_.tone_proc.enabled;} + int maxAnisotropicLevel() const {return max_anisotropic;} + QString environmentMapFile() const {return renderer_.tex_env.fileHDR();} + + QColor ambientColor() const {return ambientColor_;} + QColor fogColor() const {return fogColor_;} + float fogDensity() const {return fogDensity_;} + float fogDecay() const {return fogDecay_;} + bool isFogEnabled() const {return fogEnabled_;} + bool isLightEnabled() const {return lightEnabled_;} + bool isGrabMouseEnabled() const {return mouse.isGrabMouseEnabled();} + bool isMouseRotateEnabled() const {return mouse.isMouseRotateEnabled();} + bool isMouseSelectionEnabled() const {return mouse.isMouseSelectionEnabled();} + bool isCameraOrbit() const {return mouse.isCameraOrbit();} + bool isHoverHaloEnabled() const {return hoverHalo_;} + QColor hoverHaloColor() const {return hoverHaloColor_;} + float hoverHaloFillAlpha() const {return hoverHaloFill_;} + bool isSelectionHaloEnabled() const {return selectionHalo_;} + QColor selectionHaloColor() const {return selectionHaloColor_;} + float selectionHaloFillAlpha() const {return selectionHaloFill_;} + + QVariant feature(Feature f) const {return features_.value(int(f));} + QVariant setFeature(Feature f, const QVariant & value) {QVariant ret = features_.value(int(f)); features_[int(f)] = value; return ret;} + bool isFeatureEnabled(Feature f) const {return features_[int(f)].toBool();} + + int renderMode() const {return (int)rmode;} + void setRenderMode(int mode) {rmode = (ObjectBase::RenderMode)mode;} + + bool isServiceMode() const {return renderer_.edit_mode;} + void setServiceMode(bool yes) {renderer_.edit_mode = yes;} + + + Scene::SelectionMode selectionMode() const {return scene_->selectionMode();} + Qt::MouseButton selectionButton() const {return mouse.selectionButton();} + Qt::KeyboardModifier selectionModifier() const {return mouse.selectionModifier();} + + void setSelectionMode(Scene::SelectionMode m) {scene_->setSelectionMode(m);} + void setSelectionButton(Qt::MouseButton v) {mouse.setSelectionButton(v);} + void setSelectionModifier(Qt::KeyboardModifier v) {mouse.setSelectionModifier(v);} + + void selectObject(ObjectBase * o, bool add_to_selection = false) {scene_->selectObject(o, add_to_selection);} + void clearSelection() {scene_->clearSelection();} + ObjectBaseList selectedObjects(bool top_only = false) const {return scene_->selectedObjects(top_only);} + QList selectedLights() const; + QList selectedCameras() const; + ObjectBase * selectedObject() const {return scene_->selectedObject();} + + TextureManager * textureManager() {return renderer_.textures_manager;} + void reloadTextures() {renderer_.markReloadTextures();} + + Scene * scene() {return scene_;} + void focusOn(const Box3D & bb); + void setCameraLightMode(CameraLightMode m) {renderer_.setCameraLightMode(m);} + CameraLightMode cameraLightMode() const {return (CameraLightMode)renderer_.cameraLightMode();} + + Camera * camera() {return camera_;} + const Camera * camera() const {return camera_;} + void setCamera(Camera * camera) {camera_ = camera;} + void setDefaultCamera() {camera_ = default_camera;} + QByteArray saveCamera(); + void restoreCamera(const QByteArray & ba); + QByteArray saveFeatures(); + void restoreFeatures(const QByteArray & ba); + + QImage materialThumbnail(Material * m) {return renderer_.materialThumbnail(m);} + void setCurrentAction(RendererService::HandleAction ha) {renderer_.rend_service.setCurrentAction(ha);} + void setContextActions(QList al) {context_menu.clear(); context_menu.addActions(al);} + + GLfloat aspect, iaspect; + Renderer renderer_; + +protected: + void render(); + void resizeEvent(QResizeEvent * e); + + void timerEvent(QTimerEvent * ); + void initialize(); + void resizeGL(int width, int height); + void mousePressEvent(QMouseEvent * e) {mouse.mousePressEvent(e);} + void mouseMoveEvent(QMouseEvent * e) {mouse.mouseMoveEvent(e);} + void mouseReleaseEvent(QMouseEvent * e) {mouse.mouseReleaseEvent(e);} + void wheelEvent(QWheelEvent * e) {mouse.wheelEvent(e);} + void mouseDoubleClickEvent(QMouseEvent * e) {mouse.mouseDoubleClickEvent(e);} + void leaveEvent(QEvent * ); + + void keyPressEvent(QKeyEvent * e); + void keyReleaseEvent(QKeyEvent * e); + void focusOutEvent(QFocusEvent *); + + void checkCaps(); + +private: + void processKeys(); + bool setupViewport(); + + Scene * scene_; + Camera * camera_, * default_camera; + MouseController mouse; + QMenu context_menu; + QSet keys_; + QColor backColor_, fogColor_, ambientColor_, hoverHaloColor_, selectionHaloColor_; + QTime time, ktm_; + GLint max_anisotropic, max_texture_chanels; + ObjectBase::RenderMode rmode; + GLRendererBase::RenderingParameters start_rp; + QHash features_; + QSize prev_size; + float lineWidth_; + float fps_, fps_tm, fogDensity_, fogDecay_; + float hoverHaloFill_, selectionHaloFill_, m_motionBlurFactor; + int timer, fps_cnt, sh_id_loc, deleting_; + bool is_first_draw, is_init, fogEnabled_, lightEnabled_; + bool shaders_supported, changed_, need_init_; + bool hoverHalo_, selectionHalo_, shaders_bind; + +private slots: + void __destroyed(); + void __objectDeleted(ObjectBase * o); + +public slots: + void setBackColor(const QColor & arg) {backColor_ = arg;} + void setLineWidth(const float & arg) {lineWidth_ = arg;} + void setFOV(const float & arg) {camera()->fov_ = arg;} + void setDepthStart(const float & arg) {camera()->depth_start = arg;} + void setGamma(const float & arg) {renderer_.gamma_ = arg;} + void setAutoExposure(bool arg) {renderer_.tone_proc.enabled = arg;} + void setAmbientColor(const QColor & arg) {ambientColor_ = arg;} + void setEnvironmentMapFile(QString file) {renderer_.tex_env.setFileHDR(file); renderer_.recreateMaterialThumbnails(true);} + void setFogColor(const QColor & arg) {fogColor_ = arg;} + void setFogDensity(const float & arg) {fogDensity_ = arg;} + void setFogDecay(const float & arg) {fogDecay_ = arg;} + void setFogEnabled(const bool & arg) {fogEnabled_ = arg;} + void setLightEnabled(const bool & arg) {lightEnabled_ = arg;} + void setGrabMouseEnabled(const bool & arg) {mouse.setGrabMouseEnabled(arg);} + void setMouseRotateEnabled(const bool & arg) {mouse.setMouseRotateEnabled(arg);} + void setMouseSelectionEnabled(const bool & arg) {mouse.setMouseSelectionEnabled(arg);} + void setCustomMouseMove(const bool & arg) {mouse.setCustomMouseMove(arg);} + void setCameraOrbit(const bool & arg) {mouse.setCameraOrbit(arg);} + void setHoverHaloEnabled(const bool & arg) {hoverHalo_ = arg;} + void setHoverHaloColor(const QColor & arg) {hoverHaloColor_ = arg;} + void setHoverHaloFillAlpha(const float & arg) {hoverHaloFill_ = arg;} + void setSelectionHaloEnabled(const bool & arg) {selectionHalo_ = arg;} + void setSelectionHaloColor(const QColor & arg) {selectionHaloColor_ = arg;} + void setSelectionHaloFillAlpha(const float & arg) {selectionHaloFill_ = arg;} + + void reloadShaders() {renderer_.reloadShaders();} + +signals: + void glBeginPaint(); + void glEndPaint(); + void glKeyPressEvent(QKeyEvent * e); + void glKeyReleaseEvent(QKeyEvent * e); + void glMousePressEvent(QMouseEvent * e); + void glMouseMoveEvent(QMouseEvent * e); + void glMouseReleaseEvent(QMouseEvent * e); + void glWheelEvent(QWheelEvent * e); + void glResize(int, int); + void glInitializeDone(); + void cameraPosChanged(QVector3D pos); + void keyEvent(Qt::Key key, Qt::KeyboardModifiers mod); + void customMouseMoveEvent(QPoint curpos, QPoint lastpos, Qt::MouseButtons buttons); + + void hoverChanged(ObjectBase * cur, ObjectBase * prev); + void selectionChanged(); + void objectsPositionChanged(); + void materialsChanged(); + void materialThumbnailCreated(Material*); + void doubleClick(); + +}; + +#endif // QGLVIEW_H diff --git a/qglview.qrc b/qglview.qrc new file mode 100644 index 0000000..e67f617 --- /dev/null +++ b/qglview.qrc @@ -0,0 +1,46 @@ + + + coeffs_brdf.png + icons/add-type-camera.png + icons/add-type-geo.png + icons/add-type-light.png + icons/add-type-empty.png + icons/collapse.png + icons/expand.png + icons/edit-rename.png + icons/alpha.png + icons/application-exit.png + icons/configure.png + icons/dialog-close.png + icons/document-edit.png + icons/document-import.png + icons/document-new.png + icons/document-open.png + icons/document-save.png + icons/document-save-all.png + icons/edit-clear.png + icons/edit-clear-locationbar-rtl.png + icons/edit-copy.png + icons/edit-delete.png + icons/edit-find.png + icons/edit-paste.png + icons/go-jump.png + icons/go-top.png + icons/layer-visible-on.png + icons/layer-visible-off.png + icons/light-+.png + icons/list-add.png + icons/object-flip-horizontal.png + icons/object-flip-vertical.png + icons/picker.png + icons/qglview.png + icons/transform-move.png + icons/transform-rotate.png + icons/transform-scale.png + icons/type-camera.png + icons/type-geo.png + icons/type-light.png + icons/type-empty.png + icons/view-refresh.png + + diff --git a/qglview_test/main.cpp b/qglview_test/main.cpp new file mode 100644 index 0000000..eb43d61 --- /dev/null +++ b/qglview_test/main.cpp @@ -0,0 +1,34 @@ +/* + QGLViewWindow + 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 . +*/ + +#include +#include +#include +#include "qglview_window.h" + +int main(int argc, char ** argv) { + QApplication a(argc, argv); + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); + QGLViewWindow w; + w.show(); + QStringList al(a.arguments()); + al.pop_front(); + foreach (QString s, al) + w.loadFile(s); + return a.exec(); +} diff --git a/qglview_test/qglview_window.cpp b/qglview_test/qglview_window.cpp new file mode 100644 index 0000000..a6ac505 --- /dev/null +++ b/qglview_test/qglview_window.cpp @@ -0,0 +1,251 @@ +/* + QGLViewWindow + 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 . +*/ + +#include "qglview_window.h" +#include "renderer.h" +#include "glwidget.h" +#include "gltexture_manager.h" +#include +#include +#include +#include "qad_types.h" + + +QGLViewWindow::QGLViewWindow(QWidget * parent): QMainWindow(parent), Ui::QGLViewWindow() { + setupUi(this); + session.setFile("session_qglview_test.conf"); + session.addEntry(this); + + extensions = "All(" + supportedFormats().join(" ") + " *.qgl)"; + extensions += ";;QGLEngine(*.qgl)"; + + //view->view()->camera()->setPos(QVector3D(2, 2, 2)); + //view->view()->camera()->setAim(QVector3D()); + //view->view()->camera()->flyToDistance(2.); +// view->setFrameShape(QFrame::NoFrame); + view->view()->setMouseRotateEnabled(true); + view->view()->setMouseSelectionEnabled(true); + view->view()->setSelectionHaloEnabled(true); + view->view()->setHoverHaloEnabled(true); + view->view()->setBackColor(Qt::lightGray); + view->view()->setDepthStart(0.1); + view->view()->setSelectionMode(Scene::smMultiSelection); + + + groupShadows->setChecked(view->view()->isFeatureEnabled(QGLView::qglShadowsEnabled)); + groupEyeAccomodation->setChecked(view->view()->isFeatureEnabled(QGLView::qglEyeAccomodationEnabled)); + groupBloom->setChecked(view->view()->isFeatureEnabled(QGLView::qglBloomEnabled)); + groupMotionBlur->setChecked(view->view()->isFeatureEnabled(QGLView::qglMotionBlurEnabled)); + groupReflections->setChecked(view->view()->isFeatureEnabled(QGLView::qglReflectionsEnabled)); + checkSoftShadows->setChecked(view->view()->isFeatureEnabled(QGLView::qglShadowsSoftEnabled)); + groupSSAO->setChecked(view->view()->isFeatureEnabled(QGLView::qglSSAOEnabled)); + spinAccom->setValue(view->view()->feature(QGLView::qglEyeAccomodationTime).toDouble()); + spinAccomMS->setValue(view->view()->feature(QGLView::qglEyeAccomodationMaxSpeed).toDouble()); + checkReflectionsBlur->setChecked(view->view()->isFeatureEnabled(QGLView::qglReflectionsBlur)); + spinShadowmapSize->setValue(view->view()->feature(QGLView::qglShadowsMapSize).toInt()); + spinMotionBlurFactor->setValue(view->view()->feature(QGLView::qglMotionBlurFactor).toDouble()); + spinMotionBlurSteps->setValue(view->view()->feature(QGLView::qglMotionBlurSteps).toInt()); + spinBloomFactor->setValue(view->view()->feature(QGLView::qglBloomFactor).toDouble()); + spinBloomRadius->setValue(view->view()->feature(QGLView::qglBloomRadius).toInt()); + spinBloomThreshold->setValue(view->view()->feature(QGLView::qglBloomThreshold).toDouble()); + spinSSAORadius->setValue(view->view()->feature(QGLView::qglSSAORadius).toInt()); + groupDOF->setChecked(view->view()->isFeatureEnabled(QGLView::qglDepthOfFieldEnabled)); + checkDOFAutoFocus->setChecked(view->view()->isFeatureEnabled(QGLView::qglDepthOfFieldAutoFocusEnabled)); + spinDOFFocus->setValue(view->view()->feature(QGLView::qglDepthOfFieldFocus).toDouble()); + spinDOFDiaphragm->setValue(view->view()->feature(QGLView::qglDepthOfFieldDiaphragm).toDouble()); + spinDOFSpeed->setValue(view->view()->feature(QGLView::qglDepthOfFieldAutoFocusSpeed).toDouble()); + + view->view()->start(-1); + startTimer(1000/60); + + connect(view->view(), SIGNAL(keyEvent(Qt::Key, Qt::KeyboardModifiers)), this, SLOT(view_keyEvent(Qt::Key, Qt::KeyboardModifiers))); + //connect(matEditor, SIGNAL(changed()), this, SLOT(materialChanged())); + sceneTree->assignQGLView(view->view()); + matEditor->assignQGLView(view->view()); + objectEditor->assignQGLView(view->view()); + primitiveEditor->assignQGLView(view->view()); + viewEditor->assignQGLView(view->view()); + + session.load(); + + //loadFile("axis.DAE.qgl"); + //matEditor->setMaterial(const_cast(view->view()->scene()->rootObject()->child(0))->material()); + /*Scene * sc = loadScene("truck.obj"); + //view->view()->scene()->addScene(sc); + sc->rootObject()->moveY(-8); + for (int i = 0; i < 7; ++i) { + sc->rootObject()->moveY(2); + view->view()->scene()->addScene(sc); + } + //view->view()->scene()->dump(); + delete sc;*/ +} + + +QGLViewWindow::~QGLViewWindow() { + session.save(); + //delete ps; +} + + +void QGLViewWindow::changeEvent(QEvent * e) { + QMainWindow::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + retranslateUi(this); + return; + } +} + + +void QGLViewWindow::timerEvent(QTimerEvent * ) { + //static double t = 0.; + //((RendererSimple*)(view->view()->renderer()))->mpos = view->view()->mapFromGlobal(QCursor::pos()); + statusBar()->showMessage(QString("FPS: %1").arg(QString::number(view->view()->currentFPS(), 'f', 2))); +} + + +void QGLViewWindow::loadFile(const QString & path, bool import) { + prev_path = path; + QApplication::setOverrideCursor(Qt::WaitCursor); + QFileInfo fi(path); + Scene * s = nullptr; + if (fi.suffix().toLower() == "qgl") s = loadFromQGLFile(path); + else s = loadScene(path); + QApplication::restoreOverrideCursor(); + if (!s) { + QMessageBox::critical(this, "Import", "Can`t load " + path + "!"); + return; + } + s->setName(fi.baseName()); + if (import) view->scene()->addScene(s); + else { + TextureManager::clearSearchPathes(); + view->scene()->assignFrom(s); + view->view()->focusOn(view->scene()->boundingBox()); + } + TextureManager::addSearchPath(fi.absoluteDir().path()); + delete s; +} + + +void QGLViewWindow::on_actionReset_triggered() { + ///view->view()->removeObject(axis, false); + view->view()->scene()->clear(); + ///view->view()->addObject(axis); +} + + +void QGLViewWindow::on_actionImport_triggered() { + QStringList fl = QFileDialog::getOpenFileNames(this, "Select files", prev_path, extensions); + if (fl.isEmpty()) return; + prev_path = fl.back(); + foreach (QString f, fl) + loadFile(f, true); +} + + +void QGLViewWindow::on_actionSave_triggered() { + QString f = QFileDialog::getSaveFileName(this, "Select file", prev_path, "QGLView(*.qgl)"); + if (f.isEmpty()) return; + if (f.right(4).toLower() != ".qgl") + f += ".qgl"; + prev_path = f; + QApplication::setOverrideCursor(Qt::WaitCursor); + saveToQGLFile(f, view->scene()); + QApplication::restoreOverrideCursor(); +} + + +void QGLViewWindow::on_actionSaveSelected_triggered() { + ObjectBase * sel_obj = view->view()->selectedObject(); + if (!sel_obj) return; + QString f = QFileDialog::getSaveFileName(this, "Select file", prev_path, "QGLView(*.qgl)"); + if (f.isEmpty()) return; + if (f.right(4).toLower() != ".qgl") + f += ".qgl"; + prev_path = f; + QApplication::setOverrideCursor(Qt::WaitCursor); + ///saveToQGLFile(f, sel_obj); + QApplication::restoreOverrideCursor(); +} + + +void QGLViewWindow::on_actionOpen_triggered() { + QString f = QFileDialog::getOpenFileName(this, "Select file", prev_path, extensions); + if (f.isEmpty()) return; + prev_path = f; + loadFile(f); +} + + +void QGLViewWindow::view_keyEvent(Qt::Key k, Qt::KeyboardModifiers m) { + //qDebug() << k; + double spd = 0.2; + if (m.testFlag(Qt::ShiftModifier)) + spd = 0.5; + switch (k) { + case Qt::Key_W: view->view()->camera()->moveForward(spd); break; + case Qt::Key_S: view->view()->camera()->moveBackward(spd); break; + case Qt::Key_A: view->view()->camera()->moveLeft(spd); break; + case Qt::Key_D: view->view()->camera()->moveRight(spd); break; + default: break; + } +} + + +void QGLViewWindow::on_actionArrow_triggered(bool on) { + actionArrow ->setChecked(true); + actionMove ->setChecked(false); + actionRotate->setChecked(false); + actionScale ->setChecked(false); + view->view()->setCurrentAction(RendererService::haNoAction); +} + + +void QGLViewWindow::on_actionMove_triggered(bool on) { + actionArrow ->setChecked(false); + actionMove ->setChecked(true); + actionRotate->setChecked(false); + actionScale ->setChecked(false); + view->view()->setCurrentAction(RendererService::haMove); +} + + +void QGLViewWindow::on_actionRotate_triggered(bool on) { + actionArrow ->setChecked(false); + actionMove ->setChecked(false); + actionRotate->setChecked(true); + actionScale ->setChecked(false); + view->view()->setCurrentAction(RendererService::haRotate); +} + + +void QGLViewWindow::on_actionScale_triggered(bool on) { + actionArrow ->setChecked(false); + actionMove ->setChecked(false); + actionRotate->setChecked(false); + actionScale ->setChecked(true); + view->view()->setCurrentAction(RendererService::haScale); +} + + +#include "glcubemap.h" +void QGLViewWindow::on_pushButton_3_clicked() { + QString f = QFileDialog::getOpenFileName(this, "Select file", "", "*"); + if (f.isEmpty()) return; +} diff --git a/qglview_test/qglview_window.h b/qglview_test/qglview_window.h new file mode 100644 index 0000000..304353f --- /dev/null +++ b/qglview_test/qglview_window.h @@ -0,0 +1,119 @@ +/* + QGLViewWindow + 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 . +*/ + +#ifndef QGLVIEWWINDOW_H +#define QGLVIEWWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ui_qglview_window.h" +#include "formats/loader_qgl.h" +#include "formats/loader_assimp.h" +#include "session_manager.h" +//#include "renderer_rt.h" +#include "qglview.h" +#include "ui_qglview_window.h" + + +class QGLViewWindow: public QMainWindow, public Ui::QGLViewWindow +{ + Q_OBJECT +public: + QGLViewWindow(QWidget * parent = 0); + ~QGLViewWindow(); + + void loadFile(const QString & path, bool import = false); + +private: + // Qt`s overloaded + void changeEvent(QEvent * e); + void timerEvent(QTimerEvent * ); + + QString extensions; + QTranslator translator; + QString prev_path; + //GLPrimitiveCube * box; + Material m; + SessionManager session; + bool isChanged; + +private slots: + void on_groupShadows_clicked(bool val) {view->view()->setFeature(QGLView::qglShadowsEnabled, val);} + void on_groupEyeAccomodation_clicked(bool val) {view->view()->setFeature(QGLView::qglEyeAccomodationEnabled, val);} + void on_groupBloom_clicked(bool val) {view->view()->setFeature(QGLView::qglBloomEnabled, val);} + void on_groupMotionBlur_clicked(bool val) {view->view()->setFeature(QGLView::qglMotionBlurEnabled, val);} + void on_groupReflections_clicked(bool val) {view->view()->setFeature(QGLView::qglReflectionsEnabled, val);} + void on_checkSoftShadows_clicked(bool val) {view->view()->setFeature(QGLView::qglShadowsSoftEnabled, val);} + void on_groupSSAO_clicked(bool val) {view->view()->setFeature(QGLView::qglSSAOEnabled, val);} + void on_groupDOF_clicked(bool val) {view->view()->setFeature(QGLView::qglDepthOfFieldEnabled, val);} + void on_checkDOFAutoFocus_clicked(bool val) {view->view()->setFeature(QGLView::qglDepthOfFieldAutoFocusEnabled, val);} + void on_spinDOFFocus_valueChanged(double val) {view->view()->setFeature(QGLView::qglDepthOfFieldFocus, val);} + void on_spinDOFDiaphragm_valueChanged(double val) {view->view()->setFeature(QGLView::qglDepthOfFieldDiaphragm, val);} + void on_spinDOFSpeed_valueChanged(double val) {view->view()->setFeature(QGLView::qglDepthOfFieldAutoFocusSpeed, val);} + void on_spinAccom_valueChanged(double val) {view->view()->setFeature(QGLView::qglEyeAccomodationTime, val);} + void on_spinAccomMS_valueChanged(double val) {view->view()->setFeature(QGLView::qglEyeAccomodationMaxSpeed, val);} + void on_checkReflectionsBlur_clicked(bool val) {view->view()->setFeature(QGLView::qglReflectionsBlur, val);} + void on_spinShadowmapSize_valueChanged(double val) {view->view()->setFeature(QGLView::qglShadowsMapSize, val);} + void on_spinMotionBlurFactor_valueChanged(double val) {view->view()->setFeature(QGLView::qglMotionBlurFactor, val);} + void on_spinMotionBlurSteps_valueChanged(int val) {view->view()->setFeature(QGLView::qglMotionBlurSteps, val);} + void on_spinBloomFactor_valueChanged(double val) {view->view()->setFeature(QGLView::qglBloomFactor, val);} + void on_spinBloomRadius_valueChanged(int val) {view->view()->setFeature(QGLView::qglBloomRadius, val);} + void on_spinBloomThreshold_valueChanged(double val) {view->view()->setFeature(QGLView::qglBloomThreshold, val);} + void on_spinSSAORadius_valueChanged(int val) {view->view()->setFeature(QGLView::qglSSAORadius, val);} + + void on_actionExit_triggered() {close();} + void on_actionReset_triggered(); + void on_actionImport_triggered(); + void on_actionSave_triggered(); + void on_actionSaveSelected_triggered(); + void on_actionOpen_triggered(); + + void view_keyEvent(Qt::Key k, Qt::KeyboardModifiers m); + + void on_pushButton_2_clicked() {view->view()->reloadShaders();} + + void on_actionArrow_triggered(bool on); + void on_actionMove_triggered(bool on); + void on_actionRotate_triggered(bool on); + void on_actionScale_triggered(bool on); + + void on_colorFogBack_colorChanged(const QColor & v) {view->view()->setFogColor(v);} + void on_spinFogDensity_valueChanged(double v) {view->view()->setFogDensity(v);} + void on_spinFogDecay_valueChanged(double v) {view->view()->setFogDecay(v);} + + void on_pushButton_3_clicked(); + +public slots: + +signals: + +private: + QMatrix4x4 cam_mat; + +}; + +#endif // QGLVIEWWINDOW_H diff --git a/qglview_test/qglview_window.ui b/qglview_test/qglview_window.ui new file mode 100644 index 0000000..a9bde08 --- /dev/null +++ b/qglview_test/qglview_window.ui @@ -0,0 +1,978 @@ + + + QGLViewWindow + + + + 0 + 0 + 1107 + 878 + + + + QGLView converter + + + + + + + Qt::Horizontal + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + View + + + + + + 0 + + + + Common + + + + + + + + + + Features + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 934 + 737 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Shadows + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Shadowmap size + + + + + + + 16.000000000000000 + + + 2048.000000000000000 + + + 512.000000000000000 + + + 0 + + + 16.000000000000000 + + + 512.000000000000000 + + + true + + + + + + + Soft + + + true + + + + + + + + + + Bloom + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Factror + + + + + + + 0.000000000000000 + + + 100.000000000000000 + + + 1.000000000000000 + + + 1 + + + 0.100000000000000 + + + 1.000000000000000 + + + false + + + + + + + 1.000000000000000 + + + 128.000000000000000 + + + 8.000000000000000 + + + 0 + + + 1.000000000000000 + + + 4.000000000000000 + + + false + + + + + + + Radius + + + + + + + Threshold + + + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.900000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + false + + + + + + + + + + Motion blur + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Factror + + + + + + + Steps + + + + + + + 0.000000000000000 + + + 10000.000000000000000 + + + 1.000000000000000 + + + 1 + + + 0.100000000000000 + + + 1.000000000000000 + + + false + + + + + + + 1.000000000000000 + + + 128.000000000000000 + + + 8.000000000000000 + + + 0 + + + 1.000000000000000 + + + 4.000000000000000 + + + false + + + + + + + + + + Eye accomodation + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Time + + + + + + + Max speed + + + + + + + 0.000000000000000 + + + 256.000000000000000 + + + 32.000000000000000 + + + true + + + + + + + 0.010000000000000 + + + 1.000000000000000 + + + 0.100000000000000 + + + 3 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + + + + Depth of field + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Diaphragm + + + + + + + Max speed + + + + + + + 0.100000000000000 + + + 1024.000000000000000 + + + 8.000000000000000 + + + true + + + + + + + 0.010000000000000 + + + 10.000000000000000 + + + 0.100000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Focus + + + + + + + 0.100000000000000 + + + 1000.000000000000000 + + + 1.000000000000000 + + + true + + + 999999.000000000000000 + + + + + + + Auto focus + + + + + + + + + + Reflections + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Blur + + + + + + + + + + SSAO + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Time + + + + + + + 0.000000000000000 + + + 16.000000000000000 + + + 5.000000000000000 + + + 0 + + + 1.000000000000000 + + + 4.000000000000000 + + + false + + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + + + + + + + + reload shaders + + + + + + + load HDR + + + + + + + Qt::Vertical + + + + 20 + 107 + + + + + + + + + Object + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + Material + + + + + + + + + + Primitives + + + + + + + + + + + + + + + 0 + 0 + + + + + + Scene + + + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + + + + 0 + 0 + 1107 + 24 + + + + + File + + + + + + + + + + + + + Scene + + + + + Mode + + + + + + + + + + + + + + toolBar_2 + + + TopToolBarArea + + + false + + + + + + :/icons/application-exit.png:/icons/application-exit.png + + + Exit + + + + + + :/icons/document-import.png:/icons/document-import.png + + + Import ... + + + Ctrl+I + + + + + + :/icons/document-open.png:/icons/document-open.png + + + Open ... + + + Ctrl+O + + + + + + :/icons/document-save-all.png:/icons/document-save-all.png + + + Save ... + + + Ctrl+S + + + + + + :/icons/document-new.png:/icons/document-new.png + + + Reset + + + Ctrl+N + + + + + + :/icons/document-save.png:/icons/document-save.png + + + Save selected ... + + + Save selected + + + Ctrl+Shift+S + + + + + true + + + true + + + Arrow + + + Ctrl+1 + + + + + true + + + + :/icons/transform-move.png:/icons/transform-move.png + + + Move + + + Ctrl+2 + + + + + true + + + + :/icons/transform-rotate.png:/icons/transform-rotate.png + + + Rotate + + + Ctrl+3 + + + + + true + + + + :/icons/transform-scale.png:/icons/transform-scale.png + + + Scale + + + Ctrl+4 + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + GLWidget + QWidget +
glwidget.h
+ 1 +
+ + ObjectEditor + QWidget +
widgets/object_editor.h
+ 1 +
+ + SceneTree + QWidget +
scene_tree.h
+ 1 +
+ + MaterialsEditor + QWidget +
widgets/materials_editor.h
+ 1 +
+ + PrimitiveEditor + QWidget +
primitiveeditor.h
+ 1 +
+ + ViewEditor + QWidget +
view_editor.h
+ 1 +
+
+ + + + + + +
diff --git a/renderer.cpp b/renderer.cpp new file mode 100644 index 0000000..7aaec3c --- /dev/null +++ b/renderer.cpp @@ -0,0 +1,382 @@ +/* + QGL Renderer + 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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "renderer.h" +#include "qglview.h" +#include "glmesh.h" +#include "gltexture_manager.h" +#include + +using namespace QGLEngineShaders; + + +Renderer::Renderer(QGLView * view_): RendererBase(view_), + fbo_ds (view_, QVector() << GL_RGBA16F << GL_RGBA32F << GL_RGBA16F << GL_RGBA16F << GL_RGBA16F), + fbo_out (view_, obrBuffersCount, false, GL_RGBA16F), + rend_mat(this), rend_service(this), rend_selection(this), tone_proc(this), tex_env(view_, 512) { + quad = Primitive::plane(2., 2.); + cam_light = new Light(); + cam_light->intensity = 0.75; + cam_light->setName("Camera_Light"); + + shader_files[srSelectionFill ] = "selection.glsl"; + shader_files[srSelectionHalo ] = "selection_halo.glsl"; + shader_files[srSelectionApply] = "selection_apply.glsl"; + shader_files[srSelectionFrame] = "selection_frame.glsl"; + + shader_files[srServiceFill ] = "service_fill.glsl"; + shader_files[srServiceFrame] = "service_frame.glsl"; + shader_files[srServiceLine ] = "service_line.glsl"; + + shader_files[srGeometryPass ] = "ds_geom.glsl"; + shader_files[srLightOmniPass] = "ds_light.glsl"; + shader_files[srLightSpotPass] = "ds_light.glsl"; shader_defines[srLightSpotPass] << "SPOT"; + shader_files[srFinalPass ] = "ds_final.glsl"; + shader_files[srTonemapPass ] = "ds_tonemap.glsl"; + + /*shaders << ShaderPair("FXAA", &shader_fxaa) + << ShaderPair("dsl_pass_0", &shader_ds_0) + << ShaderPair("dsl_pass_1", &shader_ds_1) + << ShaderPair("hdr", &shader_hdr) + << ShaderPair("downscale", &shader_small) + << ShaderPair("bloom_pass_0", &shader_bloom_0) + << ShaderPair("bloom_pass_1", &shader_bloom_1) + << ShaderPair("fbo_add", &shader_fbo_add) + << ShaderPair("motion_blur", &shader_motion_blur) + << ShaderPair("shadow", &shader_shadow) + << ShaderPair("ssr", &shader_ssr) + << ShaderPair("ssr_blur", &shader_ssr_blur) + << ShaderPair("ssr_merge", &shader_ssr_merge) + << ShaderPair("ssao_blur", &shader_ssao_blur) + << ShaderPair("ssao_merge", &shader_ssao_merge) + << ShaderPair("dof", &shader_dof);*/ + shader_fxaa = 0; + gamma_ = 1.; + edit_mode = need_init_shaders = true; + camera_light_mode = QGLView::clmAuto; +} + + +Renderer::~Renderer() { + delete quad; + delete cam_light; + qDeleteAll(shaders.values()); + if (shader_fxaa) delete shader_fxaa; +} + + +void Renderer::init(int width, int height) { + resize(width, height); + rend_mat.init(width, height); + rend_service.init(width, height); + rend_selection.init(width, height); + tone_proc.init(); + initQuad(quad); + initTextureArrays(); + initCoeffTextures(); + tex_env.init(); + need_init_shaders = true; +} + + +void Renderer::resize(int width, int height) { + rend_mat.resize(width, height); + rend_service.resize(width, height); + rend_selection.resize(width, height); + fbo_ds .resize(width, height); + fbo_out .resize(width, height); + tone_proc.resize(); +} + + +void Renderer::reloadShaders() { + QMapIterator it(shader_files); + QString dir = ":shaders/"; + while (it.hasNext()) { + it.next(); + loadShadersMulti(shaders[it.key()], dir + it.value(), true, shader_defines.value(it.key())); + } + loadShadersMulti(tone_proc.shader_sum, dir + "sum.glsl", false); + QStringList fxaa_defs; + fxaa_defs << "FXAA_PC 1" << "FXAA_GLSL_130 1" << "FXAA_QUALITY__PRESET 15"; + loadShaders(shader_fxaa, QStringList() << (dir + "fxaa.vert") << (dir + "fxaa.frag"), true, fxaa_defs); + need_init_shaders = true; + view->scene()->setLightsChanged(); + view->scene()->setTreeStructChanged(); + view->scene()->setMaterialsChanged(); +} + + +bool Renderer::bindShader(Renderer::ShaderRole role, QOpenGLShaderProgram ** ret) { + QOpenGLShaderProgram * prog = shaders.value(role); + if (ret) *ret = prog; + if (!prog) return false; + if (!prog->isLinked()) return false; + prog->bind(); + return true; +} + + +bool Renderer::bindShader(QOpenGLShaderProgram * sp) { + if (!sp) return true; + if (!sp->isLinked()) return true; + if (!sp->bind()) return false; + return true; +} + + +void Renderer::initShaders() { + if (!need_init_shaders) return; + need_init_shaders = false; + initUniformBuffer(shaders.value(srGeometryPass ), &buffer_materials , bpMaterials , "QGLMaterialData" ); + initUniformBuffer(shaders.value(srLightOmniPass), &buffer_materials , bpMaterials , "QGLMaterialData" ); + initUniformBuffer(shaders.value(srLightOmniPass), &buffer_lights , bpLightParameters, "QGLLightParameterData"); + initUniformBuffer(shaders.value(srLightOmniPass), &buffer_lights_pos, bpLightPositions , "QGLLightPositionData" ); + initUniformBuffer(shaders.value(srLightSpotPass), &buffer_materials , bpMaterials , "QGLMaterialData" ); + initUniformBuffer(shaders.value(srLightSpotPass), &buffer_lights , bpLightParameters, "QGLLightParameterData"); + initUniformBuffer(shaders.value(srLightSpotPass), &buffer_lights_pos, bpLightPositions , "QGLLightPositionData" ); + ShaderRole roles[] = {srLightOmniPass, srLightSpotPass}; + QOpenGLShaderProgram * prog = 0; + for (int p = 0; p < 2; ++p) { + if (!bindShader(roles[p], &prog)) continue; + for (int i = 0; i < 5; ++i) + prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i); + prog->setUniformValue("tex_coeffs[0]", (int)Renderer::dbrBuffersCount); + //prog->setUniformValue("tex_coeffs[1]", (int)Renderer::dbrBuffersCount+1); + prog->setUniformValue("tex_env", (int)Renderer::dbrBuffersCount+1); + } + if (bindShader(srFinalPass, &prog)) { + prog->setUniformValue("tex_g1" , 0); + prog->setUniformValue("tex_s_0", 1); + prog->setUniformValue("tex_s_1", 2); + prog->setUniformValue("tex_t_0", 3); + prog->setUniformValue("tex_t_1", 4); + } + if (bindShader(srGeometryPass, &prog)) { + setUniformMaps(prog); + } + if (bindShader(srTonemapPass, &prog)) { + prog->setUniformValue("tex_0", 0); + prog->setUniformValue("tex_sum", 1); + } +} + + +void Renderer::releaseShader() { + view->glUseProgram(0); +} + + +void Renderer::fillObjectsBuffer(const ObjectBaseList & ol, RenderPass pass) { + cur_objects_.resize(ol.size()); + for (int i = 0; i < ol.size(); ++i) { + Object & so(cur_objects_[i]); + ObjectBase * o = ol[i]; + if (o->material()) { + so.material = o->material()->_index; + so.color = QVector4D(1,1,1,1); + } else { + so.material = 0; + so.color = QColor2QVector(o->color_); + } + so.object_id = o->id_; + o->worldTransform().transposed().copyDataTo(so.modelmatrix); + //qDebug() << "load obj" << o->name() << o->worldTransform(); + } + //qDebug() << "fillObjectsBuffer" << ol.size(); + +} + + +void Renderer::renderObjects(Scene & scene, RenderPass pass) { + QOpenGLExtraFunctions * f = view; + QMapIterator it(scene.geometries_used[pass]); + bool emit_pos_change = false; + while (it.hasNext()) { + it.next(); + Mesh * mesh = it.key(); + if (mesh->isObjectsChanged(pass)) { + mesh->setObjectsChanged(pass, false); + emit_pos_change = true; + fillObjectsBuffer(it.value(), pass); + //qDebug() << "loadObjects" << pass << cur_objects_.size(); + mesh->loadObjects(f, cur_objects_, pass); + } + if (mesh->isSelectionChanged(pass) && edit_mode) { + mesh->setSelectionChanged(pass, false); + fillSelectionsBuffer(rend_selection.cur_selections_, it.value()); + //qDebug() << "fillSelectionsBuffer" << pass << rend_selection.cur_selections_.size(); + mesh->loadSelections(f, rend_selection.cur_selections_, pass); + } + //qDebug() << "draw" << pass << it.value().size(); + mesh->draw(f, it.value().size(), pass); + } + if (emit_pos_change) emit view->objectsPositionChanged(); +} + + +void Renderer::renderLight(int first_wr_buff, bool clear_only) { + QOpenGLShaderProgram * prog = 0; + Camera * cam = view->camera(); + for (int i = 0; i < 2; ++i) { + view->glActiveTexture(GL_TEXTURE0 + Renderer::dbrBuffersCount + i); + view->glBindTexture(GL_TEXTURE_2D, tex_coeff[i]); + } + fbo_ds.bindColorTextures(); + fbo_out.bind(); + tex_env.bind((int)Renderer::dbrBuffersCount+1); + //int ri = 1, wi = 0; + typedef QPair PassPair; + QVector passes; + passes << PassPair(srLightOmniPass, Light::Omni) << PassPair(srLightSpotPass, Light::Cone); + QColor back = view->fogColor_; + back.setAlpha(0); + foreach (PassPair pass, passes) { + if (bindShader(pass.first, &prog)) { + fbo_out.setWriteBuffer(first_wr_buff + pass.second); + glClearFramebuffer(back, false); + if (clear_only) continue; + setUniformCamera(prog, cam); + setUniformViewCorners(prog, cam); + prog->setUniformValue("lights_start", lights_start[pass.second]); + prog->setUniformValue("lights_count", cur_lights[pass.second].size()); + prog->setUniformValue("fog_color", view->fogColor_); + prog->setUniformValue("fog_decay", qMax(view->fogDecay_, 0.001f)); + prog->setUniformValue("fog_density", view->fogDensity_); + prog->setUniformValue("view_mat", cam->viewMatrix().inverted().toGenericMatrix<3,3>()); + renderQuad(prog, quad, cam); + } + } +} + + +void Renderer::renderScene() { + initShaders(); + tex_env.load(); + QOpenGLExtraFunctions * f = view; + Scene & scene(*(view->scene_)); + Camera * cam = view->camera(); + QOpenGLShaderProgram * prog = 0; + bool scene_changed = scene.prepare(); + scene.destroyUnused(f); + + /// reload materials on change + if (scene_changed || scene.need_reload_materials) { + //if (scene.need_reload_materials) + // maps_hash = 0; + rend_selection.generateObjectsID(scene); + reloadMaterials(scene); + if (edit_mode) + recreateMaterialThumbnails(); + emit view->materialsChanged(); + } + + /// material thumbnails + if (edit_mode && !scene_changed) { + rend_mat.procQueue(); + } + + /// lights + cur_lights = scene.lights_used; + bool use_camlight = (camera_light_mode == QGLView::clmOn); + if ((camera_light_mode == QGLView::clmAuto) && cur_lights.isEmpty()) + use_camlight = true; + if (use_camlight) { + cur_lights[Light::Omni] << cam_light; + cam_light->setPos(cam->pos()); + } + if (scene.lights_changed) { + scene.lights_changed = false; + reloadLightsParameters(cur_lights); + } + reloadLightsPositions(cam); + + /// selection + if (edit_mode) { + rend_selection.renderSelection(scene); + } + + /// solid geometry pass + fbo_ds.bind(); + glEnableDepth(); + glClearFramebuffer(); + if (bindShader(srGeometryPass, &prog)) { + setUniformCamera(prog, cam); + textures_empty.bind(f, tarEmpty); + textures_maps .bind(f, tarMaps ); + renderObjects(scene, rpSolid); + } + fbo_ds.release(); + + /// lighting passes + renderLight(obrSolidOmni, scene.geometries_used[rpSolid].isEmpty()); + + /// transparent geometry pass + fbo_ds.bind(); + glEnableDepth(); + glClearFramebuffer(Qt::black, false); + if (bindShader(srGeometryPass, &prog)) { + renderObjects(scene, rpTransparent); + } + fbo_ds.release(); + + /// lighting passes + renderLight(obrTransparentOmni, scene.geometries_used[rpTransparent].isEmpty()); + + /// blending layers + if (bindShader(srFinalPass, &prog)) { + //fbo_ds .bindColorTexture(dbrNormalZ , 0); + fbo_out.bindColorTexture(obrSolidOmni , 1); + fbo_out.bindColorTexture(obrSolidSpot , 2); + fbo_out.bindColorTexture(obrTransparentOmni, 3); + fbo_out.bindColorTexture(obrTransparentSpot, 4); + fbo_out.setWriteBuffer(obrSum); + renderQuad(prog, quad); + } + + /// tonemapping + if (tone_proc.process()) + fbo_out.bind(); + if (bindShader(srTonemapPass, &prog)) { + prog->setUniformValue("gamma", gamma_); + prog->setUniformValue("frame_max", tone_proc.frameMax()); + fbo_out.bindColorTexture(obrSum, 0); + fbo_out.setWriteBuffer(obrTonemap); + renderQuad(prog, quad); + } else + fbo_out.blit(obrSum, fbo_out.id(), obrTonemap, fbo_out.rect(), fbo_out.rect()); + fbo_out.release(); + + /// apply hovers and selection frame + if (edit_mode) { + rend_selection.drawSelection(fbo_out, obrTonemap); + rend_service.renderService(); + } else { + fbo_out.blit(obrTonemap, 0, 0, fbo_out.rect(), QRect(QPoint(), view->size())); + } + //fbo_small_16.blit(0, 0, 0, fbo_small_16.rect(), fbo_small_16.rect(), GL_COLOR_BUFFER_BIT, GL_LINEAR); + +} + + +void Renderer::setCameraLightMode(int m) { + camera_light_mode = m; + view->scene()->setLightsChanged(); +} diff --git a/renderer.h b/renderer.h new file mode 100644 index 0000000..d865bbb --- /dev/null +++ b/renderer.h @@ -0,0 +1,136 @@ +/* + QGL Renderer + 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 . +*/ + +#ifndef RENDERER_H +#define RENDERER_H + +#include "renderer_base.h" +#include "renderer_material.h" +#include "renderer_service.h" +#include "renderer_selection.h" +#include "tonemapping_proc.h" +#include "glcubemap.h" +#include + + +class Renderer: public RendererBase { + friend class QGLView; + friend class MouseController; + friend class RendererBase; + friend class RendererMaterial; + friend class RendererService; + friend class RendererSelection; + friend class TonemappingProc; + enum ShaderRole { + // Selection + srSelectionFill, + srSelectionHalo, + srSelectionApply, + srSelectionFrame, + + // Service + srServiceFill, + srServiceFrame, + srServiceLine, + + // Deferred shading + srGeometryPass, + srLightOmniPass, + srLightSpotPass, + srFinalPass, + srTonemapPass, + }; + enum DeferredBufferRole { + dbrDiffuse, + dbrNormalZ, + dbrSpecularReflect, + dbrEmissionRough, + dbrSpeedBitangXY, + + dbrBuffersCount, + }; + enum OutBufferRole { + obrTonemap, + obrSum, + obrSolidOmni, + obrSolidSpot, + obrTransparentOmni, + obrTransparentSpot, + + obrBuffersCount, + }; + +public: + Renderer(QGLView * view_); + virtual ~Renderer(); + + void init(int width, int height); + void resize(int width, int height); + void reloadShaders(); + void renderScene(); + void setCameraLightMode(int m); + int cameraLightMode() const {return camera_light_mode;} + + QImage materialThumbnail(Material * m) {return rend_mat.materialThumbnail(m);} + void recreateMaterialThumbnails(bool force_all = false) {rend_mat.recreateMaterialThumbnails(force_all);} + +protected: + void fillObjectsBuffer(const ObjectBaseList & ol, RenderPass pass); + void reloadObjects(); + void renderObjects(Scene & scene, RenderPass pass); + void renderLight(int first_wr_buff, bool clear_only); + + bool bindShader(ShaderRole role, QOpenGLShaderProgram ** ret = 0); + bool bindShader(QOpenGLShaderProgram * sp); + void initShaders(); + void releaseShader(); + +private: + float gamma_; + int camera_light_mode; + bool edit_mode, need_init_shaders, need_render_sum; + Framebuffer fbo_ds, fbo_out; + /*QOpenGLShaderProgram * shader_fxaa, * shader_ds_0, * shader_ds_1, * shader_hdr, * shader_small; + QOpenGLShaderProgram * shader_bloom_0, * shader_bloom_1, * shader_motion_blur, * shader_fbo_add; + QOpenGLShaderProgram * shader_shadow, * shader_ssr, * shader_ssr_blur, * shader_ssr_merge; + QOpenGLShaderProgram * shader_ssao_blur, * shader_ssao_merge, * shader_dof;*/ + QMap shader_files; + QMap shader_defines; + QMap shaders; + QOpenGLShaderProgram * shader_fxaa; + + RendererMaterial rend_mat; + RendererService rend_service; + RendererSelection rend_selection; + TonemappingProc tone_proc; + + Mesh * quad; + Light * cam_light; + CubeTexture tex_env; + + QPoint mouse_pos; + QRect mouse_rect; + QMatrix4x4 prev_view, prev_proj; + QMatrix3x3 nm; + QVector4D corner_dirs[4]; + QVector hcontent; + QMap> cur_lights; + +}; + +#endif // RENDERER_H diff --git a/renderer_base.cpp b/renderer_base.cpp new file mode 100644 index 0000000..f22c287 --- /dev/null +++ b/renderer_base.cpp @@ -0,0 +1,442 @@ +/* + QGL RendererBase + 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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "renderer_base.h" +#include "renderer.h" +#include "qglview.h" +#include "glmesh.h" +#include "gltexture_manager.h" +#include "glshaders_headers.h" + +using namespace QGLEngineShaders; + + +RendererBase::RendererBase(QGLView * view_): + view(view_), + buffer_materials (GL_UNIFORM_BUFFER, GL_STREAM_DRAW), + buffer_lights (GL_UNIFORM_BUFFER, GL_STREAM_DRAW), + buffer_lights_pos(GL_UNIFORM_BUFFER, GL_STREAM_DRAW), + textures_empty(false), + textures_maps(true) +{ + textures_manager = new TextureManager(view); + maps_size = QSize(512, 512); + maps_hash = 0; + tex_coeff[0] = tex_coeff[1] = 0; + +} + + +RendererBase::~RendererBase() { + delete textures_manager; +} + + +void RendererBase::initTextureArrays() { + QOpenGLExtraFunctions * f = view; + textures_maps.init(f); + textures_empty.init(f); + textures_empty.resize(f, QSize(1, 1), 2); + textures_empty.bind(f); + QImage im(1, 1, QImage::Format_RGBA8888); + im.fill(0xFFFFFFFF); + textures_empty.load(f, im, emrWhite); + im.fill(0xFF8080); + textures_empty.load(f, im, emrBlue); +} + + +void RendererBase::initUniformBuffer(QOpenGLShaderProgram * prog, Buffer * buffer, int bind_point, const char * blockName) { + if (!prog || !buffer) return; + if (!prog->isLinked()) return; + QOpenGLExtraFunctions * f = view; + buffer->init(f); + //glClearError(); + GLint ubo_ind = f->glGetUniformBlockIndex(prog->programId(), blockName); + f->glUniformBlockBinding(prog->programId(), ubo_ind, bind_point); + f->glBindBufferBase(GL_UNIFORM_BUFFER, bind_point, buffer->ID()); + //qDebug() << "initUBO" << QString::number(f->glGetError(), 16); +} + + +void RendererBase::setUniformHalo(QOpenGLShaderProgram * prog, const char * type, QColor color, float fill) { + prog->setUniformValue((QString(type) + "_color").toLatin1().constData(), color); + prog->setUniformValue((QString(type) + "_fill" ).toLatin1().constData(), fill); +} + + +void RendererBase::setUniformMaps(QOpenGLShaderProgram * prog) { + prog->setUniformValue("qgl_texture_array[0]", (int)tarEmpty); + prog->setUniformValue("qgl_texture_array[1]", (int)tarMaps ); +} + + +void RendererBase::setUniformCamera(QOpenGLShaderProgram * prog, Camera * cam, bool matrices, QSize viewport) { + double w = view->width(), h = view->height(); + if (viewport.isValid()) { + w = viewport.width(); + h = viewport.height(); + } + QMatrix4x4 mat_view, mat_proj; + if (cam) { + if (matrices) { + mat_view = cam->fullViewMatrix(); + mat_proj = cam->projectionMatrix(w / h); + } + prog->setUniformValue("z_near", cam->depthStart()); + } + prog->setUniformValue("dt", QVector2D(1. / w, 1. / h)); + prog->setUniformValue("qgl_ViewMatrix" , mat_view); + prog->setUniformValue("qgl_ViewProjMatrix", mat_proj * mat_view); +} + + +void RendererBase::setUniformViewCorners(QOpenGLShaderProgram * prog, Camera * cam, QSize viewport) { + double w = view->width(), h = view->height(); + if (viewport.isValid()) { + w = viewport.width(); + h = viewport.height(); + } + QMatrix4x4 mproji = cam->projectionMatrix(w / h).inverted(); + QMatrix4x4 mviewi = cam->viewMatrix().inverted(); + QVector4D corner_dirs[4], world_dirs[4]; + corner_dirs[0] = (mproji * QVector4D(-1, -1, 0, 1)); + corner_dirs[1] = (mproji * QVector4D(-1, 1, 0, 1)); + corner_dirs[2] = (mproji * QVector4D( 1, 1, 0, 1)); + corner_dirs[3] = (mproji * QVector4D( 1, -1, 0, 1)); + for (int i = 0; i < 4; ++i) { + world_dirs[i] = mviewi.mapVector(corner_dirs[i].toVector3D()); + prog->setUniformValue(QString("view_corners[%1]").arg(i).toLatin1().constData(), corner_dirs[i]); + prog->setUniformValue(QString("world_corners[%1]").arg(i).toLatin1().constData(), world_dirs[i]); + } +} + + +void RendererBase::fillSelectionsBuffer(QVector & buffer, const ObjectBaseList & ol) { + buffer.resize(ol.size()); + for (int i = 0; i < ol.size(); ++i) { + buffer[i] = (ol[i]->isSelected(true) ? 1 : 0); + } +} + + +void RendererBase::fillSelectionsBuffer(QVector & buffer, bool yes, int size) { + buffer.resize(size); + for (int i = 0; i < size; ++i) + buffer[i] = (yes ? 1 : 0); +} + + +void RendererBase::reloadMaterials(Scene & scene) { + //qDebug() << "reloadMaterias"; + QList maps[2]; + QMap tex_layers[2]; + foreach (Material * m, scene.materials) { + if (m->map_diffuse .hasBitmap()) maps[0] << &(m->map_diffuse ); + if (m->map_normal .hasBitmap()) maps[1] << &(m->map_normal ); + if (m->map_metalness.hasBitmap()) maps[0] << &(m->map_metalness); + if (m->map_roughness.hasBitmap()) maps[0] << &(m->map_roughness); + if (m->map_emission .hasBitmap()) maps[0] << &(m->map_emission ); + if (m->map_relief .hasBitmap()) maps[0] << &(m->map_relief ); + } + for (int i = 0; i < 2; ++i) { + foreach (Map * m, maps[i]) + tex_layers[i][m->bitmap_path] = 0; + } + int layers_count = tex_layers[0].size() + tex_layers[1].size(), cl = -1; + uint cur_maps_hash = qHash(tex_layers[0].keys()) ^ (qHash(tex_layers[1].keys()) + 0xF00FF00F); + if (maps_hash != cur_maps_hash) { + maps_hash = cur_maps_hash; + textures_maps.resize(view, maps_size, layers_count); + textures_maps.bind(view); + for (int i = 0; i < 2; ++i) { + QMutableMapIterator it(tex_layers[i]); + while (it.hasNext()) { + it.next(); + QImage im = textures_manager->loadTextureImage(it.key(), i == 1); + textures_maps.load(view, im, ++cl); + it.value() = cl; + } + foreach (Map * m, maps[i]) { + m->_layer = tex_layers[i].value(m->bitmap_path); + //qDebug() << "assign" << m->bitmap_path << "layer" << m->_layer; + } + } + textures_maps.mipmaps(view); +// qDebug() << "load" << (cl+1) << "bitmaps"; + } + + QGLMaterial glm; + cur_materials_.clear(); + cur_materials_ << glm; + foreach (Material * m, scene.materials) { + if (cur_materials_.size() >= max_materials) { + qDebug() << "[QGLEngine] Warning: Too many materials! Maximum" << max_materials; + break; + } + //m->load(textures_manager); + m->_index = cur_materials_.size(); + m->_changed = false; + glm.color_diffuse = QColor2QVector(m->color_diffuse ); + glm.color_emission = QColor2QVector(m->color_emission); + glm.transparency = m->transparency; + glm.reflectivity = m->reflectivity; + glm.iof = m->iof ; + glm.dispersion = m->dispersion ; + m->map_diffuse .copyToQGLMap(glm.map[mtDiffuse ]); + m->map_normal .copyToQGLMap(glm.map[mtNormal ]); + m->map_metalness.copyToQGLMap(glm.map[mtMetalness ]); + m->map_roughness.copyToQGLMap(glm.map[mtRoughness]); + m->map_emission .copyToQGLMap(glm.map[mtEmission ]); + m->map_relief .copyToQGLMap(glm.map[mtRelief ]); + cur_materials_ << glm; + } + //qDebug() << "load" << cur_materials_.size() << "materials"; + //textures_maps.resize(maps_size, ); + //cur_materials_[0].color_diffuse = QColor2QVector(Qt::red); + buffer_materials.bind(view); + buffer_materials.resize(view, cur_materials_.size() * sizeof(QGLMaterial)); + buffer_materials.load(view, cur_materials_.constData(), cur_materials_.size() * sizeof(QGLMaterial)); + scene.need_reload_materials = false; + +} + + +void RendererBase::reloadLightsParameters(const QMap> & lights) { + lights_start.clear(); + lights_start[Light::Omni] = 0; + QMapIterator> it(lights); + current_lights.clear(); + while (it.hasNext()) { + it.next(); + lights_start[it.key()] = current_lights.size(); + current_lights.append(it.value()); + } + cur_lights_params_.resize(qMin(current_lights.size(), max_lights)); + //qDebug() << "reloadLightsParameters" << cur_lights_params_.size(); + for (int i = 0; i < cur_lights_params_.size(); ++i) { + QGLLightParameter & so(cur_lights_params_[i]); + Light * l = current_lights[i]; + double ang_start = l->angle_start / 2.f, ang_end = l->angle_end / 2.f; + if (l->light_type == Light::Omni) + ang_start = ang_end = 180.; + //qDebug() << "light" << light->name() << ulightn << pos; + so.color = QColor2QVector(l->color_); + so.angles[0] = ang_start; + so.angles[1] = cos(ang_start * deg2rad); + so.angles[2] = ang_end; + so.angles[3] = cos(ang_end * deg2rad); + so.decay_intensity[0] = l->decay_const; + so.decay_intensity[1] = l->decay_linear; + so.decay_intensity[2] = l->decay_quadratic; + so.decay_intensity[3] = l->intensity; + //so.shadow = shadow; + //so.shadowColor = shadow; + } + buffer_lights.bind(view); + buffer_lights.resize(view, cur_lights_params_.size() * sizeof(QGLLightParameter)); + buffer_lights.load(view, cur_lights_params_.constData(), cur_lights_params_.size() * sizeof(QGLLightParameter)); +} + + +void RendererBase::reloadLightsPositions(Camera * cam) { + cur_lights_pos_.resize(qMin(current_lights.size(), max_lights)); + QMatrix4x4 mat = cam->viewMatrix() * cam->offsetMatrix(); + for (int i = 0; i < cur_lights_pos_.size(); ++i) { + QGLLightPosition & so(cur_lights_pos_[i]); + Light * l = current_lights[i]; + QMatrix4x4 m = mat * l->worldTransform(); + QVector4D pos(0, 0, 0, 1.), dir(QVector3D(0,0,-1), 1);//, dir0(light->dir0), dir1(light->dir1); + pos = m * pos; + dir = (m * QVector4D(QVector3D(0,0,-1),0)).normalized();//((m * dir) - pos).normalized(); + so.position = pos; + so.direction = dir; + //so.shadowMatrix = l->shadow_matrix; + } + buffer_lights_pos.bind(view); + buffer_lights_pos.resize(view, cur_lights_pos_.size() * sizeof(QGLLightPosition)); + buffer_lights_pos.load(view, cur_lights_pos_.constData(), cur_lights_pos_.size() * sizeof(QGLLightPosition)); +} + + +void RendererBase::markReloadTextures() { + maps_hash = 0; + textures_manager->clearImageCache(); + view->scene_->need_reload_materials = true; +} + + +void RendererBase::setMapsSize(QSize sz) { + maps_size = sz; + markReloadTextures(); +} + + +void RendererBase::initQuad(Mesh * mesh, QMatrix4x4 mat) { + QGLEngineShaders::Object quab_object; + mat.transposed().copyDataTo(quab_object.modelmatrix); + mesh->init(view); + mesh->loadObject(view, quab_object); +} + + +void RendererBase::renderQuad(QOpenGLShaderProgram * prog, Mesh * mesh, Camera * cam, bool uniforms) { + glDisableDepth(); + if (uniforms) + setUniformCamera(prog, cam, false); + mesh->draw(view, 1); +} + + + + +float RadicalInverse_VdC(uint bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} +// ---------------------------------------------------------------------------- +QVector2D Hammersley(uint i, uint N) { + return QVector2D(float(i)/float(N), RadicalInverse_VdC(i)); +} +QVector3D ImportanceSampleGGX(QVector2D Xi, QVector3D N, float roughness) { + float a = roughness*roughness; + float phi = 2.0 * M_PI * Xi[0]; + float cosTheta = sqrt((1.0 - Xi[1]) / (1.0 + (a*a - 1.0) * Xi[1])); + float sinTheta = sqrt(1.0 - cosTheta*cosTheta); + // преобразование из сферических в декартовы координаты + QVector3D H; + H[0] = cos(phi) * sinTheta; + H[1] = sin(phi) * sinTheta; + H[2] = cosTheta; + // преобразование из касательного пространства в мировые координаты + QVector3D up = qAbs(N[2]) < 0.999 ? QVector3D(0.0, 0.0, 1.0) : QVector3D(1.0, 0.0, 0.0); + QVector3D tangent = QVector3D::crossProduct(up, N).normalized(); + QVector3D bitangent = QVector3D::crossProduct(N, tangent); + QVector3D sampleVec = tangent * H[0] + bitangent * H[1] + N * H[2]; + return sampleVec.normalized(); +} +float GeometrySchlickGGX(float NdotV, float roughness) { + float k = (roughness * roughness) / 2.0; + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + return nom / denom; +} +float GeometrySmith(QVector3D N, QVector3D V, QVector3D L, float roughness) { + float NdotV = piMax(QVector3D::dotProduct(N, V), 0.f); + float NdotL = piMax(QVector3D::dotProduct(N, L), 0.f); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + return ggx1 * ggx2; +} +QVector2D IntegrateBRDF(float NdotV, float roughness) { + QVector3D V; + V[0] = sqrt(1.f - NdotV*NdotV); + V[1] = 0.f; + V[2] = NdotV; + float A = 0.f; + float B = 0.f; + QVector3D N = QVector3D(0.f, 0.f, 1.f); + const uint SAMPLE_COUNT = 256u; + for(uint i = 0u; i < SAMPLE_COUNT; ++i) { + QVector2D Xi = Hammersley(i, SAMPLE_COUNT); + QVector3D H = ImportanceSampleGGX(Xi, N, roughness); + QVector3D L = (2.f * QVector3D::dotProduct(V, H) * H - V).normalized(); + float NdotL = piMax(L[2], 0.f); + float NdotH = piMax(H[2], 0.f); + float VdotH = piMax(QVector3D::dotProduct(V, H), 0.f); + if(NdotL > 0.f) { + float G = GeometrySmith(N, V, L, roughness); + float G_Vis = (G * VdotH) / (NdotH * NdotV); + float Fc = pow(1.f - VdotH, 5.f); + A += (1.f - Fc) * G_Vis; + B += Fc * G_Vis; + } + } + A /= float(SAMPLE_COUNT); + B /= float(SAMPLE_COUNT); + return QVector2D(A, B); +} + +void RendererBase::initCoeffTextures() { + QImage im = QImage(":/coeffs_brdf.png").mirrored(); + int size = im.width(); + QVector data(size*size); + int ind = -1; + for (int x = 0; x < size; ++x) { + //float c = x / double(size - 1) + 1.E-3; + for (int y = 0; y < size; ++y) { + //float r = y / double(size - 1); + QColor p = im.pixelColor(x, y); + data[++ind] = QVector2D(p.redF(), p.greenF());//IntegrateBRDF(c, 1.f - r + 1.E-3); + } + } + createCoeffTexture(tex_coeff[0], data.constData(), size, 2); + /*const int size = 512; + QVector data_diff(size*size), data_spec(size*size); + double r, c, c2; + int ind = -1; + for (int x = 0; x < size; ++x) { + c = x / double(size - 1); + c = sqrt(c); + c = piMax(1.E-16, c); + c2 = c*c; + for (int y = 0; y < size; ++y) { + r = y / double(size - 1); + double r_d = r; + double r_s = r*r*r; + r_d = piMax(1.E-16, r_d); + r_s = piMax(1.E-16, r_s); + + double ndlc = (1. - c2) / c2; + double diff = 2. / (1. + sqrt(1. + (1. - r_d) * ndlc)); + + double spec = c2 * (r_s + ndlc); + spec = r_s / (spec * spec) / M_PI; + + ++ind; + data_diff[ind] = piClamp(diff, 1.E-12, 1.E+36); + data_spec[ind] = piClamp(spec, 1.E-12, 1.E+36); + } + } + createCoeffTexture(tex_coeff[0], data_diff, size); + createCoeffTexture(tex_coeff[1], data_spec, size);*/ +} + + +void RendererBase::createCoeffTexture(GLuint & id, const void * data, int size, int channels) { + QOpenGLExtraFunctions * f = view; + deleteGLTexture(f, id); + f->glGenTextures(1, &id); + f->glBindTexture(GL_TEXTURE_2D, id); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + GLenum iformat = GL_R16F, format = GL_RED; + if (channels == 2) {iformat = GL_RG16F; format = GL_RG;} + if (channels == 3) {iformat = GL_RGB16F; format = GL_RGB;} + if (channels == 4) {iformat = GL_RGBA16F; format = GL_RGBA;} + f->glTexImage2D(GL_TEXTURE_2D, 0, iformat, size, size, 0, format, GL_FLOAT, data); +} diff --git a/renderer_base.h b/renderer_base.h new file mode 100644 index 0000000..e0f5574 --- /dev/null +++ b/renderer_base.h @@ -0,0 +1,68 @@ +/* + QGL RendererBase + 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 . +*/ + +#ifndef RENDERER_BASE_H +#define RENDERER_BASE_H + +#include "glshaders_types.h" +#include "gltexturearray.h" +#include "glbuffer.h" + + +class RendererBase { +public: + RendererBase(QGLView * view_); + ~RendererBase(); + +protected: + void initTextureArrays(); + void initUniformBuffer (QOpenGLShaderProgram * prog, Buffer * buffer, int bind_point, const char * blockName); + void setUniformHalo (QOpenGLShaderProgram * prog, const char * type, QColor color, float fill); + void setUniformMaps (QOpenGLShaderProgram * prog); + void setUniformCamera (QOpenGLShaderProgram * prog, Camera * cam, bool matrices = true, QSize viewport = QSize()); + void setUniformViewCorners(QOpenGLShaderProgram * prog, Camera * cam, QSize viewport = QSize()); + void fillSelectionsBuffer(QVector & buffer, const ObjectBaseList & ol); + void fillSelectionsBuffer(QVector & buffer, bool yes, int size); + void reloadMaterials(Scene & scene); + void reloadLightsParameters(const QMap> & lights); + void reloadLightsPositions (Camera * cam); + void markReloadTextures(); + void setMapsSize(QSize sz); + void initQuad(Mesh * mesh, QMatrix4x4 mat = QMatrix4x4()); + void renderQuad(QOpenGLShaderProgram * prog, Mesh * mesh, Camera * cam = 0, bool uniforms = true); + void initCoeffTextures(); + void createCoeffTexture(GLuint & id, const void * data, int size, int channels = 1); + + QGLView * view; + TextureManager * textures_manager; + QVector cur_objects_; + QVector cur_materials_; + QVector cur_lights_params_; + QVector cur_lights_pos_; + Buffer buffer_materials; + Buffer buffer_lights, buffer_lights_pos; + Texture2DArray textures_empty, textures_maps; + QSize maps_size; + uint maps_hash; + GLuint tex_coeff[2]; + QMap lights_start; + QList current_lights; + +}; + +#endif // RENDERER_BASE_H diff --git a/renderer_material.cpp b/renderer_material.cpp new file mode 100644 index 0000000..0e6c594 --- /dev/null +++ b/renderer_material.cpp @@ -0,0 +1,132 @@ +/* + QGL RendererMaterial + 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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "renderer_material.h" +#include "renderer.h" +#include "qglview.h" +#include "glmesh.h" +#include "gltexture_manager.h" +#include + +using namespace QGLEngineShaders; + + +RendererMaterial::RendererMaterial(Renderer * r_): r(r_), + fbo_mat_thumb(r->view, 6, true , GL_RGBA16F) { + mat_sphere = Primitive::ellipsoid(16, 16); + mat_camera = new Camera(); + mat_camera->setPos(QVector3D(2, 2, 2)); + mat_camera->setAim(QVector3D()); + mat_camera->setFOV(45.); + mat_light = new Light(); + mat_light->setPos(QVector3D(50, 100, 25)); + last_thumb_material = 0; +} + + +RendererMaterial::~RendererMaterial() { + delete mat_sphere; + delete mat_camera; + delete mat_light; +} + + +void RendererMaterial::init(int width, int height) { + resize(width, height); +} + + +void RendererMaterial::resize(int width, int height) { + fbo_mat_thumb.enablePixelBuffer(); + fbo_mat_thumb.resize(256, 256); +} + + +void RendererMaterial::renderMaterial(Material * m) { + //qDebug() << "renderMaterial" << m; + last_thumb_material = m; + QOpenGLShaderProgram * prog = 0; + QOpenGLExtraFunctions * f = r->view; + fbo_mat_thumb.bind(); + glEnableDepth(); + glClearFramebuffer(QColor(0,0,0,0)); + if (r->bindShader(Renderer::srGeometryPass, &prog)) { + r->setUniformMaps(prog); + r->setUniformCamera(prog, mat_camera, true, fbo_mat_thumb.size()); + //qDebug() << mat_camera->viewMatrix(); + r->textures_empty.bind(f, tarEmpty); + r->textures_maps .bind(f, tarMaps ); + Object o; + o.material = m->_index; + mat_sphere->loadObject(f, o); + mat_sphere->draw(f, 1); + } + fbo_mat_thumb.bindColorTextures(); + fbo_mat_thumb.bindDepthTexture(5); + fbo_mat_thumb.setWriteBuffer(5); + if (r->bindShader(Renderer::srLightOmniPass, &prog)) { + r->setUniformCamera(prog, mat_camera, true, fbo_mat_thumb.size()); + r->setUniformViewCorners(prog, mat_camera, fbo_mat_thumb.size()); + for (int i = 0; i < 5; ++i) + prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i); + prog->setUniformValue("tex_d", 5); + prog->setUniformValue("lights_start", 0); + prog->setUniformValue("lights_count", 1); + QMap> mat_l; + mat_l[Light::Omni] << mat_light; + r->reloadLightsParameters(mat_l); + r->reloadLightsPositions(mat_camera); + glClearFramebuffer(r->view->backColor(), false); + r->renderQuad(prog, r->quad, mat_camera); + r->view->scene()->setLightsChanged(); + } + fbo_mat_thumb.queryImage(5); + fbo_mat_thumb.release(); +} + + +void RendererMaterial::procQueue() { + if (last_thumb_material) { + mat_thumbnails[last_thumb_material] = fbo_mat_thumb.getImage(); + emit r->view->materialThumbnailCreated(last_thumb_material); + last_thumb_material = 0; + } + if (!mat_thumb_queue.isEmpty()) + renderMaterial(mat_thumb_queue.dequeue()); +} + + +QImage RendererMaterial::materialThumbnail(Material * m) { + return mat_thumbnails.value(m); +} + + +void RendererMaterial::recreateMaterialThumbnails(bool force_all) { + if (force_all) { + mat_thumb_queue.clear(); + //qDebug() << "recreateMaterialThumbnails" << view->scene_->materials; + foreach (Material * m, r->view->scene_->materials) + mat_thumb_queue.enqueue(m); + } else { + foreach (Material * m, r->view->scene_->changed_materials) + if (!mat_thumb_queue.contains(m)) + mat_thumb_queue.enqueue(m); + } +} diff --git a/renderer_material.h b/renderer_material.h new file mode 100644 index 0000000..3c08a7b --- /dev/null +++ b/renderer_material.h @@ -0,0 +1,54 @@ +/* + QGL RendererMaterial + 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 . +*/ + +#ifndef RENDERER_MATERIAL_H +#define RENDERER_MATERIAL_H + +#include "glframebuffer.h" +#include + + +class RendererMaterial { + friend class QGLView; + +public: + RendererMaterial(Renderer * r_); + virtual ~RendererMaterial(); + + void init(int width, int height); + void resize(int width, int height); + + QImage materialThumbnail(Material * m); + void recreateMaterialThumbnails(bool force_all = false); + void renderMaterial(Material * m); + void procQueue(); + +private: + Renderer * r; + + Framebuffer fbo_mat_thumb; + Mesh * mat_sphere; + Camera * mat_camera; + Light * mat_light; + QMap mat_thumbnails; + Material * last_thumb_material; + QQueue mat_thumb_queue; + +}; + +#endif // RENDERER_MATERIAL_H diff --git a/renderer_selection.cpp b/renderer_selection.cpp new file mode 100644 index 0000000..00897a7 --- /dev/null +++ b/renderer_selection.cpp @@ -0,0 +1,218 @@ +/* + QGL RendererSelection + 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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "renderer_selection.h" +#include "qglview.h" +#include "glmesh.h" +#include + +using namespace QGLEngineShaders; + + +RendererSelection::RendererSelection(Renderer * r_): r(r_), + fbo_selection(r->view, 6) { + sel_frame = Primitive::plane(2., 2.); + id_hover = 0; + line_thick_ = 2.; + scale_ = 0.5; +} + + +RendererSelection::~RendererSelection() { + delete sel_frame; +} + + +void RendererSelection::init(int width, int height) { + resize(width, height); +} + + +void RendererSelection::resize(int width, int height) { + line_thick_ = lineThickness() + 1.; + scale_ = 0.5 / appScale(); + fbo_selection.enablePixelBuffer(); + fbo_selection.resize(width * scale_, height * scale_); + //fbo_selection.setColorTextureFiltering(sbrSrcHover , GL_LINEAR); + //fbo_selection.setColorTextureFiltering(sbrSrcSelect, GL_LINEAR); +} + + +void RendererSelection::generateObjectsID(Scene & scene) { + ids.clear(); + aim_ids.clear(); + QList passes = scene.geometries_used.keys(); + foreach (int p, passes) { + QMapIterator it(scene.geometries_used[p]); + while (it.hasNext()) { + it.next(); + foreach (ObjectBase * o, it.value()) { + uint id = qHash(o); + ids[id] = o; + o->id_ = id; + } + } + QMapIterator> lit(scene.lights_used); + while (lit.hasNext()) { + lit.next(); + foreach (ObjectBase * o, lit.value()) { + uint id = qHash(o); + ids[id] = o; + aim_ids[id + 1] = o; + o->id_ = id; + } + } + foreach (Camera * o, scene.cameras_used) { + uint id = qHash(o); + ids[id] = o; + aim_ids[id + 1] = o; + o->id_ = id; + } + } +} + + +void RendererSelection::renderSelection(Scene & scene) { + QOpenGLShaderProgram * prog = 0; + QGLView * view = r->view; + MouseController & mc(view->mouse); + if (r->bindShader(Renderer::srSelectionFill, &prog)) { + mc.hov_objects.clear(); + mc.hov_aims.clear(); + id_hover = 0; + if (fbo_selection.queriedPoints() > 0) { + if (fbo_selection.queriedPoints() == 1) { + id_hover = fbo_selection.getPoint(); + ObjectBase * o = ids.value(id_hover); + if (o) + mc.hov_objects << o; + else { + o = aim_ids.value(id_hover); + if (o) + mc.hov_aims << o; + } + //qDebug() << id_hover; + } else { + QVector points = fbo_selection.getPointsByte(); + QSet ids_hover; + foreach (uint i, points) + ids_hover << i; + foreach (uint i, ids_hover) { + ObjectBase * o = ids.value(i); + if (o) mc.hov_objects << o; + o = aim_ids.value(i); + if (o) mc.hov_aims << o; + } + //qDebug() << ids_hover; + } + } + + fbo_selection.bind(); + + fbo_selection.setWriteBuffers(); + glEnableDepth(); + glClearFramebuffer(QColor(0,0,0,0)); + r->setUniformCamera(prog, view->camera()); + r->renderObjects(scene, rpSolid); + r->renderObjects(scene, rpTransparent); + view->glClear(GL_DEPTH_BUFFER_BIT); + + RendererService & rs(r->rend_service); + rs.drawLights(); + rs.drawCameras(); + rs.drawCurrentHandleObjects(); + + //mouse_rect = fbo_selection.rect(); + if (r->mouse_rect.isNull()) + fbo_selection.queryPoint(0, r->mouse_pos * scale_); + else + fbo_selection.queryPoints(0, QRect(r->mouse_rect.topLeft() * scale_, + r->mouse_rect.size() * scale_)); + + //qDebug() << id_hover; + fbo_selection.bindColorTexture(sbrSrcHover , sbrSrcHover ); + fbo_selection.bindColorTexture(sbrSrcSelect, sbrSrcSelect); + fbo_selection.setWriteBuffers(QVector() << sbrHovered << sbrSelected); + if (!view->hoverHalo_ && !view->selectionHalo_) + glClearFramebuffer(QColor(0,0,0,0), false); + else { + r->bindShader(Renderer::srSelectionHalo, &prog); + r->setUniformHalo(prog, "hover" , view->hoverHaloColor() , view->hoverHaloFillAlpha()); + r->setUniformHalo(prog, "selection", view->selectionHaloColor(), view->selectionHaloFillAlpha()); + prog->setUniformValue("has_hover" , view->hoverHalo_ && (id_hover > 0) ? 1.f : 0.f); + prog->setUniformValue("has_selection", view->selectionHalo_ ? 1.f : 0.f); + prog->setUniformValue("fb_hover" , (int)sbrSrcHover); + prog->setUniformValue("fb_selection", (int)sbrSrcSelect); + prog->setUniformValue("hover_id", QVector4D(float( id_hover & 0xFF) / 255.f, + float((id_hover >> 8 ) & 0xFF) / 255.f, + float((id_hover >> 16) & 0xFF) / 255.f, + float((id_hover >> 24) & 0xFF) / 255.f)); + r->renderQuad(prog, r->quad, view->camera()); + } + + prog = r->shader_fxaa; + if (r->bindShader(prog)) { + r->setUniformCamera(prog, 0, true, fbo_selection.size()); + fbo_selection.bindColorTexture(sbrHovered); + fbo_selection.setWriteBuffer(sbrHoveredFXAA); + r->renderQuad(prog, r->quad, 0, false); + fbo_selection.bindColorTexture(sbrSelected); + fbo_selection.setWriteBuffer(sbrSelectedFXAA); + r->renderQuad(prog, r->quad, 0, false); + } + + fbo_selection.release(); + } +} + + +void RendererSelection::renderSelectionFrame() { + QOpenGLShaderProgram * prog = 0; + if (r->bindShader(Renderer::srSelectionFrame, &prog)) { + QMatrix4x4 mat; + double mrx = r->mouse_rect.x(), mrw = r->mouse_rect.width() , vw = r->view->width(); + double mry = r->mouse_rect.y(), mrh = r->mouse_rect.height(), vh = r->view->height(); + mat.translate(-1. + (mrw + mrx*2) / vw, 1. - (mrh + mry*2) / vh, 0.); + mat.scale(mrw / vw, mrh / vh, 0.); + r->initQuad(sel_frame, mat); + prog->setUniformValue("size", QVector2D(mrw / vw, mrh / vh)); + prog->setUniformValue("thickness", line_thick_); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + r->renderQuad(prog, sel_frame); + glDisable(GL_BLEND); + } +} + + +void RendererSelection::drawSelection(Framebuffer & fbo_out, int index_out) { + QOpenGLShaderProgram * prog = 0; + if (r->bindShader(Renderer::srSelectionApply, &prog)) { + fbo_selection.bindColorTextures(); + fbo_out.bindColorTexture(index_out); + prog->setUniformValue("fb_out" , 0); + prog->setUniformValue("fb_hover" , (int)sbrHoveredFXAA ); + prog->setUniformValue("fb_select", (int)sbrSelectedFXAA); + r->renderQuad(prog, r->quad, r->view->camera()); + if (!r->mouse_rect.isNull()) { + renderSelectionFrame(); + } + } +} diff --git a/renderer_selection.h b/renderer_selection.h new file mode 100644 index 0000000..9062a18 --- /dev/null +++ b/renderer_selection.h @@ -0,0 +1,68 @@ +/* + QGL RendererSelection + 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 . +*/ + +#ifndef RENDERER_SELECTION_H +#define RENDERER_SELECTION_H + +#include "renderer_service.h" +#include + + +class RendererSelection { + friend class QGLView; + friend class MouseController; + friend class Renderer; + friend class RendererService; + +public: + RendererSelection(Renderer * r_); + virtual ~RendererSelection(); + + void init(int width, int height); + void resize(int width, int height); + +protected: + enum SelectionBufferRole { + sbrSrcHover, + sbrSrcSelect, + sbrHovered, + sbrSelected, + sbrHoveredFXAA, + sbrSelectedFXAA, + }; + + void generateObjectsID(Scene & scene); + void renderSelection(Scene & scene); + void renderSelectionFrame(); + void drawSelection(Framebuffer & fbo_out, int index_out = 0); + +private: + Renderer * r; + + Framebuffer fbo_selection; + Mesh * sel_frame; + float line_thick_, scale_; + + QVector cur_selections_; + QHash ids; + QHash aim_ids; + uint id_hover; + +}; + +#endif // RENDERER_selection_H diff --git a/renderer_service.cpp b/renderer_service.cpp new file mode 100644 index 0000000..e71a183 --- /dev/null +++ b/renderer_service.cpp @@ -0,0 +1,490 @@ +/* + QGL RendererService + 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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "renderer_service.h" +#include "renderer.h" +#include "qglview.h" +#include "glmesh.h" +#include + +using namespace QGLEngineShaders; + + +RendererService::RendererService(Renderer * r_): r(r_) { + line_width = 1; + current_action = haNoAction; + current_handle = 0; + mat_xyz.resize(3); mat_ms2.resize(3); + color_xyz.resize(3); color_ms2.resize(3); + const QVector3D _rot [3] = {QVector3D(0,1,0), QVector3D(-1,0,0), QVector3D(0, 0,1)}; + const QVector3D _rot2[3] = {QVector3D(0,0,0), QVector3D( 1,0,0), QVector3D(0,-1,0)}; + for (int i = 0; i < 3; ++i) { + QMatrix4x4 m; + m.rotate(90., _rot[i]); + mat_xyz[i] = m; + m.setToIdentity(); + if (!_rot2[i].isNull()) + m.rotate(90., _rot2[i]); + mat_ms2[i] = m; + color_xyz[i] = color_ms2[i] = QVector4D(0,0,0,0.8); + color_xyz[i][i] = 1.; + } + color_ms2[0] = (color_xyz[0] + color_xyz[1]) / 2.; + color_ms2[1] = (color_xyz[0] + color_xyz[2]) / 2.; + color_ms2[2] = (color_xyz[1] + color_xyz[2]) / 2.; + axis_camera = new Camera(); + axis_camera->setAim(QVector3D()); + axis_camera->setFOV(45.); + axis_mesh = Primitive::arrow(12); + size_vp_scale = size_full_scale = 1.; + + box_mesh = Primitive::cube(0.8, 0.8, 0.8); + box_mesh_f = Primitive::cubeFrame(0.8, 0.8, 0.8); + omni_mesh = Primitive::ellipsoid(2, 1, 0.5); + omni_mesh_f = Primitive::ellipsoidFrame(2, 1, 0.5); + omni_mesh ->scalePoints(1.5); + omni_mesh_f ->scalePoints(1.5); + cone_mesh = Primitive::cone(8, 0.5, 1.); + cone_mesh_f = Primitive::coneFrame(8, 0.5, 1.); + QMatrix4x4 mat; + mat.translate(0,0,-1); + cone_mesh ->transformPoints(mat); + cone_mesh_f->transformPoints(mat); + cone_mesh_f->scalePoints(1.5); + + box_mesh ->scalePoints(1.3); + omni_mesh ->scalePoints(1.3); + cone_mesh ->translatePoints(0,0,0.5); + cone_mesh ->scalePoints(1.4); + cone_mesh ->translatePoints(0,0,-0.5); + cone_mesh ->scalePoints(1.5); + + camera_mesh = Primitive::cube(1.2, 1.2, 1.2); + mat.translate(0,0,-0.5); + Mesh * m = Primitive::cone(6, 0.6, 1.); + m->scalePoints(1.4); + m->transformPoints(mat); + camera_mesh->append(m); + camera_mesh_f = Primitive::cubeFrame(1., 1., 1.); + camera_mesh_f->append(Primitive::cubeFrame(0.5, 0.5, 0.5)); + m = Primitive::coneFrame(6, 0.6, 1.); + m->transformPoints(mat); + camera_mesh_f->append(m); + + line_spot_f = Primitive::lineFrame(QVector3D(), QVector3D(0, 0, -1)); + line_camera_f = Primitive::lineFrame(QVector3D(), QVector3D(0, 0, -1)); + + handle_move_mesh = Primitive::arrow(12, 0.06); + handle_ms_2_mesh = Primitive::torus(8, 12, 0.5, 0.02, 90); + m = Primitive::disc(8, 0.5, 90); + handle_ms_2_mesh->append(m); + m->flipNormals(); + handle_ms_2_mesh->append(m); + delete m; + + handle_rotate_mesh = Primitive::arrow(12, 0.03); + m = Primitive::torus(30, 12, 0.5, 0.06); + m->translatePoints(QVector3D(0., 0., 0.75)); + handle_rotate_mesh->append(m); + delete m; + + handle_scale_mesh = Primitive::cylinder(12, 0.03, 0.85); + m = Primitive::ellipsoid(12, 12, 0.15); + m->translatePoints(QVector3D(0., 0., 0.85)); + handle_scale_mesh->append(m); + delete m; + handle_scale_3_mesh = Primitive::ellipsoid(12, 12, 0.2); + + handle_move_mesh ->scalePoints(7.5); + handle_ms_2_mesh ->scalePoints(7.5); + handle_rotate_mesh ->scalePoints(7.5); + handle_scale_mesh ->scalePoints(7.5); + handle_scale_3_mesh->scalePoints(7.5); +} + + +RendererService::~RendererService() { + delete box_mesh; + delete box_mesh_f; + delete omni_mesh; + delete omni_mesh_f; + delete cone_mesh; + delete cone_mesh_f; + delete camera_mesh; + delete camera_mesh_f; + delete line_spot_f; + delete line_camera_f; + delete axis_camera; + delete axis_mesh; + delete handle_move_mesh; + delete handle_ms_2_mesh; + delete handle_rotate_mesh; + delete handle_scale_mesh; + delete handle_scale_3_mesh; +} + + +void RendererService::init(int width, int height) { + fillXYZObjects(); + axis_mesh->loadObjects(r->view, cur_objects); + resize(width, height); +} + + +void RendererService::resize(int width, int height) { + axis_viewport = preferredIconSize(10.); + line_width = lineThickness(); + size_vp_scale = 25. * appScale() / qMax(qMin(width, height), 1); + //qDebug() << axis_viewport; +} + + +QMatrix4x4 RendererService::invariantSizeMatrix(QVector3D p, double * ret_scale) { + QVector4D pos = QVector4D(p, 1.); + double dist = -(v_mat * pos).z(); + QMatrix4x4 m; + m.translate(pos.toVector3D()); + m.scale(dist * size_full_scale); + if (ret_scale) *ret_scale = dist * size_full_scale; + return m; +} + + +QMatrix4x4 RendererService::parentRotationMatrix(ObjectBase * o, bool self_rotation) { + QMatrix4x4 ret; + if (!o) return ret; + QMatrix4x4 pmat; + if (o->parent()) { + pmat = o->parent()->transform().matrixRotate(); + } + if (self_rotation) { + ret *= o->transform().matrixRotate(); + } + ret = pmat * ret; + return ret; +} + + +void RendererService::fillXYZObjects() { + cur_objects.resize(3); + for (int i = 0; i < 3; ++i) { + cur_objects[i].color = color_xyz[i]; + mat_xyz[i].transposed().copyDataTo(cur_objects[i].modelmatrix); + } +} + + +void RendererService::fillOmniObjects() { + QList ll = r->view->scene()->lights_used.value(Light::Omni); + Object o; + cur_objects.clear(); + foreach (Light * l, ll) { + QMatrix4x4 m = invariantSizeMatrix(l->worldPos()); + m.transposed().copyDataTo(o.modelmatrix); + o.object_id = l->id_; + cur_objects << o; + } +} + + +void RendererService::fillAimedObjects(const ObjectBaseList & objects, Mesh * line_mesh) { + Object o; + cur_objects.clear(); + cur_aims.clear(); + QVector & lv (line_mesh->vertices ()); + QVector & ln (line_mesh->normals ()); + QVector & lt (line_mesh->texcoords ()); + QVector< Vector2i> & lind(line_mesh->indicesLines()); + lv.clear(); + foreach (ObjectBase * go, objects) { + AimedObject * ao = (AimedObject *)go; + QMatrix4x4 m; + m = invariantSizeMatrix(ao->worldPos()) * parentRotationMatrix(ao); + m.transposed().copyDataTo(o.modelmatrix); + o.object_id = ao->id_; + cur_objects << o; + + lv << ao->worldPos() << ao->worldAim(); + + m = invariantSizeMatrix(ao->worldAim()); + m.transposed().copyDataTo(o.modelmatrix); + o.object_id = ao->id_ + 1; + cur_aims << o; + } + ln.resize(lv.size()); + lt.resize(lv.size()); + lind.resize(lv.size() / 2); + for (int i = 0; i < lind.size(); ++i) { + lind[i] = Vector2i(i*2, i*2 + 1); + } +} + + +void RendererService::fillHandleObjects(QVector3D center, HandleMesh ids[], const QVector & mats, const QVector & colors, QMatrix4x4 add_mat, int count) { + QMatrix4x4 m = invariantSizeMatrix(center) * add_mat; + cur_objects.resize(count); + for (int i = 0; i < count; ++i) { + cur_objects[i].color = colors[i]; + QMatrix4x4 omat = m * mats[i]; + cur_objects[i].object_id = ids[i]; + if (current_handle.testFlag(ids[i])) { + cur_objects[i].color = QVector4D(0,1,1,1); + } + omat.transposed().copyDataTo(cur_objects[i].modelmatrix); + } +} + + +bool RendererService::calculateCenter() { + ObjectBaseList sol = r->view->scene()->selectedObjects(true); + if (sol.isEmpty()) return false; + selection_center = sol[0]->worldPos(); + if (sol.size() > 1) { + Box3D bb; + foreach (ObjectBase * o, sol) { + o->calculateBoundingBox(); + bb |= o->boundingBox(); + } + if (!bb.isEmpty()) { + selection_center = bb.center(); + } + } + axis_mat = QMatrix4x4(); + if ((sol.size() == 1)) { + if (current_action == haMove) { + if (sol[0]->selected_aim) + selection_center = ((AimedObject*)sol[0])->worldAim(); + } else { + axis_mat = parentRotationMatrix(sol[0]); + } + } + return true; +} + + +void RendererService::drawCurrentHandleObjects() { + if (current_action == haNoAction) return; + if (calculateCenter()) { + HandleMesh ids[3]; + switch (current_action) { + case haMove : ids[0] = hmMoveX ; ids[1] = hmMoveY ; ids[2] = hmMoveZ ; break; + case haRotate: ids[0] = hmRotateX; ids[1] = hmRotateY; ids[2] = hmRotateZ; break; + case haScale : ids[0] = hmScaleX ; ids[1] = hmScaleY ; ids[2] = hmScaleZ ; break; + default: break; + } + fillHandleObjects(selection_center, ids, mat_xyz, color_xyz, axis_mat); + Mesh * hm = currentHandleMesh(); + QVector sel; + sel.fill(0, 3); + if (hm) { + hm->loadObjects(r->view, cur_objects); + hm->loadSelections(r->view, sel); + hm->draw(r->view, 3); + } + if (current_action == haMove || current_action == haScale) { + switch (current_action) { + case haMove : ids[0] = hmMoveXY ; ids[1] = hmMoveXZ ; ids[2] = hmMoveYZ ; break; + case haScale : ids[0] = hmScaleXY ; ids[1] = hmScaleXZ ; ids[2] = hmScaleYZ ; break; + default: break; + } + hm = handle_ms_2_mesh; + fillHandleObjects(selection_center, ids, mat_ms2, color_ms2, axis_mat); + hm->loadObjects(r->view, cur_objects); + hm->loadSelections(r->view, sel); + hm->draw(r->view, 3); + if (current_action == haScale) { + hm = handle_scale_3_mesh; + QVector mv; mv.resize(1); + QVector cv; cv.fill(QVector4D(1, 1, 0.5, 1), 1); + ids[0] = hmMaxScale; + fillHandleObjects(selection_center, ids, mv, cv, axis_mat, 1); + hm->loadObjects(r->view, cur_objects); + hm->loadSelections(r->view, sel); + hm->draw(r->view, 1); + } + } + } +} + + +void RendererService::drawLights() { + QGLView * v = r->view; + RendererSelection & rs(r->rend_selection); + + fillOmniObjects(); + omni_mesh->loadObjects(v, cur_objects); + r->fillSelectionsBuffer(rs.cur_selections_, lights2objectList(v->scene_->lights_used.value(Light::Omni))); + omni_mesh->loadSelections(v, rs.cur_selections_); + omni_mesh->draw(v, cur_objects.size()); + + ObjectBaseList ll = lights2objectList(r->view->scene()->lights_used.value(Light::Cone)); + fillAimedObjects(ll, line_spot_f); + cone_mesh->loadObjects(v, cur_objects); + r->fillSelectionsBuffer(rs.cur_selections_, ll); + cone_mesh->loadSelections(v, rs.cur_selections_); + cone_mesh->draw(v, cur_objects.size()); + box_mesh->loadObjects(v, cur_aims); + box_mesh->loadSelections(v, rs.cur_selections_); + box_mesh->draw(v, cur_aims.size()); + +} + + +void RendererService::drawLightsFrame(QColor color) { + QGLView * v = r->view; + RendererSelection & rs(r->rend_selection); + + fillOmniObjects(); + setObjectsColor(cur_objects, color); + omni_mesh_f->loadObjects(v, cur_objects); + r->fillSelectionsBuffer(rs.cur_selections_, lights2objectList(v->scene_->lights_used.value(Light::Omni))); + omni_mesh_f->loadSelections(v, rs.cur_selections_); + omni_mesh_f->draw(v, cur_objects.size()); + + ObjectBaseList ll = lights2objectList(r->view->scene()->lights_used.value(Light::Cone)); + fillAimedObjects(ll, line_spot_f); + setObjectsColor(cur_objects, color); + cone_mesh_f->loadObjects(v, cur_objects); + r->fillSelectionsBuffer(rs.cur_selections_, ll); + cone_mesh_f->loadSelections(v, rs.cur_selections_); + cone_mesh_f->draw(v, cur_objects.size()); + + setObjectsColor(cur_aims, color); + box_mesh_f->loadObjects(v, cur_aims); + box_mesh_f->loadSelections(v, rs.cur_selections_); + box_mesh_f->draw(v, cur_aims.size()); + +} + + +void RendererService::drawCameras() { + QGLView * v = r->view; + RendererSelection & rs(r->rend_selection); + + ObjectBaseList cl = cameras2objectList(r->view->scene()->cameras_used); + cl.removeOne(r->view->camera()); + fillAimedObjects(cl, line_camera_f); + camera_mesh->loadObjects(v, cur_objects); + r->fillSelectionsBuffer(rs.cur_selections_, cl); + camera_mesh->loadSelections(v, rs.cur_selections_); + camera_mesh->draw(v, cur_objects.size()); + box_mesh->loadObjects(v, cur_aims); + box_mesh->loadSelections(v, rs.cur_selections_); + box_mesh->draw(v, cur_aims.size()); + +} + + +void RendererService::drawCamerasFrame(QColor color) { + QGLView * v = r->view; + RendererSelection & rs(r->rend_selection); + + ObjectBaseList cl = cameras2objectList(r->view->scene()->cameras_used); + cl.removeOne(r->view->camera()); + fillAimedObjects(cl, line_camera_f); + setObjectsColor(cur_objects, color); + camera_mesh_f->loadObjects(v, cur_objects); + r->fillSelectionsBuffer(rs.cur_selections_, cl); + camera_mesh_f->loadSelections(v, rs.cur_selections_); + camera_mesh_f->draw(v, cur_objects.size()); + + setObjectsColor(cur_aims, color); + box_mesh_f->loadObjects(v, cur_aims); + box_mesh_f->loadSelections(v, rs.cur_selections_); + box_mesh_f->draw(v, cur_aims.size()); + +} + + +void RendererService::setObjectsColor(QVector & ol, QColor col) { + QVector4D cv = QColor2QVector(col); + for (int i = 0; i < ol.size(); ++i) + ol[i].color = cv; +} + + +void RendererService::renderService() { + QOpenGLShaderProgram * prog = 0; + QOpenGLExtraFunctions * f = r->view; + size_full_scale = tan(r->view->camera()->FOV() / 2. * deg2rad) * size_vp_scale; + v_mat = r->view->camera()->fullViewMatrix(); + f->glEnable(GL_MULTISAMPLE); + glEnableDepth(); + f->glClear(GL_DEPTH_BUFFER_BIT); + glDisable(GL_CULL_FACE); + if (r->bindShader(Renderer::srServiceFrame, &prog)) { + prog->setUniformValue("qgl_ProjMatrix", r->view->camera()->projectionMatrix(r->view->aspect)); + + /// lights + r->setUniformCamera(prog, r->view->camera()); + + prog->setUniformValue("line_width", 2.f); + prog->setUniformValue("z_offset", 0.f); + drawLightsFrame(Qt::white); + drawCamerasFrame(Qt::white); + prog->setUniformValue("line_width", 1.f); + prog->setUniformValue("z_offset", -1.E-2f); + drawLightsFrame(Qt::black); + drawCamerasFrame(Qt::black); + + } + if (r->bindShader(Renderer::srServiceLine, &prog)) { + //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + r->setUniformCamera(prog, r->view->camera()); + line_object.color = QColor2QVector(Qt::white); + line_spot_f ->loadObject(f, line_object); + line_camera_f->loadObject(f, line_object); + line_spot_f ->draw(f, 1); + line_camera_f->draw(f, 1); + //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + glEnable(GL_CULL_FACE); + if (r->bindShader(Renderer::srServiceFill, &prog)) { + r->setUniformCamera(prog, r->view->camera()); + + /// handles + f->glEnable(GL_BLEND); + f->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + drawCurrentHandleObjects(); + f->glDisable(GL_BLEND); + + /// axis + f->glViewport(0, 0, axis_viewport.width(), axis_viewport.height()); + axis_camera->setPos(-r->view->camera()->direction() * 3.); + axis_camera->setAim(QVector3D()); + axis_camera->setRotation(r->view->camera()->rotation()); + r->setUniformCamera(prog, axis_camera, true, axis_viewport); + axis_mesh->draw(f, 3); + f->glViewport(0, 0, r->view->width(), r->view->height()); + + } + f->glDisable(GL_MULTISAMPLE); +} + + +Mesh * RendererService::currentHandleMesh() { + switch (current_action) { + case haMove : return handle_move_mesh; + case haRotate: return handle_rotate_mesh; + case haScale : return handle_scale_mesh; + default: break; + } + return 0; +} diff --git a/renderer_service.h b/renderer_service.h new file mode 100644 index 0000000..c0de71f --- /dev/null +++ b/renderer_service.h @@ -0,0 +1,108 @@ +/* + QGL RendererService + 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 . +*/ + +#ifndef RENDERER_SERVICE_H +#define RENDERER_SERVICE_H + +#include "glframebuffer.h" +#include "glshaders_types.h" +#include + + +class RendererService { + friend class QGLView; + friend class MouseController; + friend class Renderer; + friend class RendererSelection; + +public: + RendererService(Renderer * r_); + virtual ~RendererService(); + + enum HandleAction { + haNoAction, + haMove, + haRotate, + haScale, + }; + + enum HandleMesh { + hmMoveX = 0x01, + hmMoveY = 0x02, + hmMoveZ = 0x04, + hmMoveXY = hmMoveX | hmMoveY, + hmMoveXZ = hmMoveX | hmMoveZ, + hmMoveYZ = hmMoveY | hmMoveZ, + hmMaxMove = hmMoveX | hmMoveY | hmMoveZ, + hmRotateX = 0x08, + hmRotateY = 0x10, + hmRotateZ = 0x20, + hmMaxRotate = hmRotateX | hmRotateY | hmRotateZ, + hmScaleX = 0x40, + hmScaleY = 0x80, + hmScaleZ = 0x100, + hmScaleXY = hmScaleX | hmScaleY, + hmScaleXZ = hmScaleX | hmScaleZ, + hmScaleYZ = hmScaleY | hmScaleZ, + hmMaxScale = hmScaleX | hmScaleY | hmScaleZ, + }; + + void init(int width, int height); + void resize(int width, int height); + + QMatrix4x4 invariantSizeMatrix(QVector3D p, double * ret_scale = 0); + QMatrix4x4 parentRotationMatrix(ObjectBase * o, bool self_rotation = true); + void fillXYZObjects(); + void fillOmniObjects(); + void fillAimedObjects(const ObjectBaseList & objects, Mesh * line_mesh); + void fillHandleObjects(QVector3D center, HandleMesh ids[], const QVector & mats, const QVector & colors, QMatrix4x4 add_mat, int count = 3); + bool calculateCenter(); + void drawCurrentHandleObjects(); + void drawLights(); + void drawLightsFrame(QColor color); + void drawCameras(); + void drawCamerasFrame(QColor color); + void setObjectsColor(QVector & ol, QColor col); + void renderService(); + void setCurrentAction(HandleAction ha) {current_action = ha;} + Mesh * currentHandleMesh(); + +private: + Renderer * r; + + Mesh * axis_mesh, * handle_move_mesh, * handle_rotate_mesh, * handle_scale_mesh; + Mesh * handle_ms_2_mesh, * handle_scale_3_mesh; + Mesh * box_mesh_f, * omni_mesh_f, * cone_mesh_f, * camera_mesh_f; + Mesh * box_mesh, * omni_mesh, * cone_mesh, * camera_mesh; + Mesh * line_spot_f, * line_camera_f; + QMatrix4x4 v_mat, axis_mat; + QVector3D selection_center; + QVector mat_xyz, mat_ms2; + QVector color_xyz, color_ms2; + QVector cur_objects, cur_aims; + QGLEngineShaders::Object line_object; + Camera * axis_camera; + QSize axis_viewport; + HandleAction current_action; + QFlags current_handle; + int line_width; + double size_vp_scale, size_full_scale; + +}; + +#endif // RENDERER_SERVICE_H diff --git a/shaders.qrc b/shaders.qrc new file mode 100644 index 0000000..97e4492 --- /dev/null +++ b/shaders.qrc @@ -0,0 +1,19 @@ + + + shaders/ds_final.glsl + shaders/ds_geom.glsl + shaders/ds_light.glsl + shaders/ds_tonemap.glsl + shaders/fxaa.frag + shaders/fxaa.vert + shaders/fxaa_v3.h + shaders/selection.glsl + shaders/selection_apply.glsl + shaders/selection_frame.glsl + shaders/selection_halo.glsl + shaders/service_fill.glsl + shaders/service_frame.glsl + shaders/service_line.glsl + shaders/sum.glsl + + diff --git a/shaders/ds_final.glsl b/shaders/ds_final.glsl new file mode 100644 index 0000000..73aecb6 --- /dev/null +++ b/shaders/ds_final.glsl @@ -0,0 +1,22 @@ +// vert // + +void main(void) { + gl_Position = qgl_ftransform(); +} + + +// frag // + +uniform sampler2D tex_g1, tex_s_0, tex_s_1, tex_t_0, tex_t_1; + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + vec4 vs0 = texelFetch(tex_s_0, tc, 0); + vec4 vs1 = texelFetch(tex_s_1, tc, 0); + vec3 sr = vs0.rgb + vs1.rgb; + vec4 vt0 = texelFetch(tex_t_0, tc, 0); + vec4 vt1 = texelFetch(tex_t_1, tc, 0); + vec3 tr = vt0.rgb + vt1.rgb; + float alpha = (vt0.a + vt1.a) / 2; + qgl_FragColor.rgb = mix(sr, tr, alpha); +} diff --git a/shaders/ds_geom.glsl b/shaders/ds_geom.glsl new file mode 100644 index 0000000..617a6ee --- /dev/null +++ b/shaders/ds_geom.glsl @@ -0,0 +1,71 @@ +// vert // + +out vec3 geom_normal; +out mat3 TBN; +out vec4 object_color; + +void main(void) { + qgl_MaterialIndex = qgl_Material; + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); + + geom_normal = normalize(qgl_Normal * qgl_getNormalMatrix()); + TBN = qgl_getTangentMatrix() * mat3(qgl_Tangent, qgl_Bitangent, qgl_Normal); + object_color = qgl_ObjectColor; +} + + +// frag // + +in vec3 geom_normal; +in mat3 TBN; +in vec4 object_color; + +uniform vec2 dt; +uniform float z_near; + +const vec3 luma = vec3(0.299, 0.587, 0.114); +const float _pe = 2.4e-7; + +void main(void) { + vec2 tc = qgl_FragTexture.xy; + + vec4 diffuse = qgl_materialTexture(QGL_MAP_DIFFUSE, tc, vec4(0)) * object_color; + diffuse.rgb *= qgl_material[qgl_MaterialIndex].color_diffuse.rgb; + diffuse.a *= (1.f - qgl_material[qgl_MaterialIndex].transparency); + + vec3 normal, dn; + dn = qgl_materialTexture(QGL_MAP_NORMAL, tc, -vec4(0.5, 0.5, 1., 0.)).xyz; + //dn.y = -dn.y; + float dn_sl = length(dn); + dn = TBN * dn; + dn *= dn_sl / (length(dn) + 1E-6); + normal = normalize(geom_normal + dn); + + float metalness = dot(qgl_materialTexture(QGL_MAP_METALNESS, tc, vec4(0)).rgb, luma); + metalness = clamp(metalness, 0, 1); + + float roughness = dot(qgl_materialTexture(QGL_MAP_ROUGHNESS, tc, vec4(0)).rgb, luma); + roughness = clamp(roughness, 0.0001, 0.9999); + + float reflectivity = clamp(qgl_material[qgl_MaterialIndex].reflectivity, 0., 1.); + + vec4 emission = qgl_materialTexture(QGL_MAP_EMISSION, tc, vec4(0)); + emission *= qgl_material[qgl_MaterialIndex].color_emission; + + float height = dot(qgl_materialTexture(QGL_MAP_RELIEF, tc, vec4(0)).rgb, luma); + + float z = gl_FragCoord.z; + z = z + z - 1; + z = ((_pe - 2.) * z_near) / (z + _pe - 1.); // infinite depth + + qgl_FragData[0] = vec4(diffuse .rgba); + qgl_FragData[1] = vec4(normal .xyz, z); + qgl_FragData[2] = vec4(metalness, roughness, reflectivity, 0); + qgl_FragData[3] = vec4(emission.rgb, 0/*bn.x*/); + //qgl_FragData[4] = vec4(speed.xy, bn.yz); + + //ivec2 itc = ivec2(gl_FragCoord.xy); + //qgl_FragData[0].rgb = vec3(dot(n,vec3(0,0,1))); + //qgl_FragData[0].rgb = diffuse.rgb * dot(n,vec3(0,0,1)); +} diff --git a/shaders/ds_light.glsl b/shaders/ds_light.glsl new file mode 100644 index 0000000..7f8c132 --- /dev/null +++ b/shaders/ds_light.glsl @@ -0,0 +1,181 @@ +// vert // + +out vec3 view_dir, world_dir; + +uniform vec4 view_corners[4], world_corners[4]; + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); + view_dir = view_corners [gl_VertexID].xyz; + world_dir = world_corners[gl_VertexID].xyz; +} + + +// frag // + +in vec3 view_dir, world_dir; + +uniform vec2 dt; +uniform float z_near; +uniform sampler2D tex_coeffs[2]; +uniform sampler2D tex_0, tex_1, tex_2, tex_3, tex_4; +uniform samplerCube tex_env; +uniform int lights_start, lights_count; + +uniform vec4 fog_color = vec4(0.5, 0.5, 0.5, 1); +uniform float fog_decay = 10, fog_density = 0; +uniform mat3 view_mat; + +const vec3 luma = vec3(0.299, 0.587, 0.114); +const float _min_rough = 1.e-8, max_lod = 8; + +vec4 pos, lpos, shp; +vec3 li, si, ldir, halfV, bn, bn2, lwdir; +//vec3 vds, vds2; +float rough_diff, rough_spec, dist, NdotL, NdotH, spot, ldist, diff, spec, sdist, shadow; + +void calcLight(in int index, in vec3 n, in vec3 v) { + lpos = qgl_light_position[index].position; + ldir = lpos.xyz - (pos.xyz * lpos.w); + ldist = length(ldir); + ldir = normalize(ldir); + //ldir = vec3(0,0,1); + halfV = normalize(ldir + v); + NdotL = max(dot(n, ldir), 1E-8); + NdotH = max(dot(n, halfV), 1E-8); + spot = step(1.001E-8, NdotL) * qgl_light_parameter[index].decay_intensity.w; +#ifdef SPOT + float scos = max(dot(-ldir, qgl_light_position[index].direction.xyz), 0.); + spot *= scos * step(qgl_light_parameter[index].angles.w, scos); + spot *= smoothstep(qgl_light_parameter[index].angles.w, qgl_light_parameter[index].angles.y, scos); + /*//lwdir = mat3(mat_viewi) * qgl_Light[index].direction.xyz; + //bn = normalize(cross(lwdir, vec3(1, 0, 0))); + //bn2 = normalize(cross(lwdir, bn)); + float ds = ldist/200.;//max(abs(sdist) / 5000, 0.02); + //spot *= clamp(1. - sdist, 0, 1); + vds = ds * bn.xyz; + vds2 = ds * bn2.xyz; + float shadow = getShadow(index, pos.xyz, vec3(0)) * 3.; + shadow += getShadow(index, pos.xyz, vds ) * 2.; + shadow += getShadow(index, pos.xyz, - vds ) * 2.; + shadow += getShadow(index, pos.xyz, - vds2 ) * 2.; + shadow += getShadow(index, pos.xyz, + vds2 ) * 2.; + //shadow += getShadow(index, pos.xyz, vds - vds2 ) * 1.5; + //shadow += getShadow(index, pos.xyz, vds + vds2 ) * 1.5; + //shadow += getShadow(index, pos.xyz, - vds - vds2 ) * 1.5; + //shadow += getShadow(index, pos.xyz, - vds + vds2 ) * 1.5; + //shadow += getShadow(index, pos.xyz, vds + vds ); + //shadow += getShadow(index, pos.xyz, - vds - vds ); + //shadow += getShadow(index, pos.xyz, - vds2 - vds2); + //shadow += getShadow(index, pos.xyz, + vds2 + vds2); + //shadow += getShadow(index, pos.xyz, vds + vds - vds2 ); + //shadow += getShadow(index, pos.xyz, - vds - vds - vds2 ); + //shadow += getShadow(index, pos.xyz, vds + vds + vds2 ); + //shadow += getShadow(index, pos.xyz, - vds - vds + vds2 ); + //shadow += getShadow(index, pos.xyz, vds - vds2 - vds2); + //shadow += getShadow(index, pos.xyz, vds + vds2 + vds2); + //shadow += getShadow(index, pos.xyz, - vds - vds2 - vds2); + //shadow += getShadow(index, pos.xyz, - vds + vds2 + vds2); + //shadow += shadow += getShadow(index, pos.xyz, vds+vds2)*10; + spot *= mix(1., shadow / 11., shadow_on);*/ +#endif + vec3 dist_decay = vec3(1, ldist, ldist*ldist); + spot /= dot(qgl_light_parameter[index].decay_intensity.xyz, dist_decay); + + float NdotLs = NdotL*NdotL; + float NdotHs = NdotH*NdotH; + + float ndlc = (1. - NdotLs) / NdotLs; + diff = 2. / (1. + sqrt(1. + (1. - rough_diff) * ndlc)); + //diff = texture(tex_coeffs[0], vec2(roughness, (NdotLs))).r; + li += spot * diff * qgl_light_parameter[index].color.rgb; + + ndlc = (1. - NdotHs) / NdotHs; + float der = NdotHs * (rough_spec + ndlc); + spec = rough_spec / (der*der) / 3.1416; + //spec = texture(tex_coeffs[1], vec2(roughness, (NdotHs))).r; + si += spot * spec * qgl_light_parameter[index].color.rgb; +} + + + +float GeometrySchlickGGX(float NdotV, float roughness) { + float a = roughness; + float k = (a * a) / 2.0; + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + return nom / denom; +} +// ---------------------------------------------------------------------------- +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + return ggx1 * ggx2; +} + + + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + vec4 v1 = texelFetch(tex_1, tc, 0); + float z = v1.w; + if (z == 1.) { + discard; + } + pos.w = 1; + pos.xyz = view_dir * z; + vec3 v = normalize(-pos.xyz); + + vec4 v0 = texelFetch(tex_0, tc, 0), + v2 = texelFetch(tex_2, tc, 0), + v3 = texelFetch(tex_3, tc, 0), + v4 = texelFetch(tex_4, tc, 0); + + vec3 diffuse = v0.rgb; + vec3 normal = v1.xyz; + vec3 emission = v3.rgb; + float alpha = v0.a; + float metalness = v2.r; + float roughness = v2.g; + float reflectivity = v2.b; + float NdotV = dot(normal, v); + float roughness3 = roughness*roughness*roughness; + //bn = normalize(vec3(v3.w, v4.zw)); + //bn2 = normalize(cross(n, bn)); + rough_diff = max(roughness, _min_rough); + rough_spec = max(roughness3, _min_rough); + float shlick = clamp(metalness + (1 - metalness) * pow(1 - NdotV, 5), 0, 1); + + li = vec3(0.);//qgl_AmbientLight.color.rgb * qgl_AmbientLight.intensity; + si = vec3(0.); + for (int i = 0; i < lights_count; ++i) + calcLight(lights_start + i, normal, v); + si *= shlick; + li *= (1 - shlick); + alpha = min(1, alpha * (1 + shlick)); + + vec2 brdf = texture(tex_coeffs[0], vec2(NdotV*0.99, roughness*0.995)).rg; + float env_spec = shlick * brdf.x + brdf.y; + vec3 spec_col = mix(vec3(1), diffuse, metalness); + vec3 env_dir = view_mat * reflect(-v, normal); + vec3 env_col = textureLod(tex_env, env_dir, sqrt(roughness) * max_lod).rgb * spec_col; + + vec3 res_col = max(vec3(0), li * diffuse + si * spec_col + emission); + res_col = mix(res_col, env_col, env_spec * reflectivity); + + float plen = length(pos.xyz); + float fog = 1 - exp(-plen / fog_decay); + fog = clamp(fog * fog_color.a * fog_density, 0, 1); + res_col = mix(res_col, fog_color.rgb, fog); + + qgl_FragColor = vec4(res_col, alpha); + + //vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); + //qgl_FragColor.rgb = vec3(shlick * brdf.x + brdf.y); + //qgl_FragColor.rgb = vec3(alpha); + //qgl_FragColor.rgb = vec3(textureLod(tex_env, world_dir, 0).rgb); + //qgl_FragColor.a = 1.; +} diff --git a/shaders/ds_tonemap.glsl b/shaders/ds_tonemap.glsl new file mode 100644 index 0000000..b9b1f8a --- /dev/null +++ b/shaders/ds_tonemap.glsl @@ -0,0 +1,25 @@ +// vert // + +void main(void) { + gl_Position = qgl_ftransform(); +} + + +// frag // + +uniform sampler2D tex_0; +uniform float gamma, frame_max; + +const vec3 luma = vec3(0.299, 0.587, 0.114); + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + vec4 src = texelFetch(tex_0, tc, 0); + vec3 res = src.rgb; + float l = dot(res, luma) * 0.75; + float g = gamma / frame_max; + res /= l; + l = 1 - exp(-l*g); + res *= l; + qgl_FragColor = vec4(res, dot(res, luma)); +} diff --git a/shaders/fxaa.frag b/shaders/fxaa.frag new file mode 100644 index 0000000..5e93037 --- /dev/null +++ b/shaders/fxaa.frag @@ -0,0 +1,2223 @@ +/*============================================================================ + + + NVIDIA FXAA 3.11 by TIMOTHY LOTTES + + +------------------------------------------------------------------------------ +COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. +------------------------------------------------------------------------------ +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED +*AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA +OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR +LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, +OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE +THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +------------------------------------------------------------------------------ + INTEGRATION CHECKLIST +------------------------------------------------------------------------------ +(1.) +In the shader source, setup defines for the desired configuration. +When providing multiple shaders (for different presets), +simply setup the defines differently in multiple files. +Example, + + #define FXAA_PC 1 + #define FXAA_HLSL_5 1 + #define FXAA_QUALITY__PRESET 12 + +Or, + + #define FXAA_360 1 + +Or, + + #define FXAA_PS3 1 + +Etc. + +(2.) +Then include this file, + + #include "Fxaa3_11.h" + +(3.) +Then call the FXAA pixel shader from within your desired shader. +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. +As for FXAA 3.11 all inputs for all shaders are the same +to enable easy porting between platforms. + + return FxaaPixelShader(...); + +(4.) +Insure pass prior to FXAA outputs RGBL (see next section). +Or use, + + #define FXAA_GREEN_AS_LUMA 1 + +(5.) +Setup engine to provide the following constants +which are used in the FxaaPixelShader() inputs, + + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. + +(6.) +Have FXAA vertex shader run as a full screen triangle, +and output "pos" and "fxaaConsolePosPos" +such that inputs in the pixel shader provide, + + // {xy} = center of pixel + FxaaFloat2 pos, + + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + +(7.) +Insure the texture sampler(s) used by FXAA are set to bilinear filtering. + + +------------------------------------------------------------------------------ + INTEGRATION - RGBL AND COLORSPACE +------------------------------------------------------------------------------ +FXAA3 requires RGBL as input unless the following is set, + + #define FXAA_GREEN_AS_LUMA 1 + +In which case the engine uses green in place of luma, +and requires RGB input is in a non-linear colorspace. + +RGB should be LDR (low dynamic range). +Specifically do FXAA after tonemapping. + +RGB data as returned by a texture fetch can be non-linear, +or linear when FXAA_GREEN_AS_LUMA is not set. +Note an "sRGB format" texture counts as linear, +because the result of a texture fetch is linear data. +Regular "RGBA8" textures in the sRGB colorspace are non-linear. + +If FXAA_GREEN_AS_LUMA is not set, +luma must be stored in the alpha channel prior to running FXAA. +This luma should be in a perceptual space (could be gamma 2.0). +Example pass before FXAA where output is gamma 2.0 encoded, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma + return color; + +Another example where output is linear encoded, +say for instance writing to an sRGB formated render target, +where the render target does the conversion back to sRGB after blending, + + color.rgb = ToneMap(color.rgb); // linear color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma + return color; + +Getting luma correct is required for the algorithm to work correctly. + + +------------------------------------------------------------------------------ + BEING LINEARLY CORRECT? +------------------------------------------------------------------------------ +Applying FXAA to a framebuffer with linear RGB color will look worse. +This is very counter intuitive, but happends to be true in this case. +The reason is because dithering artifacts will be more visiable +in a linear colorspace. + + +------------------------------------------------------------------------------ + COMPLEX INTEGRATION +------------------------------------------------------------------------------ +Q. What if the engine is blending into RGB before wanting to run FXAA? + +A. In the last opaque pass prior to FXAA, + have the pass write out luma into alpha. + Then blend into RGB only. + FXAA should be able to run ok + assuming the blending pass did not any add aliasing. + This should be the common case for particles and common blending passes. + +A. Or use FXAA_GREEN_AS_LUMA. + +============================================================================*/ + +/*============================================================================ + + INTEGRATION KNOBS + +============================================================================*/ +// +// FXAA_PS3 and FXAA_360 choose the console algorithm (FXAA3 CONSOLE). +// FXAA_360_OPT is a prototype for the new optimized 360 version. +// +// 1 = Use API. +// 0 = Don't use API. +// +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PS3 + #define FXAA_PS3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360 + #define FXAA_360 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360_OPT + #define FXAA_360_OPT 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_PC + // + // FXAA Quality + // The high quality PC algorithm. + // + #define FXAA_PC 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PC_CONSOLE + // + // The console algorithm for PC is included + // for developers targeting really low spec machines. + // Likely better to just run FXAA_PC, and use a really low preset. + // + #define FXAA_PC_CONSOLE 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_3 + #define FXAA_HLSL_3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_4 + #define FXAA_HLSL_4 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_5 + #define FXAA_HLSL_5 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_GREEN_AS_LUMA + // + // For those using non-linear color, + // and either not able to get luma in alpha, or not wanting to, + // this enables FXAA to run using green as a proxy for luma. + // So with this enabled, no need to pack luma in alpha. + // + // This will turn off AA on anything which lacks some amount of green. + // Pure red and blue or combination of only R and B, will get no AA. + // + // Might want to lower the settings for both, + // fxaaConsoleEdgeThresholdMin + // fxaaQualityEdgeThresholdMin + // In order to insure AA does not get turned off on colors + // which contain a minor amount of green. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_GREEN_AS_LUMA 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_EARLY_EXIT + // + // Controls algorithm's early exit path. + // On PS3 turning this ON adds 2 cycles to the shader. + // On 360 turning this OFF adds 10ths of a millisecond to the shader. + // Turning this off on console will result in a more blurry image. + // So this defaults to on. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_EARLY_EXIT 1 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_DISCARD + // + // Only valid for PC OpenGL currently. + // Probably will not work when FXAA_GREEN_AS_LUMA = 1. + // + // 1 = Use discard on pixels which don't need AA. + // For APIs which enable concurrent TEX+ROP from same surface. + // 0 = Return unchanged color on pixels which don't need AA. + // + #define FXAA_DISCARD 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_FAST_PIXEL_OFFSET + // + // Used for GLSL 120 only. + // + // 1 = GL API supports fast pixel offsets + // 0 = do not use fast pixel offsets + // + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GATHER4_ALPHA + // + // 1 = API supports gather4 on alpha channel. + // 0 = API does not support gather4 on alpha channel. + // + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif +#endif + +/*============================================================================ + FXAA CONSOLE PS3 - TUNING KNOBS +============================================================================*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_SHARPNESS + // + // Consoles the sharpness of edges on PS3 only. + // Non-PS3 tuning is done with shader input. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 8.0 is sharper + // 4.0 is softer + // 2.0 is really soft (good for vector graphics inputs) + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 8.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 4.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 2.0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_THRESHOLD + // + // Only effects PS3. + // Non-PS3 tuning is done with shader input. + // + // The minimum amount of local contrast required to apply algorithm. + // The console setting has a different mapping than the quality setting. + // + // This only applies when FXAA_EARLY_EXIT is 1. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 0.25 and 0.125. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 0.125 leaves less aliasing, but is softer + // 0.25 leaves more aliasing, and is sharper + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.125 + #else + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.25 + #endif +#endif + +/*============================================================================ + FXAA QUALITY - TUNING KNOBS +------------------------------------------------------------------------------ +NOTE the other tuning knobs are now in the shader function inputs! +============================================================================*/ +#ifndef FXAA_QUALITY__PRESET + // + // Choose the quality preset. + // This needs to be compiled into the shader as it effects code. + // Best option to include multiple presets is to + // in each shader define the preset, then include this file. + // + // OPTIONS + // ----------------------------------------------------------------------- + // 10 to 15 - default medium dither (10=fastest, 15=highest quality) + // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) + // 39 - no dither, very expensive + // + // NOTES + // ----------------------------------------------------------------------- + // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) + // 13 = about same speed as FXAA 3.9 and better than 12 + // 23 = closest to FXAA 3.9 visually and performance wise + // _ = the lowest digit is directly related to performance + // _ = the highest digit is directly related to style + // + #define FXAA_QUALITY__PRESET 12 +#endif + + +/*============================================================================ + + FXAA QUALITY - PRESETS + +============================================================================*/ + +/*============================================================================ + FXAA QUALITY - MEDIUM DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 10) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 3.0 + #define FXAA_QUALITY__P2 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 11) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 3.0 + #define FXAA_QUALITY__P3 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 12) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 4.0 + #define FXAA_QUALITY__P4 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 13) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 4.0 + #define FXAA_QUALITY__P5 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 14) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 4.0 + #define FXAA_QUALITY__P6 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 15) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 12.0 +#endif + +/*============================================================================ + FXAA QUALITY - LOW DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 20) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 2.0 + #define FXAA_QUALITY__P2 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 21) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 22) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 23) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 24) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 3.0 + #define FXAA_QUALITY__P6 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 25) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 26) + #define FXAA_QUALITY__PS 9 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 4.0 + #define FXAA_QUALITY__P8 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 27) + #define FXAA_QUALITY__PS 10 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 4.0 + #define FXAA_QUALITY__P9 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 28) + #define FXAA_QUALITY__PS 11 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 4.0 + #define FXAA_QUALITY__P10 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 29) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + +/*============================================================================ + FXAA QUALITY - EXTREME QUALITY +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 39) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.0 + #define FXAA_QUALITY__P2 1.0 + #define FXAA_QUALITY__P3 1.0 + #define FXAA_QUALITY__P4 1.0 + #define FXAA_QUALITY__P5 1.5 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + + + +/*============================================================================ + + API PORTING + +============================================================================*/ +#if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D +#else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_120 == 1) + // Requires, + // #version 120 + // And at least, + // #extension GL_EXT_gpu_shader4 : enable + // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_130 == 1) + // Requires "#version 130" or better + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_3 == 1) || (FXAA_360 == 1) || (FXAA_PS3 == 1) + #define FxaaInt2 float2 + #define FxaaTex sampler2D + #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) + #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_4 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_5 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) +#endif + + +/*============================================================================ + GREEN AS LUMA OPTION SUPPORT FUNCTION +============================================================================*/ +#if (FXAA_GREEN_AS_LUMA == 0) + //FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return dot(rgba.rgb, FxaaFloat3(0.299, 0.587, 0.114)); } +#else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +#endif + + + + +/*============================================================================ + + FXAA3 QUALITY - PC + +============================================================================*/ +#if (FXAA_PC == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + FxaaTex fxaaConsole360TexExpBiasNegOne, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + FxaaTex fxaaConsole360TexExpBiasNegTwo, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x___} = -N/screenWidthInPixels + // {_y__} = -N/screenHeightInPixels + // {__z_} = N/screenWidthInPixels + // {___w} = N/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt, + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x___} = -2.0/screenWidthInPixels + // {_y__} = -2.0/screenHeightInPixels + // {__z_} = 2.0/screenWidthInPixels + // {___w} = 2.0/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x___} = 8.0/screenWidthInPixels + // {_y__} = 8.0/screenHeightInPixels + // {__z_} = -4.0/screenWidthInPixels + // {___w} = -4.0/screenHeightInPixels + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + FxaaFloat fxaaConsoleEdgeSharpness, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + FxaaFloat fxaaConsoleEdgeThreshold, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaConsoleEdgeThresholdMin, + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM dot(rgbyM.rgb, FxaaFloat3(0.299, 0.587, 0.114)) + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM dot(rgbyM.rgb, FxaaFloat3(0.299, 0.587, 0.114)) + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif +/*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + //return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + return FxaaFloat4(FxaaTexTop(tex, posM)); + #endif +} +/*==========================================================================*/ +#endif + + + + +/*============================================================================ + + FXAA3 CONSOLE - PC VERSION + +------------------------------------------------------------------------------ +Instead of using this on PC, I'd suggest just using FXAA Quality with + #define FXAA_QUALITY__PRESET 10 +Or + #define FXAA_QUALITY__PRESET 20 +Either are higher qualilty and almost as fast as this on modern PC GPUs. +============================================================================*/ +#if (FXAA_PC_CONSOLE == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy)); + FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw)); + FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy)); + FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw)); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy); + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat lumaM = rgbyM.w; + #else + FxaaFloat lumaM = rgbyM.y; + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw); + lumaNe += 1.0/384.0; + FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe); + FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw); + FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMinM = min(lumaMin, lumaM); + FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled); + FxaaFloat lumaMaxM = max(lumaMax, lumaM); + FxaaFloat dirSwMinusNe = lumaSw - lumaNe; + FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM; + FxaaFloat dirSeMinusNw = lumaSe - lumaNw; + if(lumaMaxSubMinM < lumaMaxScaledClamped) return rgbyM; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir; + dir.x = dirSwMinusNe + dirSeMinusNw; + dir.y = dirSwMinusNe - dirSeMinusNw; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir1 = normalize(dir.xy); + FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw); + FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness; + FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw); + FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyA = rgbyN1 + rgbyP1; + FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25); +/*--------------------------------------------------------------------------*/ + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax); + #else + FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax); + #endif + if(twoTap) rgbyB.xyz = rgbyA.xyz * 0.5; + return rgbyB; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - 360 PIXEL SHADER + +------------------------------------------------------------------------------ +This optimized version thanks to suggestions from Andy Luedke. +Should be fully tex bound in all cases. +As of the FXAA 3.11 release, I have still not tested this code, +however I fixed a bug which was in both FXAA 3.9 and FXAA 3.10. +And note this is replacing the old unoptimized version. +If it does not work, please let me know so I can fix it. +============================================================================*/ +#if (FXAA_360 == 1) +/*--------------------------------------------------------------------------*/ +[reduceTempRegUsage(4)] +float4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + float4 lumaNwNeSwSe; + #if (FXAA_GREEN_AS_LUMA == 0) + asm { + tfetch2D lumaNwNeSwSe.w___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._w__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__w_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___w, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #else + asm { + tfetch2D lumaNwNeSwSe.y___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._y__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__y_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___y, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #endif +/*--------------------------------------------------------------------------*/ + lumaNwNeSwSe.y += 1.0/384.0; + float2 lumaMinTemp = min(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float2 lumaMaxTemp = max(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float lumaMin = min(lumaMinTemp.x, lumaMinTemp.y); + float lumaMax = max(lumaMaxTemp.x, lumaMaxTemp.y); +/*--------------------------------------------------------------------------*/ + float4 rgbyM = tex2Dlod(tex, float4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + float lumaMinM = min(lumaMin, rgbyM.w); + float lumaMaxM = max(lumaMax, rgbyM.w); + #else + float lumaMinM = min(lumaMin, rgbyM.y); + float lumaMaxM = max(lumaMax, rgbyM.y); + #endif + if((lumaMaxM - lumaMinM) < max(fxaaConsoleEdgeThresholdMin, lumaMax * fxaaConsoleEdgeThreshold)) return rgbyM; +/*--------------------------------------------------------------------------*/ + float2 dir; + dir.x = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.yyxx); + dir.y = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.xyxy); + dir = normalize(dir); +/*--------------------------------------------------------------------------*/ + float4 dir1 = dir.xyxy * fxaaConsoleRcpFrameOpt.xyzw; +/*--------------------------------------------------------------------------*/ + float4 dir2; + float dirAbsMinTimesC = min(abs(dir.x), abs(dir.y)) * fxaaConsoleEdgeSharpness; + dir2 = saturate(fxaaConsole360ConstDir.zzww * dir.xyxy / dirAbsMinTimesC + 0.5); + dir2 = dir2 * fxaaConsole360RcpFrameOpt2.xyxy + fxaaConsole360RcpFrameOpt2.zwzw; +/*--------------------------------------------------------------------------*/ + float4 rgbyN1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.xy, 0.0, 0.0)); + float4 rgbyP1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.zw, 0.0, 0.0)); + float4 rgbyN2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.xy, 0.0, 0.0)); + float4 rgbyP2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.zw, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ + float4 rgbyA = rgbyN1 + rgbyP1; + float4 rgbyB = rgbyN2 + rgbyP2 + rgbyA * 0.5; +/*--------------------------------------------------------------------------*/ + float4 rgbyR = ((FxaaLuma(rgbyB) - lumaMax) > 0.0) ? rgbyA : rgbyB; + rgbyR = ((FxaaLuma(rgbyB) - lumaMin) > 0.0) ? rgbyR : rgbyA; + return rgbyR; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (NO EARLY EXIT) + +============================================================================== +The code below does not exactly match the assembly. +I have a feeling that 12 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.z(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h0.w(TRUE), v5.xwxx, #0 + 6: addh h0.z(TRUE), -h2, h0.w + 7: texpkb h1.w(TRUE), v5, #0 + 9: addh h0.x(TRUE), h0.z, -h1.w + 10: addh h3.w(TRUE), h0.z, h1 + 11: texpkb h2.w(TRUE), v5.zwzz, #0 + 13: addh h0.z(TRUE), h3.w, -h2.w + 14: addh h0.x(TRUE), h2.w, h0 + 15: nrmh h1.xz(TRUE), h0_n + 16: minh_m8 h0.x(TRUE), |h1|, |h1.z| + 17: maxh h4.w(TRUE), h0, h1 + 18: divx h2.xy(TRUE), h1_n.xzzw, h0_n + 19: movr r1.zw(TRUE), v4.xxxy + 20: madr r2.xz(TRUE), -h1, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zzww, r1.zzww + 22: minh h5.w(TRUE), h0, h1 + 23: texpkb h0(TRUE), r2.xzxx, #0 + 25: madr r0.zw(TRUE), h1.xzxz, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w), r1 + 27: maxh h4.x(TRUE), h2.z, h2.w + 28: texpkb h1(TRUE), r0.zwzz, #0 + 30: addh_d2 h1(TRUE), h0, h1 + 31: madr r0.xy(TRUE), -h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 33: texpkb h0(TRUE), r0, #0 + 35: minh h4.z(TRUE), h2, h2.w + 36: fenct TRUE + 37: madr r1.xy(TRUE), h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 39: texpkb h2(TRUE), r1, #0 + 41: addh_d2 h0(TRUE), h0, h2 + 42: maxh h2.w(TRUE), h4, h4.x + 43: minh h2.x(TRUE), h5.w, h4.z + 44: addh_d2 h0(TRUE), h0, h1 + 45: slth h2.x(TRUE), h0.w, h2 + 46: sgth h2.w(TRUE), h0, h2 + 47: movh h0(TRUE), h0 + 48: addx.c0 rc(TRUE), h2, h2.w + 49: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB1 | add | 2: ADDh h2.z, h0.--w-, const.--x-; + | | | + 2 | SCT0/1 | mov | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB1 | add | 6: ADDh h0.z,-h2, h0.--w-; + | | | + 3 | SCT0/1 | mov | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 9: ADDh h0.x, h0.z---,-h1.w---; + | SCB1 | add | 10: ADDh h3.w, h0.---z, h1; + | | | + 4 | SCT0/1 | mov | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h2.w---, h0; + | SCB1 | add | 13: ADDh h0.z, h3.--w-,-h2.--w-; + | | | + 5 | SCT1 | mov | 15: NRMh h1.xz, h0; + | SRB | nrm | 15: NRMh h1.xz, h0; + | SCB0 | min | 16: MINh*8 h0.x, |h1|, |h1.z---|; + | SCB1 | max | 17: MAXh h4.w, h0, h1; + | | | + 6 | SCT0 | div | 18: DIVx h2.xy, h1.xz--, h0; + | SCT1 | mov | 19: MOVr r1.zw, g[TEX0].--xy; + | SCB0 | mad | 20: MADr r2.xz,-h1, const.z-w-, r1.z-w-; + | SCB1 | min | 22: MINh h5.w, h0, h1; + | | | + 7 | SCT0/1 | mov | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | TEX | txl | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | SCB0 | max | 27: MAXh h4.x, h2.z---, h2.w---; + | SCB1 | mad | 25: MADr r0.zw, h1.--xz, const, r1; + | | | + 8 | SCT0/1 | mov | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | TEX | txl | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | SCB0/1 | add | 30: ADDh/2 h1, h0, h1; + | | | + 9 | SCT0 | mad | 31: MADr r0.xy,-h2, const.xy--, r1.zw--; + | SCT1 | mov | 33: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 33: TXLr h0, r0, const.zzzz, TEX0; + | SCB1 | min | 35: MINh h4.z, h2, h2.--w-; + | | | + 10 | SCT0 | mad | 37: MADr r1.xy, h2, const.xy--, r1.zw--; + | SCT1 | mov | 39: TXLr h2, r1, const.zzzz, TEX0; + | TEX | txl | 39: TXLr h2, r1, const.zzzz, TEX0; + | SCB0/1 | add | 41: ADDh/2 h0, h0, h2; + | | | + 11 | SCT0 | min | 43: MINh h2.x, h5.w---, h4.z---; + | SCT1 | max | 42: MAXh h2.w, h4, h4.---x; + | SCB0/1 | add | 44: ADDh/2 h0, h0, h1; + | | | + 12 | SCT0 | set | 45: SLTh h2.x, h0.w---, h2; + | SCT1 | set | 46: SGTh h2.w, h0, h2; + | SCB0/1 | mul | 47: MOVh h0, h0; + | | | + 13 | SCT0 | mad | 48: ADDxc0_s rc, h2, h2.w---; + | SCB0/1 | mul | 49: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 0% 0% 50% + 6: 100% 0% 75% + 7: 0% 100% 75% + 8: 0% 100% 100% + 9: 0% 100% 25% + 10: 0% 100% 100% + 11: 50% 0% 100% + 12: 50% 0% 100% + 13: 25% 0% 100% + +MEAN: 17% 61% 67% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 0% 100% + 2: 0% 0% 100% 0% 100% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 0% 0% 0% 100% 100% + 6: 100% 100% 0% 100% 100% + 7: 0% 0% 100% 100% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 0% 100% + 10: 0% 0% 100% 100% 100% + 11: 100% 100% 0% 100% 100% + 12: 100% 100% 0% 100% 100% + 13: 100% 0% 0% 100% 100% + +MEAN: 30% 23% 61% 76% 100% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 13 cycles, 3 r regs, 923,076,923 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 0) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O3 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 dir; + half4 lumaNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + lumaNe.w += half(1.0/512.0); + dir.x = -lumaNe.w; + dir.z = -lumaNe.w; + #else + lumaNe.y += half(1.0/512.0); + dir.x = -lumaNe.y; + dir.z = -lumaNe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSw.w; + dir.z += lumaSw.w; + #else + dir.x += lumaSw.y; + dir.z += lumaSw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x -= lumaNw.w; + dir.z += lumaNw.w; + #else + dir.x -= lumaNw.y; + dir.z += lumaNw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSe.w; + dir.z -= lumaSe.w; + #else + dir.x += lumaSe.y; + dir.z -= lumaSe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half4 dir1_pos; + dir1_pos.xy = normalize(dir.xyz).xz; + half dirAbsMinTimesC = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimesC, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (7) + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (8) + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (9) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (11) + // compilier moves these scalar ops up to other cycles + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMin = min(min(lumaNw.w, lumaSw.w), min(lumaNe.w, lumaSe.w)); + half lumaMax = max(max(lumaNw.w, lumaSw.w), max(lumaNe.w, lumaSe.w)); + #else + half lumaMin = min(min(lumaNw.y, lumaSw.y), min(lumaNe.y, lumaSe.y)); + half lumaMax = max(max(lumaNw.y, lumaSw.y), max(lumaNe.y, lumaSe.y)); + #endif + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (12) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif +/*--------------------------------------------------------------------------*/ +// (13) + if(twoTapLt || twoTapGt) rgby2 = rgby1; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (WITH EARLY EXIT) + +============================================================================== +The code mostly matches the assembly. +I have a feeling that 14 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +Use of FXAA_GREEN_AS_LUMA currently adds a cycle (16 clks). +Will look at fixing this for FXAA 3.12. +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.y(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h1.w(TRUE), v5.xwxx, #0 + 6: addh h0.x(TRUE), h1.w, -h2.y + 7: texpkb h2.w(TRUE), v5.zwzz, #0 + 9: minh h4.w(TRUE), h2.y, h2 + 10: maxh h5.x(TRUE), h2.y, h2.w + 11: texpkb h0.w(TRUE), v5, #0 + 13: addh h3.w(TRUE), -h0, h0.x + 14: addh h0.x(TRUE), h0.w, h0 + 15: addh h0.z(TRUE), -h2.w, h0.x + 16: addh h0.x(TRUE), h2.w, h3.w + 17: minh h5.y(TRUE), h0.w, h1.w + 18: nrmh h2.xz(TRUE), h0_n + 19: minh_m8 h2.w(TRUE), |h2.x|, |h2.z| + 20: divx h4.xy(TRUE), h2_n.xzzw, h2_n.w + 21: movr r1.zw(TRUE), v4.xxxy + 22: maxh h2.w(TRUE), h0, h1 + 23: fenct TRUE + 24: madr r0.xy(TRUE), -h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 26: texpkb h0(TRUE), r0, #0 + 28: maxh h5.x(TRUE), h2.w, h5 + 29: minh h5.w(TRUE), h5.y, h4 + 30: madr r1.xy(TRUE), h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 32: texpkb h2(TRUE), r1, #0 + 34: addh_d2 h2(TRUE), h0, h2 + 35: texpkb h1(TRUE), v4, #0 + 37: maxh h5.y(TRUE), h5.x, h1.w + 38: minh h4.w(TRUE), h1, h5 + 39: madr r0.xy(TRUE), -h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 41: texpkb h0(TRUE), r0, #0 + 43: addh_m8 h5.z(TRUE), h5.y, -h4.w + 44: madr r2.xy(TRUE), h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 46: texpkb h3(TRUE), r2, #0 + 48: addh_d2 h0(TRUE), h0, h3 + 49: addh_d2 h3(TRUE), h0, h2 + 50: movh h0(TRUE), h3 + 51: slth h3.x(TRUE), h3.w, h5.w + 52: sgth h3.w(TRUE), h3, h5.x + 53: addx.c0 rc(TRUE), h3.x, h3 + 54: slth.c0 rc(TRUE), h5.z, h5 + 55: movh h0(c0.NE.w), h2 + 56: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB0 | add | 2: ADDh h2.y, h0.-w--, const.-x--; + | | | + 2 | SCT0/1 | mov | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB0 | add | 6: ADDh h0.x, h1.w---,-h2.y---; + | | | + 3 | SCT0/1 | mov | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | max | 10: MAXh h5.x, h2.y---, h2.w---; + | SCB1 | min | 9: MINh h4.w, h2.---y, h2; + | | | + 4 | SCT0/1 | mov | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h0.w---, h0; + | SCB1 | add | 13: ADDh h3.w,-h0, h0.---x; + | | | + 5 | SCT0 | mad | 16: ADDh h0.x, h2.w---, h3.w---; + | SCT1 | mad | 15: ADDh h0.z,-h2.--w-, h0.--x-; + | SCB0 | min | 17: MINh h5.y, h0.-w--, h1.-w--; + | | | + 6 | SCT1 | mov | 18: NRMh h2.xz, h0; + | SRB | nrm | 18: NRMh h2.xz, h0; + | SCB1 | min | 19: MINh*8 h2.w, |h2.---x|, |h2.---z|; + | | | + 7 | SCT0 | div | 20: DIVx h4.xy, h2.xz--, h2.ww--; + | SCT1 | mov | 21: MOVr r1.zw, g[TEX0].--xy; + | SCB1 | max | 22: MAXh h2.w, h0, h1; + | | | + 8 | SCT0 | mad | 24: MADr r0.xy,-h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 26: TXLr h0, r0, const.xxxx, TEX0; + | TEX | txl | 26: TXLr h0, r0, const.xxxx, TEX0; + | SCB0 | max | 28: MAXh h5.x, h2.w---, h5; + | SCB1 | min | 29: MINh h5.w, h5.---y, h4; + | | | + 9 | SCT0 | mad | 30: MADr r1.xy, h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 32: TXLr h2, r1, const.xxxx, TEX0; + | TEX | txl | 32: TXLr h2, r1, const.xxxx, TEX0; + | SCB0/1 | add | 34: ADDh/2 h2, h0, h2; + | | | + 10 | SCT0/1 | mov | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | TEX | txl | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | SCB0 | max | 37: MAXh h5.y, h5.-x--, h1.-w--; + | SCB1 | min | 38: MINh h4.w, h1, h5; + | | | + 11 | SCT0 | mad | 39: MADr r0.xy,-h4, const.xy--, r1.zw--; + | SCT1 | mov | 41: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 41: TXLr h0, r0, const.zzzz, TEX0; + | SCB0 | mad | 44: MADr r2.xy, h4, const.xy--, r1.zw--; + | SCB1 | add | 43: ADDh*8 h5.z, h5.--y-,-h4.--w-; + | | | + 12 | SCT0/1 | mov | 46: TXLr h3, r2, const.xxxx, TEX0; + | TEX | txl | 46: TXLr h3, r2, const.xxxx, TEX0; + | SCB0/1 | add | 48: ADDh/2 h0, h0, h3; + | | | + 13 | SCT0/1 | mad | 49: ADDh/2 h3, h0, h2; + | SCB0/1 | mul | 50: MOVh h0, h3; + | | | + 14 | SCT0 | set | 51: SLTh h3.x, h3.w---, h5.w---; + | SCT1 | set | 52: SGTh h3.w, h3, h5.---x; + | SCB0 | set | 54: SLThc0 rc, h5.z---, h5; + | SCB1 | add | 53: ADDxc0_s rc, h3.---x, h3; + | | | + 15 | SCT0/1 | mul | 55: MOVh h0(NE0.wwww), h2; + | SCB0/1 | mul | 56: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 50% 0% 25% + 6: 0% 0% 25% + 7: 100% 0% 25% + 8: 0% 100% 50% + 9: 0% 100% 100% + 10: 0% 100% 50% + 11: 0% 100% 75% + 12: 0% 100% 100% + 13: 100% 0% 100% + 14: 50% 0% 50% + 15: 100% 0% 100% + +MEAN: 26% 60% 56% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 100% 0% + 2: 0% 0% 100% 100% 0% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 100% 100% 0% 100% 0% + 6: 0% 0% 0% 0% 100% + 7: 100% 100% 0% 0% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 100% 100% + 10: 0% 0% 100% 100% 100% + 11: 0% 0% 100% 100% 100% + 12: 0% 0% 100% 100% 100% + 13: 100% 100% 0% 100% 100% + 14: 100% 100% 0% 100% 100% + 15: 100% 100% 0% 100% 100% + +MEAN: 33% 33% 60% 86% 80% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 15 cycles, 3 r regs, 800,000,000 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 1) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O2 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 rgbyNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaNe = rgbyNe.w + half(1.0/512.0); + #else + half lumaNe = rgbyNe.y + half(1.0/512.0); + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaSwNegNe = lumaSw.w - lumaNe; + #else + half lumaSwNegNe = lumaSw.y - lumaNe; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNwSw = max(lumaNw.w, lumaSw.w); + half lumaMinNwSw = min(lumaNw.w, lumaSw.w); + #else + half lumaMaxNwSw = max(lumaNw.y, lumaSw.y); + half lumaMinNwSw = min(lumaNw.y, lumaSw.y); + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half dirZ = lumaNw.w + lumaSwNegNe; + half dirX = -lumaNw.w + lumaSwNegNe; + #else + half dirZ = lumaNw.y + lumaSwNegNe; + half dirX = -lumaNw.y + lumaSwNegNe; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half3 dir; + dir.y = 0.0; + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x = lumaSe.w + dirX; + dir.z = -lumaSe.w + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.w); + #else + dir.x = lumaSe.y + dirX; + dir.z = -lumaSe.y + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir1_pos; + dir1_pos.xy = normalize(dir).xz; + half dirAbsMinTimes8 = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (7) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimes8, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNeSe = max(lumaNe, lumaSe.w); + #else + half lumaMaxNeSe = max(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (8) + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half lumaMax = max(lumaMaxNwSw, lumaMaxNeSe); + half lumaMin = min(lumaMinNwSw, lumaMinNeSe); +/*--------------------------------------------------------------------------*/ +// (9) + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgbyM = h4tex2Dlod(tex, half4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxM = max(lumaMax, rgbyM.w); + half lumaMinM = min(lumaMin, rgbyM.w); + #else + half lumaMaxM = max(lumaMax, rgbyM.y); + half lumaMinM = min(lumaMin, rgbyM.y); + #endif +/*--------------------------------------------------------------------------*/ +// (11) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + half lumaRangeM = (lumaMaxM - lumaMinM) / FXAA_CONSOLE__PS3_EDGE_THRESHOLD; +/*--------------------------------------------------------------------------*/ +// (12) + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (13) + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (14) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif + bool earlyExit = lumaRangeM < lumaMax; + bool twoTap = twoTapLt || twoTapGt; +/*--------------------------------------------------------------------------*/ +// (15) + if(twoTap) rgby2 = rgby1; + if(earlyExit) rgby2 = rgbyM; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif + + + + + + + + + + +in vec4 pos; + +uniform sampler2D tex0; +uniform vec2 dt; + +void main() { + qgl_FragColor = FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + pos.xy, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + vec4(0), + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + tex0, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + tex0, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + tex0, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + dt, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x___} = -N/screenWidthInPixels + // {_y__} = -N/screenHeightInPixels + // {__z_} = N/screenWidthInPixels + // {___w} = N/screenHeightInPixels + vec4(0), + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x___} = -2.0/screenWidthInPixels + // {_y__} = -2.0/screenHeightInPixels + // {__z_} = 2.0/screenWidthInPixels + // {___w} = 2.0/screenHeightInPixels + vec4(0), + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x___} = 8.0/screenWidthInPixels + // {_y__} = 8.0/screenHeightInPixels + // {__z_} = -4.0/screenWidthInPixels + // {___w} = -4.0/screenHeightInPixels + vec4(0), + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + 0.75, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + 0.125, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + 0.0625, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + 0., + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + 0., + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + 0., + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + vec4(0)); +} diff --git a/shaders/fxaa.vert b/shaders/fxaa.vert new file mode 100644 index 0000000..02c3d70 --- /dev/null +++ b/shaders/fxaa.vert @@ -0,0 +1,11 @@ +out vec4 pos; + +const float FXAA_SUBPIX_SHIFT = 1. / 4.; +uniform vec2 dt; + +void main(void) { + qgl_FragTexture = qgl_Texture; + pos.xy = qgl_Texture.xy; + pos.zw = qgl_Texture.xy - (dt * (0.5 + FXAA_SUBPIX_SHIFT)); + gl_Position = qgl_ftransform(); +} diff --git a/shaders/fxaa_v3.h b/shaders/fxaa_v3.h new file mode 100644 index 0000000..633482e --- /dev/null +++ b/shaders/fxaa_v3.h @@ -0,0 +1,2048 @@ +/*============================================================================ + + + NVIDIA FXAA 3.11 by TIMOTHY LOTTES + + +------------------------------------------------------------------------------ +COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. +------------------------------------------------------------------------------ +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED +*AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA +OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR +LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, +OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE +THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +------------------------------------------------------------------------------ + INTEGRATION CHECKLIST +------------------------------------------------------------------------------ +(1.) +In the shader source, setup defines for the desired configuration. +When providing multiple shaders (for different presets), +simply setup the defines differently in multiple files. +Example, + + #define FXAA_PC 1 + #define FXAA_HLSL_5 1 + #define FXAA_QUALITY__PRESET 12 + +Or, + + #define FXAA_360 1 + +Or, + + #define FXAA_PS3 1 + +Etc. + +(2.) +Then include this file, + + #include "Fxaa3_11.h" + +(3.) +Then call the FXAA pixel shader from within your desired shader. +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. +As for FXAA 3.11 all inputs for all shaders are the same +to enable easy porting between platforms. + + return FxaaPixelShader(...); + +(4.) +Insure pass prior to FXAA outputs RGBL (see next section). +Or use, + + #define FXAA_GREEN_AS_LUMA 1 + +(5.) +Setup engine to provide the following constants +which are used in the FxaaPixelShader() inputs, + + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + +Look at the FXAA Quality FxaaPixelShader() for docs on inputs. + +(6.) +Have FXAA vertex shader run as a full screen triangle, +and output "pos" and "fxaaConsolePosPos" +such that inputs in the pixel shader provide, + + // {xy} = center of pixel + FxaaFloat2 pos, + + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + +(7.) +Insure the texture sampler(s) used by FXAA are set to bilinear filtering. + + +------------------------------------------------------------------------------ + INTEGRATION - RGBL AND COLORSPACE +------------------------------------------------------------------------------ +FXAA3 requires RGBL as input unless the following is set, + + #define FXAA_GREEN_AS_LUMA 1 + +In which case the engine uses green in place of luma, +and requires RGB input is in a non-linear colorspace. + +RGB should be LDR (low dynamic range). +Specifically do FXAA after tonemapping. + +RGB data as returned by a texture fetch can be non-linear, +or linear when FXAA_GREEN_AS_LUMA is not set. +Note an "sRGB format" texture counts as linear, +because the result of a texture fetch is linear data. +Regular "RGBA8" textures in the sRGB colorspace are non-linear. + +If FXAA_GREEN_AS_LUMA is not set, +luma must be stored in the alpha channel prior to running FXAA. +This luma should be in a perceptual space (could be gamma 2.0). +Example pass before FXAA where output is gamma 2.0 encoded, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma + return color; + +Another example where output is linear encoded, +say for instance writing to an sRGB formated render target, +where the render target does the conversion back to sRGB after blending, + + color.rgb = ToneMap(color.rgb); // linear color output + return color; + +To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma + return color; + +Getting luma correct is required for the algorithm to work correctly. + + +------------------------------------------------------------------------------ + BEING LINEARLY CORRECT? +------------------------------------------------------------------------------ +Applying FXAA to a framebuffer with linear RGB color will look worse. +This is very counter intuitive, but happends to be true in this case. +The reason is because dithering artifacts will be more visiable +in a linear colorspace. + + +------------------------------------------------------------------------------ + COMPLEX INTEGRATION +------------------------------------------------------------------------------ +Q. What if the engine is blending into RGB before wanting to run FXAA? + +A. In the last opaque pass prior to FXAA, + have the pass write out luma into alpha. + Then blend into RGB only. + FXAA should be able to run ok + assuming the blending pass did not any add aliasing. + This should be the common case for particles and common blending passes. + +A. Or use FXAA_GREEN_AS_LUMA. + +============================================================================*/ + +/*============================================================================ + + INTEGRATION KNOBS + +============================================================================*/ +// +// FXAA_PS3 and FXAA_360 choose the console algorithm (FXAA3 CONSOLE). +// FXAA_360_OPT is a prototype for the new optimized 360 version. +// +// 1 = Use API. +// 0 = Don't use API. +// +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PS3 + #define FXAA_PS3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360 + #define FXAA_360 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_360_OPT + #define FXAA_360_OPT 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_PC + // + // FXAA Quality + // The high quality PC algorithm. + // + #define FXAA_PC 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PC_CONSOLE + // + // The console algorithm for PC is included + // for developers targeting really low spec machines. + // Likely better to just run FXAA_PC, and use a really low preset. + // + #define FXAA_PC_CONSOLE 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_3 + #define FXAA_HLSL_3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_4 + #define FXAA_HLSL_4 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_5 + #define FXAA_HLSL_5 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_GREEN_AS_LUMA + // + // For those using non-linear color, + // and either not able to get luma in alpha, or not wanting to, + // this enables FXAA to run using green as a proxy for luma. + // So with this enabled, no need to pack luma in alpha. + // + // This will turn off AA on anything which lacks some amount of green. + // Pure red and blue or combination of only R and B, will get no AA. + // + // Might want to lower the settings for both, + // fxaaConsoleEdgeThresholdMin + // fxaaQualityEdgeThresholdMin + // In order to insure AA does not get turned off on colors + // which contain a minor amount of green. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_GREEN_AS_LUMA 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_EARLY_EXIT + // + // Controls algorithm's early exit path. + // On PS3 turning this ON adds 2 cycles to the shader. + // On 360 turning this OFF adds 10ths of a millisecond to the shader. + // Turning this off on console will result in a more blurry image. + // So this defaults to on. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_EARLY_EXIT 1 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_DISCARD + // + // Only valid for PC OpenGL currently. + // Probably will not work when FXAA_GREEN_AS_LUMA = 1. + // + // 1 = Use discard on pixels which don't need AA. + // For APIs which enable concurrent TEX+ROP from same surface. + // 0 = Return unchanged color on pixels which don't need AA. + // + #define FXAA_DISCARD 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_FAST_PIXEL_OFFSET + // + // Used for GLSL 120 only. + // + // 1 = GL API supports fast pixel offsets + // 0 = do not use fast pixel offsets + // + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GATHER4_ALPHA + // + // 1 = API supports gather4 on alpha channel. + // 0 = API does not support gather4 on alpha channel. + // + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif +#endif + +/*============================================================================ + FXAA CONSOLE PS3 - TUNING KNOBS +============================================================================*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_SHARPNESS + // + // Consoles the sharpness of edges on PS3 only. + // Non-PS3 tuning is done with shader input. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 8.0 is sharper + // 4.0 is softer + // 2.0 is really soft (good for vector graphics inputs) + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 8.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 4.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 2.0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_CONSOLE__PS3_EDGE_THRESHOLD + // + // Only effects PS3. + // Non-PS3 tuning is done with shader input. + // + // The minimum amount of local contrast required to apply algorithm. + // The console setting has a different mapping than the quality setting. + // + // This only applies when FXAA_EARLY_EXIT is 1. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 0.25 and 0.125. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 0.125 leaves less aliasing, but is softer + // 0.25 leaves more aliasing, and is sharper + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.125 + #else + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.25 + #endif +#endif + +/*============================================================================ + FXAA QUALITY - TUNING KNOBS +------------------------------------------------------------------------------ +NOTE the other tuning knobs are now in the shader function inputs! +============================================================================*/ +#ifndef FXAA_QUALITY__PRESET + // + // Choose the quality preset. + // This needs to be compiled into the shader as it effects code. + // Best option to include multiple presets is to + // in each shader define the preset, then include this file. + // + // OPTIONS + // ----------------------------------------------------------------------- + // 10 to 15 - default medium dither (10=fastest, 15=highest quality) + // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) + // 39 - no dither, very expensive + // + // NOTES + // ----------------------------------------------------------------------- + // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) + // 13 = about same speed as FXAA 3.9 and better than 12 + // 23 = closest to FXAA 3.9 visually and performance wise + // _ = the lowest digit is directly related to performance + // _ = the highest digit is directly related to style + // + #define FXAA_QUALITY__PRESET 12 +#endif + + +/*============================================================================ + + FXAA QUALITY - PRESETS + +============================================================================*/ + +/*============================================================================ + FXAA QUALITY - MEDIUM DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 10) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 3.0 + #define FXAA_QUALITY__P2 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 11) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 3.0 + #define FXAA_QUALITY__P3 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 12) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 4.0 + #define FXAA_QUALITY__P4 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 13) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 4.0 + #define FXAA_QUALITY__P5 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 14) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 4.0 + #define FXAA_QUALITY__P6 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 15) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 12.0 +#endif + +/*============================================================================ + FXAA QUALITY - LOW DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 20) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 2.0 + #define FXAA_QUALITY__P2 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 21) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 22) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 23) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 24) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 3.0 + #define FXAA_QUALITY__P6 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 25) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 26) + #define FXAA_QUALITY__PS 9 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 4.0 + #define FXAA_QUALITY__P8 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 27) + #define FXAA_QUALITY__PS 10 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 4.0 + #define FXAA_QUALITY__P9 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 28) + #define FXAA_QUALITY__PS 11 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 4.0 + #define FXAA_QUALITY__P10 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 29) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + +/*============================================================================ + FXAA QUALITY - EXTREME QUALITY +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 39) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.0 + #define FXAA_QUALITY__P2 1.0 + #define FXAA_QUALITY__P3 1.0 + #define FXAA_QUALITY__P4 1.0 + #define FXAA_QUALITY__P5 1.5 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + + + +/*============================================================================ + + API PORTING + +============================================================================*/ +#if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D +#else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_120 == 1) + // Requires, + // #version 120 + // And at least, + // #extension GL_EXT_gpu_shader4 : enable + // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_130 == 1) + // Requires "#version 130" or better + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_3 == 1) || (FXAA_360 == 1) || (FXAA_PS3 == 1) + #define FxaaInt2 float2 + #define FxaaTex sampler2D + #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) + #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_4 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_5 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) +#endif + + +/*============================================================================ + GREEN AS LUMA OPTION SUPPORT FUNCTION +============================================================================*/ +#if (FXAA_GREEN_AS_LUMA == 0) + //FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return dot(rgba.rgb, FxaaFloat3(0.299, 0.587, 0.114)); } +#else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +#endif + + + + +/*============================================================================ + + FXAA3 QUALITY - PC + +============================================================================*/ +#if (FXAA_PC == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + FxaaTex fxaaConsole360TexExpBiasNegOne, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + FxaaTex fxaaConsole360TexExpBiasNegTwo, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x___} = -N/screenWidthInPixels + // {_y__} = -N/screenHeightInPixels + // {__z_} = N/screenWidthInPixels + // {___w} = N/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt, + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x___} = -2.0/screenWidthInPixels + // {_y__} = -2.0/screenHeightInPixels + // {__z_} = 2.0/screenWidthInPixels + // {___w} = 2.0/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x___} = 8.0/screenWidthInPixels + // {_y__} = 8.0/screenHeightInPixels + // {__z_} = -4.0/screenWidthInPixels + // {___w} = -4.0/screenHeightInPixels + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + FxaaFloat fxaaConsoleEdgeSharpness, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + FxaaFloat fxaaConsoleEdgeThreshold, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaConsoleEdgeThresholdMin, + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif +/*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif +} +/*==========================================================================*/ +#endif + + + + +/*============================================================================ + + FXAA3 CONSOLE - PC VERSION + +------------------------------------------------------------------------------ +Instead of using this on PC, I'd suggest just using FXAA Quality with + #define FXAA_QUALITY__PRESET 10 +Or + #define FXAA_QUALITY__PRESET 20 +Either are higher qualilty and almost as fast as this on modern PC GPUs. +============================================================================*/ +#if (FXAA_PC_CONSOLE == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy)); + FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw)); + FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy)); + FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw)); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy); + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat lumaM = rgbyM.w; + #else + FxaaFloat lumaM = rgbyM.y; + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw); + lumaNe += 1.0/384.0; + FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe); + FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw); + FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw); +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaMinM = min(lumaMin, lumaM); + FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled); + FxaaFloat lumaMaxM = max(lumaMax, lumaM); + FxaaFloat dirSwMinusNe = lumaSw - lumaNe; + FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM; + FxaaFloat dirSeMinusNw = lumaSe - lumaNw; + if(lumaMaxSubMinM < lumaMaxScaledClamped) return rgbyM; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir; + dir.x = dirSwMinusNe + dirSeMinusNw; + dir.y = dirSwMinusNe - dirSeMinusNw; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 dir1 = normalize(dir.xy); + FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw); + FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness; + FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw); + FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw); +/*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyA = rgbyN1 + rgbyP1; + FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25); +/*--------------------------------------------------------------------------*/ + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax); + #else + FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax); + #endif + if(twoTap) rgbyB.xyz = rgbyA.xyz * 0.5; + return rgbyB; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - 360 PIXEL SHADER + +------------------------------------------------------------------------------ +This optimized version thanks to suggestions from Andy Luedke. +Should be fully tex bound in all cases. +As of the FXAA 3.11 release, I have still not tested this code, +however I fixed a bug which was in both FXAA 3.9 and FXAA 3.10. +And note this is replacing the old unoptimized version. +If it does not work, please let me know so I can fix it. +============================================================================*/ +#if (FXAA_360 == 1) +/*--------------------------------------------------------------------------*/ +[reduceTempRegUsage(4)] +float4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + float4 lumaNwNeSwSe; + #if (FXAA_GREEN_AS_LUMA == 0) + asm { + tfetch2D lumaNwNeSwSe.w___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._w__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__w_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___w, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #else + asm { + tfetch2D lumaNwNeSwSe.y___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._y__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__y_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___y, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #endif +/*--------------------------------------------------------------------------*/ + lumaNwNeSwSe.y += 1.0/384.0; + float2 lumaMinTemp = min(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float2 lumaMaxTemp = max(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float lumaMin = min(lumaMinTemp.x, lumaMinTemp.y); + float lumaMax = max(lumaMaxTemp.x, lumaMaxTemp.y); +/*--------------------------------------------------------------------------*/ + float4 rgbyM = tex2Dlod(tex, float4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + float lumaMinM = min(lumaMin, rgbyM.w); + float lumaMaxM = max(lumaMax, rgbyM.w); + #else + float lumaMinM = min(lumaMin, rgbyM.y); + float lumaMaxM = max(lumaMax, rgbyM.y); + #endif + if((lumaMaxM - lumaMinM) < max(fxaaConsoleEdgeThresholdMin, lumaMax * fxaaConsoleEdgeThreshold)) return rgbyM; +/*--------------------------------------------------------------------------*/ + float2 dir; + dir.x = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.yyxx); + dir.y = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.xyxy); + dir = normalize(dir); +/*--------------------------------------------------------------------------*/ + float4 dir1 = dir.xyxy * fxaaConsoleRcpFrameOpt.xyzw; +/*--------------------------------------------------------------------------*/ + float4 dir2; + float dirAbsMinTimesC = min(abs(dir.x), abs(dir.y)) * fxaaConsoleEdgeSharpness; + dir2 = saturate(fxaaConsole360ConstDir.zzww * dir.xyxy / dirAbsMinTimesC + 0.5); + dir2 = dir2 * fxaaConsole360RcpFrameOpt2.xyxy + fxaaConsole360RcpFrameOpt2.zwzw; +/*--------------------------------------------------------------------------*/ + float4 rgbyN1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.xy, 0.0, 0.0)); + float4 rgbyP1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.zw, 0.0, 0.0)); + float4 rgbyN2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.xy, 0.0, 0.0)); + float4 rgbyP2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.zw, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ + float4 rgbyA = rgbyN1 + rgbyP1; + float4 rgbyB = rgbyN2 + rgbyP2 + rgbyA * 0.5; +/*--------------------------------------------------------------------------*/ + float4 rgbyR = ((FxaaLuma(rgbyB) - lumaMax) > 0.0) ? rgbyA : rgbyB; + rgbyR = ((FxaaLuma(rgbyB) - lumaMin) > 0.0) ? rgbyR : rgbyA; + return rgbyR; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (NO EARLY EXIT) + +============================================================================== +The code below does not exactly match the assembly. +I have a feeling that 12 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.z(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h0.w(TRUE), v5.xwxx, #0 + 6: addh h0.z(TRUE), -h2, h0.w + 7: texpkb h1.w(TRUE), v5, #0 + 9: addh h0.x(TRUE), h0.z, -h1.w + 10: addh h3.w(TRUE), h0.z, h1 + 11: texpkb h2.w(TRUE), v5.zwzz, #0 + 13: addh h0.z(TRUE), h3.w, -h2.w + 14: addh h0.x(TRUE), h2.w, h0 + 15: nrmh h1.xz(TRUE), h0_n + 16: minh_m8 h0.x(TRUE), |h1|, |h1.z| + 17: maxh h4.w(TRUE), h0, h1 + 18: divx h2.xy(TRUE), h1_n.xzzw, h0_n + 19: movr r1.zw(TRUE), v4.xxxy + 20: madr r2.xz(TRUE), -h1, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zzww, r1.zzww + 22: minh h5.w(TRUE), h0, h1 + 23: texpkb h0(TRUE), r2.xzxx, #0 + 25: madr r0.zw(TRUE), h1.xzxz, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w), r1 + 27: maxh h4.x(TRUE), h2.z, h2.w + 28: texpkb h1(TRUE), r0.zwzz, #0 + 30: addh_d2 h1(TRUE), h0, h1 + 31: madr r0.xy(TRUE), -h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 33: texpkb h0(TRUE), r0, #0 + 35: minh h4.z(TRUE), h2, h2.w + 36: fenct TRUE + 37: madr r1.xy(TRUE), h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 39: texpkb h2(TRUE), r1, #0 + 41: addh_d2 h0(TRUE), h0, h2 + 42: maxh h2.w(TRUE), h4, h4.x + 43: minh h2.x(TRUE), h5.w, h4.z + 44: addh_d2 h0(TRUE), h0, h1 + 45: slth h2.x(TRUE), h0.w, h2 + 46: sgth h2.w(TRUE), h0, h2 + 47: movh h0(TRUE), h0 + 48: addx.c0 rc(TRUE), h2, h2.w + 49: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB1 | add | 2: ADDh h2.z, h0.--w-, const.--x-; + | | | + 2 | SCT0/1 | mov | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB1 | add | 6: ADDh h0.z,-h2, h0.--w-; + | | | + 3 | SCT0/1 | mov | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 9: ADDh h0.x, h0.z---,-h1.w---; + | SCB1 | add | 10: ADDh h3.w, h0.---z, h1; + | | | + 4 | SCT0/1 | mov | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h2.w---, h0; + | SCB1 | add | 13: ADDh h0.z, h3.--w-,-h2.--w-; + | | | + 5 | SCT1 | mov | 15: NRMh h1.xz, h0; + | SRB | nrm | 15: NRMh h1.xz, h0; + | SCB0 | min | 16: MINh*8 h0.x, |h1|, |h1.z---|; + | SCB1 | max | 17: MAXh h4.w, h0, h1; + | | | + 6 | SCT0 | div | 18: DIVx h2.xy, h1.xz--, h0; + | SCT1 | mov | 19: MOVr r1.zw, g[TEX0].--xy; + | SCB0 | mad | 20: MADr r2.xz,-h1, const.z-w-, r1.z-w-; + | SCB1 | min | 22: MINh h5.w, h0, h1; + | | | + 7 | SCT0/1 | mov | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | TEX | txl | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | SCB0 | max | 27: MAXh h4.x, h2.z---, h2.w---; + | SCB1 | mad | 25: MADr r0.zw, h1.--xz, const, r1; + | | | + 8 | SCT0/1 | mov | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | TEX | txl | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | SCB0/1 | add | 30: ADDh/2 h1, h0, h1; + | | | + 9 | SCT0 | mad | 31: MADr r0.xy,-h2, const.xy--, r1.zw--; + | SCT1 | mov | 33: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 33: TXLr h0, r0, const.zzzz, TEX0; + | SCB1 | min | 35: MINh h4.z, h2, h2.--w-; + | | | + 10 | SCT0 | mad | 37: MADr r1.xy, h2, const.xy--, r1.zw--; + | SCT1 | mov | 39: TXLr h2, r1, const.zzzz, TEX0; + | TEX | txl | 39: TXLr h2, r1, const.zzzz, TEX0; + | SCB0/1 | add | 41: ADDh/2 h0, h0, h2; + | | | + 11 | SCT0 | min | 43: MINh h2.x, h5.w---, h4.z---; + | SCT1 | max | 42: MAXh h2.w, h4, h4.---x; + | SCB0/1 | add | 44: ADDh/2 h0, h0, h1; + | | | + 12 | SCT0 | set | 45: SLTh h2.x, h0.w---, h2; + | SCT1 | set | 46: SGTh h2.w, h0, h2; + | SCB0/1 | mul | 47: MOVh h0, h0; + | | | + 13 | SCT0 | mad | 48: ADDxc0_s rc, h2, h2.w---; + | SCB0/1 | mul | 49: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 0% 0% 50% + 6: 100% 0% 75% + 7: 0% 100% 75% + 8: 0% 100% 100% + 9: 0% 100% 25% + 10: 0% 100% 100% + 11: 50% 0% 100% + 12: 50% 0% 100% + 13: 25% 0% 100% + +MEAN: 17% 61% 67% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 0% 100% + 2: 0% 0% 100% 0% 100% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 0% 0% 0% 100% 100% + 6: 100% 100% 0% 100% 100% + 7: 0% 0% 100% 100% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 0% 100% + 10: 0% 0% 100% 100% 100% + 11: 100% 100% 0% 100% 100% + 12: 100% 100% 0% 100% 100% + 13: 100% 0% 0% 100% 100% + +MEAN: 30% 23% 61% 76% 100% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 13 cycles, 3 r regs, 923,076,923 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 0) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O3 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 dir; + half4 lumaNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + lumaNe.w += half(1.0/512.0); + dir.x = -lumaNe.w; + dir.z = -lumaNe.w; + #else + lumaNe.y += half(1.0/512.0); + dir.x = -lumaNe.y; + dir.z = -lumaNe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSw.w; + dir.z += lumaSw.w; + #else + dir.x += lumaSw.y; + dir.z += lumaSw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x -= lumaNw.w; + dir.z += lumaNw.w; + #else + dir.x -= lumaNw.y; + dir.z += lumaNw.y; + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSe.w; + dir.z -= lumaSe.w; + #else + dir.x += lumaSe.y; + dir.z -= lumaSe.y; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half4 dir1_pos; + dir1_pos.xy = normalize(dir.xyz).xz; + half dirAbsMinTimesC = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimesC, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (7) + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; +/*--------------------------------------------------------------------------*/ +// (8) + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (9) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (11) + // compilier moves these scalar ops up to other cycles + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMin = min(min(lumaNw.w, lumaSw.w), min(lumaNe.w, lumaSe.w)); + half lumaMax = max(max(lumaNw.w, lumaSw.w), max(lumaNe.w, lumaSe.w)); + #else + half lumaMin = min(min(lumaNw.y, lumaSw.y), min(lumaNe.y, lumaSe.y)); + half lumaMax = max(max(lumaNw.y, lumaSw.y), max(lumaNe.y, lumaSe.y)); + #endif + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (12) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif +/*--------------------------------------------------------------------------*/ +// (13) + if(twoTapLt || twoTapGt) rgby2 = rgby1; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif + + + +/*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (WITH EARLY EXIT) + +============================================================================== +The code mostly matches the assembly. +I have a feeling that 14 cycles is possible, but was not able to get there. +Might have to increase register count to get full performance. +Note this shader does not use perspective interpolation. + +Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + +Use of FXAA_GREEN_AS_LUMA currently adds a cycle (16 clks). +Will look at fixing this for FXAA 3.12. +------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT +------------------------------------------------------------------------------ +For reference and to aid in debug, output of NVShaderPerf should match this, + +Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.y(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h1.w(TRUE), v5.xwxx, #0 + 6: addh h0.x(TRUE), h1.w, -h2.y + 7: texpkb h2.w(TRUE), v5.zwzz, #0 + 9: minh h4.w(TRUE), h2.y, h2 + 10: maxh h5.x(TRUE), h2.y, h2.w + 11: texpkb h0.w(TRUE), v5, #0 + 13: addh h3.w(TRUE), -h0, h0.x + 14: addh h0.x(TRUE), h0.w, h0 + 15: addh h0.z(TRUE), -h2.w, h0.x + 16: addh h0.x(TRUE), h2.w, h3.w + 17: minh h5.y(TRUE), h0.w, h1.w + 18: nrmh h2.xz(TRUE), h0_n + 19: minh_m8 h2.w(TRUE), |h2.x|, |h2.z| + 20: divx h4.xy(TRUE), h2_n.xzzw, h2_n.w + 21: movr r1.zw(TRUE), v4.xxxy + 22: maxh h2.w(TRUE), h0, h1 + 23: fenct TRUE + 24: madr r0.xy(TRUE), -h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 26: texpkb h0(TRUE), r0, #0 + 28: maxh h5.x(TRUE), h2.w, h5 + 29: minh h5.w(TRUE), h5.y, h4 + 30: madr r1.xy(TRUE), h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 32: texpkb h2(TRUE), r1, #0 + 34: addh_d2 h2(TRUE), h0, h2 + 35: texpkb h1(TRUE), v4, #0 + 37: maxh h5.y(TRUE), h5.x, h1.w + 38: minh h4.w(TRUE), h1, h5 + 39: madr r0.xy(TRUE), -h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 41: texpkb h0(TRUE), r0, #0 + 43: addh_m8 h5.z(TRUE), h5.y, -h4.w + 44: madr r2.xy(TRUE), h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 46: texpkb h3(TRUE), r2, #0 + 48: addh_d2 h0(TRUE), h0, h3 + 49: addh_d2 h3(TRUE), h0, h2 + 50: movh h0(TRUE), h3 + 51: slth h3.x(TRUE), h3.w, h5.w + 52: sgth h3.w(TRUE), h3, h5.x + 53: addx.c0 rc(TRUE), h3.x, h3 + 54: slth.c0 rc(TRUE), h5.z, h5 + 55: movh h0(c0.NE.w), h2 + 56: movh h0(c0.NE.x), h1 + +IPU0 ------ Simplified schedule: -------- +Pass | Unit | uOp | PC: Op +-----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB0 | add | 2: ADDh h2.y, h0.-w--, const.-x--; + | | | + 2 | SCT0/1 | mov | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB0 | add | 6: ADDh h0.x, h1.w---,-h2.y---; + | | | + 3 | SCT0/1 | mov | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | max | 10: MAXh h5.x, h2.y---, h2.w---; + | SCB1 | min | 9: MINh h4.w, h2.---y, h2; + | | | + 4 | SCT0/1 | mov | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h0.w---, h0; + | SCB1 | add | 13: ADDh h3.w,-h0, h0.---x; + | | | + 5 | SCT0 | mad | 16: ADDh h0.x, h2.w---, h3.w---; + | SCT1 | mad | 15: ADDh h0.z,-h2.--w-, h0.--x-; + | SCB0 | min | 17: MINh h5.y, h0.-w--, h1.-w--; + | | | + 6 | SCT1 | mov | 18: NRMh h2.xz, h0; + | SRB | nrm | 18: NRMh h2.xz, h0; + | SCB1 | min | 19: MINh*8 h2.w, |h2.---x|, |h2.---z|; + | | | + 7 | SCT0 | div | 20: DIVx h4.xy, h2.xz--, h2.ww--; + | SCT1 | mov | 21: MOVr r1.zw, g[TEX0].--xy; + | SCB1 | max | 22: MAXh h2.w, h0, h1; + | | | + 8 | SCT0 | mad | 24: MADr r0.xy,-h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 26: TXLr h0, r0, const.xxxx, TEX0; + | TEX | txl | 26: TXLr h0, r0, const.xxxx, TEX0; + | SCB0 | max | 28: MAXh h5.x, h2.w---, h5; + | SCB1 | min | 29: MINh h5.w, h5.---y, h4; + | | | + 9 | SCT0 | mad | 30: MADr r1.xy, h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 32: TXLr h2, r1, const.xxxx, TEX0; + | TEX | txl | 32: TXLr h2, r1, const.xxxx, TEX0; + | SCB0/1 | add | 34: ADDh/2 h2, h0, h2; + | | | + 10 | SCT0/1 | mov | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | TEX | txl | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | SCB0 | max | 37: MAXh h5.y, h5.-x--, h1.-w--; + | SCB1 | min | 38: MINh h4.w, h1, h5; + | | | + 11 | SCT0 | mad | 39: MADr r0.xy,-h4, const.xy--, r1.zw--; + | SCT1 | mov | 41: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 41: TXLr h0, r0, const.zzzz, TEX0; + | SCB0 | mad | 44: MADr r2.xy, h4, const.xy--, r1.zw--; + | SCB1 | add | 43: ADDh*8 h5.z, h5.--y-,-h4.--w-; + | | | + 12 | SCT0/1 | mov | 46: TXLr h3, r2, const.xxxx, TEX0; + | TEX | txl | 46: TXLr h3, r2, const.xxxx, TEX0; + | SCB0/1 | add | 48: ADDh/2 h0, h0, h3; + | | | + 13 | SCT0/1 | mad | 49: ADDh/2 h3, h0, h2; + | SCB0/1 | mul | 50: MOVh h0, h3; + | | | + 14 | SCT0 | set | 51: SLTh h3.x, h3.w---, h5.w---; + | SCT1 | set | 52: SGTh h3.w, h3, h5.---x; + | SCB0 | set | 54: SLThc0 rc, h5.z---, h5; + | SCB1 | add | 53: ADDxc0_s rc, h3.---x, h3; + | | | + 15 | SCT0/1 | mul | 55: MOVh h0(NE0.wwww), h2; + | SCB0/1 | mul | 56: MOVh h0(NE0.xxxx), h1; + +Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 50% 0% 25% + 6: 0% 0% 25% + 7: 100% 0% 25% + 8: 0% 100% 50% + 9: 0% 100% 100% + 10: 0% 100% 50% + 11: 0% 100% 75% + 12: 0% 100% 100% + 13: 100% 0% 100% + 14: 50% 0% 50% + 15: 100% 0% 100% + +MEAN: 26% 60% 56% + +Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 100% 0% + 2: 0% 0% 100% 100% 0% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 100% 100% 0% 100% 0% + 6: 0% 0% 0% 0% 100% + 7: 100% 100% 0% 0% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 100% 100% + 10: 0% 0% 100% 100% 100% + 11: 0% 0% 100% 100% 100% + 12: 0% 0% 100% 100% 100% + 13: 100% 100% 0% 100% 100% + 14: 100% 100% 0% 100% 100% + 15: 100% 100% 0% 100% 100% + +MEAN: 33% 33% 60% 86% 80% +Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 +Results 15 cycles, 3 r regs, 800,000,000 pixels/s +============================================================================*/ +#if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 1) +/*--------------------------------------------------------------------------*/ +#pragma regcount 7 +#pragma disablepc all +#pragma option O2 +#pragma option OutColorPrec=fp16 +#pragma texformat default RGBA8 +/*==========================================================================*/ +half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ +// (1) + half4 rgbyNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaNe = rgbyNe.w + half(1.0/512.0); + #else + half lumaNe = rgbyNe.y + half(1.0/512.0); + #endif +/*--------------------------------------------------------------------------*/ +// (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaSwNegNe = lumaSw.w - lumaNe; + #else + half lumaSwNegNe = lumaSw.y - lumaNe; + #endif +/*--------------------------------------------------------------------------*/ +// (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNwSw = max(lumaNw.w, lumaSw.w); + half lumaMinNwSw = min(lumaNw.w, lumaSw.w); + #else + half lumaMaxNwSw = max(lumaNw.y, lumaSw.y); + half lumaMinNwSw = min(lumaNw.y, lumaSw.y); + #endif +/*--------------------------------------------------------------------------*/ +// (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half dirZ = lumaNw.w + lumaSwNegNe; + half dirX = -lumaNw.w + lumaSwNegNe; + #else + half dirZ = lumaNw.y + lumaSwNegNe; + half dirX = -lumaNw.y + lumaSwNegNe; + #endif +/*--------------------------------------------------------------------------*/ +// (5) + half3 dir; + dir.y = 0.0; + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x = lumaSe.w + dirX; + dir.z = -lumaSe.w + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.w); + #else + dir.x = lumaSe.y + dirX; + dir.z = -lumaSe.y + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (6) + half4 dir1_pos; + dir1_pos.xy = normalize(dir).xz; + half dirAbsMinTimes8 = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); +/*--------------------------------------------------------------------------*/ +// (7) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimes8, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNeSe = max(lumaNe, lumaSe.w); + #else + half lumaMaxNeSe = max(lumaNe, lumaSe.y); + #endif +/*--------------------------------------------------------------------------*/ +// (8) + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half lumaMax = max(lumaMaxNwSw, lumaMaxNeSe); + half lumaMin = min(lumaMinNwSw, lumaMinNeSe); +/*--------------------------------------------------------------------------*/ +// (9) + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (10) + half4 rgbyM = h4tex2Dlod(tex, half4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxM = max(lumaMax, rgbyM.w); + half lumaMinM = min(lumaMin, rgbyM.w); + #else + half lumaMaxM = max(lumaMax, rgbyM.y); + half lumaMinM = min(lumaMin, rgbyM.y); + #endif +/*--------------------------------------------------------------------------*/ +// (11) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + half lumaRangeM = (lumaMaxM - lumaMinM) / FXAA_CONSOLE__PS3_EDGE_THRESHOLD; +/*--------------------------------------------------------------------------*/ +// (12) + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; +/*--------------------------------------------------------------------------*/ +// (13) + rgby2 = (rgby2 + rgby1) * 0.5; +/*--------------------------------------------------------------------------*/ +// (14) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif + bool earlyExit = lumaRangeM < lumaMax; + bool twoTap = twoTapLt || twoTapGt; +/*--------------------------------------------------------------------------*/ +// (15) + if(twoTap) rgby2 = rgby1; + if(earlyExit) rgby2 = rgbyM; +/*--------------------------------------------------------------------------*/ + return rgby2; } +/*==========================================================================*/ +#endif \ No newline at end of file diff --git a/shaders/selection.glsl b/shaders/selection.glsl new file mode 100644 index 0000000..23909ca --- /dev/null +++ b/shaders/selection.glsl @@ -0,0 +1,19 @@ +// vert // + +flat out uint id, select; + +void main(void) { + id = qgl_ObjectID; + select = qgl_ObjectSelected; + gl_Position = qgl_ftransform(); +} + + +// frag // + +flat in uint id, select; + +void main(void) { + qgl_FragData[0] = unpackUnorm4x8(id); + qgl_FragData[1] = vec4(select); +} diff --git a/shaders/selection_apply.glsl b/shaders/selection_apply.glsl new file mode 100644 index 0000000..5029eba --- /dev/null +++ b/shaders/selection_apply.glsl @@ -0,0 +1,25 @@ +// vert // + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} + + +// frag // + +uniform sampler2D fb_out, fb_hover, fb_select; + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + vec2 stc = qgl_FragTexture.xy; + vec4 src = texelFetch(fb_out , tc, 0); + vec4 hov = texture(fb_hover , stc); + vec4 sel = texture(fb_select, stc); + src.rgb = clamp(src.rgb, vec3(0), vec3(1)); + src.rgb = mix(src.rgb, sel.rgb, sel.a); + src.rgb = mix(src.rgb, hov.rgb, hov.a * 0.667f); + //src.rgb = src.rgb + (sel.rgb*sel.a); + qgl_FragData[0] = src; + //qgl_FragData[0] = vec4(sel.a); +} diff --git a/shaders/selection_frame.glsl b/shaders/selection_frame.glsl new file mode 100644 index 0000000..e811faa --- /dev/null +++ b/shaders/selection_frame.glsl @@ -0,0 +1,24 @@ +// vert // + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} + + +// frag // + +uniform vec2 dt, size; +uniform float thickness; + +void main(void) { + vec2 frame_size = size / dt; + vec2 pix_pos = qgl_FragTexture * frame_size; + vec2 frame = vec2(1) + step(pix_pos, vec2(thickness)) - step(pix_pos, frame_size - vec2(thickness)); + vec2 line = frame * step(0.5, fract(pix_pos.yx / thickness / 4.)); + float fc = max(frame.x, frame.y); + float fl = max(line.x , line.y ); + fl = mix(1, fl, fc); + qgl_FragColor = vec4(vec3(fl), fc * 0.8 + 0.2); + //qgl_FragColor = vec4(vec3(fl,0),1); +} diff --git a/shaders/selection_halo.glsl b/shaders/selection_halo.glsl new file mode 100644 index 0000000..f62f74f --- /dev/null +++ b/shaders/selection_halo.glsl @@ -0,0 +1,42 @@ +// vert // + +void main(void) { + gl_Position = qgl_ftransform(); +} + + +// frag // + +uniform sampler2D fb_hover, fb_selection; +uniform vec4 hover_id, hover_color, selection_color; +uniform float hover_fill, selection_fill, has_hover, has_selection; +ivec2 tc; + +vec4 diffVector(in sampler2D map, in vec4 id) { + vec4 ds0 = abs(texelFetchOffset(map, tc, 0, ivec2(-1, 0)) - id); + vec4 ds1 = abs(texelFetchOffset(map, tc, 0, ivec2( 1, 0)) - id); + vec4 ds2 = abs(texelFetchOffset(map, tc, 0, ivec2(0, -1)) - id); + vec4 ds3 = abs(texelFetchOffset(map, tc, 0, ivec2(0, 1)) - id); + return vec4(dot(ds0, vec4(1)), dot(ds1, vec4(1)), dot(ds2, vec4(1)), dot(ds3, vec4(1))); +} +vec2 diffPair(in vec4 dv) { + const float _c = 64; + return vec2(step(1e-6, dot(dv, vec4(1))), + step(1e-3, (dv.r * _c) * (dv.g * _c) * (dv.b * _c) * (dv.a * _c))); +} +float diffAlpha(in vec2 dp, in float fill) { + return mix(dp.x - dp.y, dp.x - dp.y - dp.y + 1, fill); +} + +void main(void) { + tc = ivec2(gl_FragCoord.xy); + float hh = clamp(dot(hover_id, vec4(1)), 0, 1); + + vec2 dp = diffPair(diffVector(fb_hover, hover_id)); + float v = diffAlpha(dp, hover_fill); + qgl_FragData[0] = has_hover*hh*v*vec4(hover_color.rgb, hover_color.a); + + dp = diffPair(diffVector(fb_selection, vec4(1))); + v = diffAlpha(dp, selection_fill); + qgl_FragData[1] = has_selection*v*vec4(selection_color.rgb, selection_color.a); +} diff --git a/shaders/service_fill.glsl b/shaders/service_fill.glsl new file mode 100644 index 0000000..bc1cc8f --- /dev/null +++ b/shaders/service_fill.glsl @@ -0,0 +1,24 @@ +// vert // + +out vec3 geom_normal; +out vec4 object_color; + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); + + geom_normal = normalize(qgl_Normal * qgl_getNormalMatrix()); + object_color = qgl_ObjectColor; +} + + +// frag // + +in vec3 geom_normal; +in vec4 object_color; + +void main(void) { + vec3 normal = normalize(geom_normal); + float l = max(0, dot(normal, vec3(0,0,1))); + qgl_FragColor = object_color * (l * 0.6 + 0.4); +} diff --git a/shaders/service_frame.glsl b/shaders/service_frame.glsl new file mode 100644 index 0000000..65189c1 --- /dev/null +++ b/shaders/service_frame.glsl @@ -0,0 +1,51 @@ +// vert // + +out vec4 object_color_g; +out float mat_scale; + +void main(void) { + gl_Position = qgl_ftransform(); + object_color_g = qgl_ObjectColor; + //mat_scale = pow(qgl_ModelMatrix[0][0] * qgl_ModelMatrix[1][1] * qgl_ModelMatrix[2][2], 0.3333); + mat_scale = pow(determinant(mat3(qgl_ModelMatrix)), 0.333); + //mat3 mm = transpose(mat3(qgl_ModelMatrix)); + //mat_scale = min(min(dot(vec3(1), mm[0]), dot(vec3(1), mm[1])), dot(vec3(1), mm[2])); +} + + +// geom // + +layout (lines) in; +layout (triangle_strip, max_vertices = 4) out; + +in float mat_scale[]; +in vec4 object_color_g[]; +out vec4 object_color; + +uniform float line_width, z_offset; +uniform mat4 qgl_ProjMatrix; + +void main(void) { + object_color = object_color_g[0]; + vec2 dir = normalize(gl_in[0].gl_Position.xy - gl_in[1].gl_Position.xy); + vec2 side = cross(vec3(dir, 0), vec3(0, 0, 1)).xy; + vec4 vo = vec4(0, 0, z_offset, 0); + float dts = 0.05 * line_width * mat_scale[0]; + dir *= dts; + side *= dts; + gl_Position = gl_in[0].gl_Position + vec4((dir + side), 0, 0)*qgl_ProjMatrix + vo; EmitVertex(); + gl_Position = gl_in[0].gl_Position + vec4((dir - side), 0, 0)*qgl_ProjMatrix + vo; EmitVertex(); + gl_Position = gl_in[1].gl_Position - vec4((dir - side), 0, 0)*qgl_ProjMatrix + vo; EmitVertex(); + gl_Position = gl_in[1].gl_Position - vec4((dir + side), 0, 0)*qgl_ProjMatrix + vo; EmitVertex(); + EndPrimitive(); +} + + +// frag // + +in vec4 object_color; +uniform float scale; + +void main(void) { + qgl_FragColor = object_color; +} diff --git a/shaders/service_line.glsl b/shaders/service_line.glsl new file mode 100644 index 0000000..2cfec5d --- /dev/null +++ b/shaders/service_line.glsl @@ -0,0 +1,17 @@ +// vert // + +out vec4 object_color; + +void main(void) { + gl_Position = qgl_ftransform(); + object_color = qgl_ObjectColor; +} + + +// frag // + +in vec4 object_color; + +void main(void) { + qgl_FragColor = object_color; +} diff --git a/shaders/sum.glsl b/shaders/sum.glsl new file mode 100644 index 0000000..e12fd95 --- /dev/null +++ b/shaders/sum.glsl @@ -0,0 +1,25 @@ +// vert // + +layout(location = 1) in ivec2 qgl_Vertex; + +flat out vec4 color; + +uniform sampler2D tex; + +void main(void) { + color = texelFetch(tex, qgl_Vertex, 0); + gl_Position = vec4(0,0,0,1); +} + + +// frag // + +out vec4 qgl_FragData[gl_MaxDrawBuffers]; + +flat in vec4 color; + +uniform float pcnt; + +void main(void) { + qgl_FragData[0] = color;// / pcnt; +} diff --git a/tonemapping_proc.cpp b/tonemapping_proc.cpp new file mode 100644 index 0000000..b12b67c --- /dev/null +++ b/tonemapping_proc.cpp @@ -0,0 +1,186 @@ +/* + QGL TonemappingProc + 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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "tonemapping_proc.h" +#include "qglview.h" +#include + +using namespace QGLEngineShaders; + + +TonemappingProc::TonemappingProc(Renderer * rend): QThread(), r(rend), + fbo_1x1(r->view, 1, false, GL_RGB32F), + fbomm(r->fbo_out, Renderer::obrSum, 3), + buffer_vbo(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW) { + shader_sum = 0; + timer_delim = 0; + frame_max = cur_max = 1.; + need_render_sum = exit_ = false; + enabled = false; + timer_tone = startTimer(10); +} + + +TonemappingProc::~TonemappingProc() { + exit_ = true; + if (!wait(1000)) + terminate(); + killTimer(timer_tone); + if (shader_sum) delete shader_sum; +} + + +void TonemappingProc::init() { + QOpenGLExtraFunctions * f = r->view; + buffer_vbo.init(f); + f->glGenVertexArrays(1, &vbo_vao); + f->glBindVertexArray(vbo_vao); + buffer_vbo.bind(f); + f->glEnableVertexAttribArray(1); + f->glVertexAttribIPointer(1, 2, GL_UNSIGNED_INT, 0, 0); + buffer_vbo.release(f); + f->glBindVertexArray(0); + fbomm.lastPlane().enablePixelBuffer(); + fbo_1x1.resize(1, 1); + resize(); + if (!isRunning()) start(); +} + + +void TonemappingProc::resize() { + QOpenGLExtraFunctions * f = r->view; + fbomm.resize(); + int pcnt = fbomm.width(fbomm.lastLevel()) * fbomm.height(fbomm.lastLevel()); + QVector _data; + _data.resize(pcnt); + pcnt = -1; + for (int x = 0; x < fbomm.width(fbomm.lastLevel()); ++x) + for (int y = 0; y < fbomm.height(fbomm.lastLevel()); ++y) + _data[++pcnt] = Vector2i(x,y); + buffer_vbo.bind(f); + buffer_vbo.resize(f, _data.size() * sizeof(Vector2i)); + buffer_vbo.load(f, _data.constData(), _data.size() * sizeof(Vector2i)); +} + + +void TonemappingProc::timerEvent(QTimerEvent * e) { + if (!fbo_1x1.isInit() || !enabled) return; + if (timer_delim == 0) + need_render_sum = true; + timer_delim = (timer_delim + 1) % 10; + mutex.lock(); + float fmax = frame_max; + mutex.unlock(); + float dt = 0.01f, a = dt * 5.f, b = 1.f - a; + cur_max = fmax * a + cur_max * b; +} + + +void TonemappingProc::renderSum(Framebuffer & fbo_src, int index) { + QOpenGLExtraFunctions * f = r->view; + int pcnt = fbo_src.width() * fbo_src.height(); + fbo_src.bindColorTexture(index); + fbo_1x1.bind(); + //glClearFramebuffer(Qt::white, false); + glClearFramebuffer(); + if (shader_sum) { + if (shader_sum->isLinked()) { + if (shader_sum->bind()) { + shader_sum->setUniformValue("tex", 0); + shader_sum->setUniformValue("pcnt", float(pcnt)); + f->glBindVertexArray(vbo_vao); + f->glEnable(GL_BLEND); + f->glBlendFunc(GL_ONE, GL_ONE); + f->glBlendEquation(GL_MAX); + f->glDrawArrays(GL_POINTS, 0, pcnt); + f->glBlendEquation(GL_FUNC_ADD); + f->glDisable(GL_BLEND); + f->glBindVertexArray(0); + } + } + } + fbo_1x1.release(); + //fbo_src.bind(); +} + + +void TonemappingProc::run() { + while (!exit_) { + if (!enabled) { + msleep(100); + continue; + } + mutex.lock(); + if (last_data.isEmpty()) { + mutex.unlock(); + msleep(10); + continue; + } + QVector data = last_data; + last_data.clear(); + mutex.unlock(); + float max = calcHistogram(data); + last_max << max; + if (last_max.size() > 5) + last_max.removeAt(0); + float cm = last_max[0]; + for (int i = 1; i < last_max.size(); ++i) + cm += last_max[i]; + cm /= last_max.size(); + mutex.lock(); + frame_max = cm; + mutex.unlock(); + } +} + + +float TonemappingProc::calcHistogram(const QVector & data) { + if (data.isEmpty()) return 1.f; + float max = 0.; + QVector3D luma(0.299, 0.587, 0.114); + foreach (const QVector4D & p, data) { + float l = QVector3D::dotProduct(p.toVector3D(), luma); + max = qMax(max, l); + } + return max; +} + + +bool TonemappingProc::process() { + if (!need_render_sum || !enabled) return false; + need_render_sum = false; + Framebuffer & fbo(fbomm.lastPlane()); + if (fbo.queriedPoints() > 0) { + QVector data = fbo.getPointsFloat(); + mutex.lock(); + last_data = data; + mutex.unlock(); + } + fbomm.create(); + fbo.queryPoints(0, fbo.rect(), GL_FLOAT); + //renderSum(fbomm.plane(fbomm.lastLevel()), 0); + return true; +} + + +float TonemappingProc::frameMax() { + if (!enabled) return 1.f; + return cur_max; +} diff --git a/tonemapping_proc.h b/tonemapping_proc.h new file mode 100644 index 0000000..b6f99d5 --- /dev/null +++ b/tonemapping_proc.h @@ -0,0 +1,66 @@ +/* + QGL TonemappingProc + 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 . +*/ + +#ifndef TONEMAPPING_PROC_H +#define TONEMAPPING_PROC_H + +#include "glframebuffer_mipmap.h" +#include + + +class TonemappingProc: public QThread +{ + friend class Renderer; + friend class QGLView; +public: + TonemappingProc(Renderer * rend); + virtual ~TonemappingProc(); + + void init(); + void resize(); + void prepareSum(); + void renderSum(Framebuffer & fbo_src, int index); + + bool process(); + float frameMax(); + +protected: + void timerEvent(QTimerEvent * e); + void run() override; + float calcHistogram(const QVector & data); + +private: + Renderer * r; + QMutex mutex; + QVector last_data; + QVector last_max; + + float frame_max, cur_max; + bool need_render_sum, enabled; + std::atomic_bool exit_; + int timer_tone, timer_delim; + Framebuffer fbo_1x1; + FramebufferMipmap fbomm; + + QOpenGLShaderProgram * shader_sum; + Buffer buffer_vbo; + GLenum vbo_vao; + +}; + +#endif // RENDERER_H diff --git a/widgets/CMakeLists.txt b/widgets/CMakeLists.txt new file mode 100644 index 0000000..8055180 --- /dev/null +++ b/widgets/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.0) +project(qglengine_widgets) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +find_qt(Qt5 Core Gui Widgets) +qt_sources(SRC) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +file(GLOB PHS "*_p.h") +list(REMOVE_ITEM out_HDR "${PHS}") +import_version(${PROJECT_NAME} qglengine) +set_deploy_property(${PROJECT_NAME} SHARED + LABEL "QGLEngine widgets library" + FULLNAME "${_DOMAIN}.${PROJECT_NAME}" + COMPANY "${_COMPANY}" + INFO "QGLEngine widgets library") +make_rc(${PROJECT_NAME} _RC) +qt_add_library(${PROJECT_NAME} SHARED out_CPP ${_RC}) +qt_generate_export_header(${PROJECT_NAME}) +list(APPEND out_HDR "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_export.h") +qt_target_link_libraries(${PROJECT_NAME} qad_utils qad_widgets qglengine_core) +qt_target_include_directories(${PROJECT_NAME} PRIVATE ${QAD_INCLUDES} "${CMAKE_CURRENT_SOURCE_DIR}/../" "${CMAKE_CURRENT_SOURCE_DIR}/../core") +list(APPEND QT_MULTILIB_LIST ${PROJECT_NAME}) +copy_to_parent("") +#message(STATUS "Building ${PROJECT_NAME}") +sdk_install("qglengine" FALSE "${PROJECT_NAME}" "${out_HDR}" "${out_QM}") diff --git a/widgets/material_editor.cpp b/widgets/material_editor.cpp new file mode 100644 index 0000000..e6636fe --- /dev/null +++ b/widgets/material_editor.cpp @@ -0,0 +1,237 @@ +/* + QGL MaterialEditor + 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 . +*/ + +#include "material_editor.h" +#include "ui_material_editor.h" +#include "glmaterial.h" + + +MaterialEditor::MaterialEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::MaterialEditor(); + ui->setupUi(this); + ui->mapDiffuse ->configure(tr("Diffuse"), true); + ui->mapNormal ->configure(tr("Normal")); + ui->mapMetalness->configure(tr("Metalness")); + ui->mapRoughness->configure(tr("Roughness")); + ui->mapEmission ->configure(tr("Emission"), true); + ui->mapRelief ->configure(tr("Relief")); + ui->checkGlass->hide(); + ui->frameReflection->hide(); + ui->label_13->hide(); + mat = 0; + active = true; + ignore_next = 0; +} + + +void MaterialEditor::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void MaterialEditor::materialChanged() { + if (!active || !mat) return; + ignore_next = 2; + mat->_changed = true; + mat->color_diffuse = ui->mapDiffuse ->color(); + mat->color_emission = ui->mapEmission->color(); + mat->transparency = ui->spinTransparent->value(); + mat->reflectivity = ui->spinReflect->value(); + mat->iof = ui->spinIOF->value(); + mat->dispersion = ui->spinDispersion->value(); + mat->glass = ui->checkGlass->isChecked(); + //emit changed(); +} + + +void MaterialEditor::setMaterial(Material * m) { + if (ignore_next > 0) { + //ignore_next = false; + return; + } + active = false; + mat = m; + setEnabled(m); + if (!mat) return; + ui->mapDiffuse ->setColor(mat->color_diffuse ); + ui->mapEmission->setColor(mat->color_emission); + ui->spinTransparent->setValue(mat->transparency); + ui->spinReflect->setValue(mat->reflectivity); + ui->spinIOF->setValue(mat->iof); + ui->spinDispersion->setValue(mat->dispersion); + ui->checkGlass->setChecked(mat->glass); + ui->mapDiffuse ->setMap(&(mat->map_diffuse )); + ui->mapNormal ->setMap(&(mat->map_normal )); + ui->mapMetalness->setMap(&(mat->map_metalness)); + ui->mapRoughness->setMap(&(mat->map_roughness)); + ui->mapEmission ->setMap(&(mat->map_emission )); + ui->mapRelief ->setMap(&(mat->map_relief )); + /*ui->lineReflFront->setProperty("GLpath", mat->map_reflection.path(0)); ui->lineReflFront->setText(QFileInfo(mat->map_reflection.path(0)).fileName()); + ui->lineReflBack->setProperty("GLpath", mat->map_reflection.path(1)); ui->lineReflBack->setText(QFileInfo(mat->map_reflection.path(1)).fileName()); + ui->lineReflLeft->setProperty("GLpath", mat->map_reflection.path(2)); ui->lineReflLeft->setText(QFileInfo(mat->map_reflection.path(2)).fileName()); + ui->lineReflRight->setProperty("GLpath", mat->map_reflection.path(3)); ui->lineReflRight->setText(QFileInfo(mat->map_reflection.path(3)).fileName()); + ui->lineReflTop->setProperty("GLpath", mat->map_reflection.path(4)); ui->lineReflTop->setText(QFileInfo(mat->map_reflection.path(4)).fileName()); + ui->lineReflBottom->setProperty("GLpath", mat->map_reflection.path(5)); ui->lineReflBottom->setText(QFileInfo(mat->map_reflection.path(5)).fileName()); + */active = true; +} + +/* +Material MaterialEditor::material() { + Material m; + mat->color_diffuse = ui->colorDiffuse ->color(); + mat->color_specular = ui->colorSpecular->color(); + mat->color_emission = ui->colorEmission->color(); + mat->transparency = ui->spinTransparent->value(); + mat->reflectivity = ui->spinReflect->value(); + mat->iof = ui->spinIOF->value(); + mat->dispersion = ui->spinDispersion->value(); + mat->glass = ui->checkGlass->isChecked(); + mat->map_diffuse = ui->mapDiffuse->map(); + mat->map_normal = ui->mapNormal->map(); + mat->map_specular = ui->mapSpecular->map(); + mat->map_roughness = ui->mapRoughness->map(); + mat->map_emission = ui->mapEmission->map(); + mat->map_relief = ui->mapRelief->map(); + mat->map_reflection.setPath(0, ui->lineReflFront->property("GLpath").toString()); + mat->map_reflection.setPath(1, ui->lineReflBack->property("GLpath").toString()); + mat->map_reflection.setPath(2, ui->lineReflLeft->property("GLpath").toString()); + mat->map_reflection.setPath(3, ui->lineReflRight->property("GLpath").toString()); + mat->map_reflection.setPath(4, ui->lineReflTop->property("GLpath").toString()); + mat->map_reflection.setPath(5, ui->lineReflBottom->property("GLpath").toString()); + return m; +} +*/ + +void MaterialEditor::on_buttonReflFrontSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflFront->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflFront->setProperty("GLpath", str); + ui->lineReflFront->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBackSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflBack->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflBack->setProperty("GLpath", str); + ui->lineReflBack->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflLeftSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflLeft->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflLeft->setProperty("GLpath", str); + ui->lineReflLeft->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflRightSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflRight->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflRight->setProperty("GLpath", str); + ui->lineReflRight->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflTopSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflTop->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflTop->setProperty("GLpath", str); + ui->lineReflTop->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBottomSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflBottom->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflBottom->setProperty("GLpath", str); + ui->lineReflBottom->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflFrontClear_clicked() { + ui->lineReflFront->setText(""); + ui->lineReflFront->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBackClear_clicked() { + ui->lineReflBack->setText(""); + ui->lineReflBack->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflLeftClear_clicked() { + ui->lineReflLeft->setText(""); + ui->lineReflLeft->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflRightClear_clicked() { + ui->lineReflRight->setText(""); + ui->lineReflRight->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflTopClear_clicked() { + ui->lineReflTop->setText(""); + ui->lineReflTop->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBottomClear_clicked() { + ui->lineReflBottom->setText(""); + ui->lineReflBottom->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonLoadCubeDir_clicked() { + QString dir = QFileDialog::getExistingDirectory(this, "Select directory", ui->lineReflFront->property("GLpath").toString()); + if (dir.isEmpty()) return; + /*CubeTexture cb(0); + cb.loadPathesFromDirectory(dir); + active = false; + ui->lineReflFront->setProperty("GLpath", cb.path(0)); ui->lineReflFront->setText(QFileInfo(cb.path(0)).fileName()); + ui->lineReflBack->setProperty("GLpath", cb.path(1)); ui->lineReflBack->setText(QFileInfo(cb.path(1)).fileName()); + ui->lineReflLeft->setProperty("GLpath", cb.path(2)); ui->lineReflLeft->setText(QFileInfo(cb.path(2)).fileName()); + ui->lineReflRight->setProperty("GLpath", cb.path(3)); ui->lineReflRight->setText(QFileInfo(cb.path(3)).fileName()); + ui->lineReflTop->setProperty("GLpath", cb.path(4)); ui->lineReflTop->setText(QFileInfo(cb.path(4)).fileName()); + ui->lineReflBottom->setProperty("GLpath", cb.path(5)); ui->lineReflBottom->setText(QFileInfo(cb.path(5)).fileName());*/ + active = true; + materialChanged(); +} diff --git a/widgets/material_editor.h b/widgets/material_editor.h new file mode 100644 index 0000000..16bc29d --- /dev/null +++ b/widgets/material_editor.h @@ -0,0 +1,67 @@ +/* + QGL MaterialEditor + 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 . +*/ + +#ifndef MATERIAL_EDITOR_H +#define MATERIAL_EDITOR_H + +#include +#include "gltypes.h" + +namespace Ui { + class MaterialEditor; +} + +class MaterialEditor: public QWidget +{ + friend class MaterialsEditor; + Q_OBJECT +public: + explicit MaterialEditor(QWidget * parent = 0); + + void setMaterial(Material * m); + +protected: + void changeEvent(QEvent * e); + + bool active; + int ignore_next; + Ui::MaterialEditor * ui; + Material * mat; + +private slots: + void materialChanged(); + void on_buttonReflFrontSelect_clicked(); + void on_buttonReflFrontClear_clicked(); + void on_buttonReflBackSelect_clicked(); + void on_buttonReflBackClear_clicked(); + void on_buttonReflLeftSelect_clicked(); + void on_buttonReflLeftClear_clicked(); + void on_buttonReflRightSelect_clicked(); + void on_buttonReflRightClear_clicked(); + void on_buttonReflTopSelect_clicked(); + void on_buttonReflTopClear_clicked(); + void on_buttonReflBottomSelect_clicked(); + void on_buttonReflBottomClear_clicked(); + void on_buttonLoadCubeDir_clicked(); + +signals: + void changed(); + +}; + +#endif // MATERIAL_EDITOR_H diff --git a/widgets/material_editor.ui b/widgets/material_editor.ui new file mode 100644 index 0000000..307d6a7 --- /dev/null +++ b/widgets/material_editor.ui @@ -0,0 +1,637 @@ + + + MaterialEditor + + + + 0 + 0 + 435 + 535 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + font:normal; + + + + + + + Qt::Horizontal + + + + + + + font:normal; + + + + + + + Qt::Horizontal + + + + + + + font:normal; + + + + + + + Qt::Horizontal + + + + + + + font:normal; + + + + + + + Qt::Horizontal + + + + + + + font:normal; + + + + + + + Qt::Horizontal + + + + + + + font:normal; + + + + + + + Qt::Horizontal + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Glass + + + + + + + Transparency + + + + + + + 1.000000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + Reflectivity + + + + + + + 1.000000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + IOF + + + + + + + 2.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + Dispersion + + + + + + + 1.000000000000000 + + + 0.100000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + Reflection map + + + + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Front: + + + + + + + Back: + + + + + + + Left: + + + + + + + Right: + + + + + + + Top: + + + + + + + Bottom: + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + load from directory + + + + + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + MaterialMapEditor + QWidget +
material_map_editor.h
+ 1 + + changed() + +
+
+ + + + spinTransparent + valueChanged(double) + MaterialEditor + materialChanged() + + + 433 + 468 + + + 283 + 149 + + + + + spinReflect + valueChanged(double) + MaterialEditor + materialChanged() + + + 433 + 497 + + + 284 + 174 + + + + + spinIOF + valueChanged(double) + MaterialEditor + materialChanged() + + + 433 + 526 + + + 284 + 236 + + + + + checkGlass + toggled(bool) + MaterialEditor + materialChanged() + + + 433 + 439 + + + 284 + 84 + + + + + spinDispersion + valueChanged(double) + MaterialEditor + materialChanged() + + + 433 + 555 + + + 326 + 288 + + + + + mapDiffuse + changed() + MaterialEditor + materialChanged() + + + 421 + 69 + + + 434 + 63 + + + + + mapMetalness + changed() + MaterialEditor + materialChanged() + + + 421 + 189 + + + 434 + 143 + + + + + mapEmission + changed() + MaterialEditor + materialChanged() + + + 421 + 338 + + + 434 + 216 + + + + + mapNormal + changed() + MaterialEditor + materialChanged() + + + 421 + 129 + + + 434 + 260 + + + + + mapRelief + changed() + MaterialEditor + materialChanged() + + + 421 + 398 + + + 434 + 304 + + + + + mapRoughness + changed() + MaterialEditor + materialChanged() + + + 421 + 249 + + + 434 + 250 + + + + + + materialChanged() + +
diff --git a/widgets/material_map_editor.cpp b/widgets/material_map_editor.cpp new file mode 100644 index 0000000..3768d82 --- /dev/null +++ b/widgets/material_map_editor.cpp @@ -0,0 +1,150 @@ +/* + QGL MaterialMapEditor + 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 . +*/ + +#include "material_map_editor.h" +#include "ui_material_map_editor.h" +#include "gltexture_manager.h" +#include "glmaterial.h" + + +MaterialMapEditor::MaterialMapEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::MaterialMapEditor(); + ui->setupUi(this); + ui->widgetMap->hide(); + active = true; +} + + +void MaterialMapEditor::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void MaterialMapEditor::resizeEvent(QResizeEvent * e) { + QSize sz = preferredIconSize(6, this); + ui->iconedLabel->setFixedSize(sz); + ui->iconedLabel->setIconSize(sz); +} + + +void MaterialMapEditor::updateIcon() { + ui->iconedLabel->setIcon(QIcon(TextureManager::findFile(ui->linePath->property("GLpath").toString()))); +} + + +void MaterialMapEditor::mapChanged() { + if (!active || !map) return; + active = false; + map->use_bitmap = ui->checkMap->isChecked(); + if (ui->checkMap->isChecked()) { + map->color_amount = ui->sliderAmount->value(); + map->color_offset = ui->sliderOffset->value(); + } else { + map->color_amount = ui->sliderValue->value(); + map->color_offset = 0.f; + } + map->bitmap_scale.setX(ui->spinScaleX->value()); + map->bitmap_scale.setY(ui->spinScaleY->value()); + active = true; + emit changed(); +} + + +void MaterialMapEditor::setMap(Map * m) { + active = false; + map = m; + setEnabled(m); + if (!map) return; + ui->stackedWidget->setCurrentIndex(map->use_bitmap ? 1 : 0); + ui->checkMap->setChecked(map->use_bitmap); + ui->sliderValue->setValue(map->color_amount); + ui->sliderAmount->setValue(map->color_amount); + ui->sliderOffset->setValue(map->color_offset); + ui->spinScaleX->setValue(map->bitmap_scale.x()); + ui->spinScaleY->setValue(map->bitmap_scale.y()); + ui->linePath->setProperty("GLpath", map->bitmap_path); ui->linePath->setText(QFileInfo(map->bitmap_path).fileName()); + updateIcon(); + active = true; +} + + +void MaterialMapEditor::configure(QString title, bool has_color) { + ui->labelTitle->setText(title); + ui->colorButton->setVisible(has_color); +} + + +void MaterialMapEditor::setColor(QColor c) { + active = false; + ui->colorButton->setColor(c); + active = true; +} + + +QColor MaterialMapEditor::color() const { + return ui->colorButton->color(); +} + +/* +Map MaterialMapEditor::map() { + Map m; + map->color_amount = ui->sliderAmount->value(); + map->color_offset = ui->sliderOffset->value(); + map->bitmap_scale.setX(ui->spinScaleX->value()); + map->bitmap_scale.setY(ui->spinScaleY->value()); + map->bitmap_path = ui->linePath->property("GLpath").toString(); + return m; +} +*/ + +void MaterialMapEditor::on_buttonSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->linePath->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->linePath->setProperty("GLpath", str); + ui->linePath->setText(QFileInfo(str).fileName()); + map->setBitmapPath(str); + updateIcon(); + mapChanged(); +} + + +void MaterialMapEditor::on_buttonClear_clicked() { + ui->linePath->setText(""); + ui->linePath->setProperty("GLpath", ""); + map->clearBitmap(); + updateIcon(); + mapChanged(); +} + + +void MaterialMapEditor::on_checkMap_toggled(bool checked) { + if (checked) { + ui->sliderAmount->setValue(ui->sliderValue->value()); + resizeEvent(0); + } else + ui->sliderValue->setValue(ui->sliderAmount->value()); + ui->stackedWidget->setCurrentIndex(checked ? 1 : 0); + mapChanged(); +} diff --git a/widgets/material_map_editor.h b/widgets/material_map_editor.h new file mode 100644 index 0000000..8175247 --- /dev/null +++ b/widgets/material_map_editor.h @@ -0,0 +1,60 @@ +/* + QGL MaterialMapEditor + 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 . +*/ + +#ifndef MATERIAL_MAP_EDITOR_H +#define MATERIAL_MAP_EDITOR_H + +#include +#include "gltypes.h" + +namespace Ui { + class MaterialMapEditor; +} + +class MaterialMapEditor: public QWidget +{ + Q_OBJECT +public: + explicit MaterialMapEditor(QWidget * parent = 0); + + void setMap(Map * m); + void configure(QString title, bool has_color = false); + void setColor(QColor c); + QColor color() const; + +protected: + void changeEvent(QEvent * e); + void resizeEvent(QResizeEvent * e); + void updateIcon(); + + bool active; + Ui::MaterialMapEditor * ui; + Map * map; + +private slots: + void mapChanged(); + void on_buttonSelect_clicked(); + void on_buttonClear_clicked(); + void on_checkMap_toggled(bool checked); + +signals: + void changed(); + +}; + +#endif // MATERIAL_MAP_EDITOR_H diff --git a/widgets/material_map_editor.ui b/widgets/material_map_editor.ui new file mode 100644 index 0000000..33ca383 --- /dev/null +++ b/widgets/material_map_editor.ui @@ -0,0 +1,506 @@ + + + MaterialMapEditor + + + + 0 + 0 + 725 + 218 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + font: bold; + + + title: + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.050000000000000 + + + 0.200000000000000 + + + 0.000000000000000 + + + 1.000000000000000 + + + + + + + + + + + + + 0 + 0 + + + + Map + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + IconedLabel::RightToLeft + + + + + + + + + 2 + + + + + + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + + :/icons/document-open.png:/icons/document-open.png + + + + + + + + + Amount: + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.050000000000000 + + + 0.200000000000000 + + + -99.000000000000000 + + + 99.000000000000000 + + + + + + + Offset: + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.050000000000000 + + + 0.200000000000000 + + + -99.000000000000000 + + + 99.000000000000000 + + + + + + + + + + 0 + 0 + + + + Scale X: + + + + + + + 9999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + Scale Y: + + + + + + + 9999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 1 + 20 + + + + + + + + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+ + IconedLabel + QFrame +
iconedlabel.h
+
+
+ + + + + + + linePath + textChanged(QString) + MaterialMapEditor + mapChanged() + + + 598 + 55 + + + 99 + 73 + + + + + sliderAmount + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 655 + 87 + + + 512 + 37 + + + + + sliderOffset + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 655 + 116 + + + 511 + 65 + + + + + spinScaleX + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 491 + 146 + + + 332 + 164 + + + + + spinScaleY + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 647 + 146 + + + 493 + 164 + + + + + checkMap + toggled(bool) + sliderValue + setHidden(bool) + + + 693 + 15 + + + 626 + 14 + + + + + checkMap + toggled(bool) + widgetMap + setVisible(bool) + + + 686 + 9 + + + 695 + 62 + + + + + sliderValue + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 99 + 15 + + + 130 + 28 + + + + + colorButton + colorChanged(QColor) + MaterialMapEditor + mapChanged() + + + 60 + 11 + + + 75 + 27 + + + + + + mapChanged() + +
diff --git a/widgets/materials_editor.cpp b/widgets/materials_editor.cpp new file mode 100644 index 0000000..467e613 --- /dev/null +++ b/widgets/materials_editor.cpp @@ -0,0 +1,207 @@ +/* + QGL MaterialsEditor + 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 . +*/ + +#include "materials_editor.h" +#include "ui_materials_editor.h" +#include "material_editor.h" +#include "qglview.h" +#include "glmaterial.h" +#include +#include +#include + + +MaterialsEditor::MaterialsEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::MaterialsEditor(); + ui->setupUi(this); + ui->widgetMaterial->setMaterial(0); + ui->scrollArea->viewport()->setAutoFillBackground(false); + ui->scrollAreaWidgetContents->setAutoFillBackground(false); + view = 0; +} + + +void MaterialsEditor::assignQGLView(QGLView * v) { + view = v; + connect(view, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + connect(view, SIGNAL(materialsChanged()), this, SLOT(materialsChanged())); + connect(view, SIGNAL(materialThumbnailCreated(Material*)), this, SLOT(materialThumbnailCreated(Material*))); + materialsChanged(); +} + + +bool MaterialsEditor::event(QEvent * e) { + if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) { + QSize sz = preferredIconSize(1.5, this); + ui->comboMaterial->setIconSize(sz * 3); + ui->buttonRename->setIconSize(sz); + ui->buttonAdd ->setIconSize(sz); + ui->buttonClone ->setIconSize(sz); + ui->buttonDelete->setIconSize(sz); + ui->buttonAssign->setIconSize(sz); + ui->buttonUnset ->setIconSize(sz); + } + return QWidget::event(e); +} + + +void MaterialsEditor::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +Material * MaterialsEditor::currentMaterial() { + if (!view) return 0; + int ind = ui->comboMaterial->currentIndex(); + QVector mats = view->scene()->getMaterials(); + if (ind < 0 || ind >= mats.size()) return 0; + return mats[ind]; +} + + +void MaterialsEditor::selectMaterial(Material * m) { + if (!m) { + ui->comboMaterial->setCurrentIndex(-1); + return; + } + for (int i = 0; i < ui->comboMaterial->count(); ++i) { + if ((Material*)(ui->comboMaterial->itemData(i, Qt::UserRole + 1).value()) == m) { + ui->comboMaterial->setCurrentIndex(i); + break; + } + } +} + + +int MaterialsEditor::indexByMaterial(Material * m) { + if (!m) return -1; + for (int i = 0; i < ui->comboMaterial->count(); ++i) { + if ((Material*)(ui->comboMaterial->itemData(i, Qt::UserRole + 1).value()) == m) + return i; + } + return -1; +} + + +void MaterialsEditor::selectionChanged() { + if (!view) return; + //qDebug() << "selectionChanged"; + ObjectBase * o = view->selectedObject(); + if (o) selectMaterial(o->material()); +} + + +void MaterialsEditor::materialsChanged() { + if (ui->widgetMaterial->ignore_next > 0) { + ui->widgetMaterial->ignore_next--; + return; + } + Material * cm = currentMaterial(); + ui->comboMaterial->clear(); + if (!view) return; + QVector mats = view->scene()->getMaterials(); + for (int i = 0; i < mats.size(); ++i) { + Material * m = mats[i]; + ui->comboMaterial->addItem(QString("[%1] " + m->name).arg(i + 1), QVariant(m->name)); + ui->comboMaterial->setItemData(i, QVariant(quintptr(m)), Qt::UserRole + 1); + ui->comboMaterial->setItemIcon(i, QIcon(QPixmap::fromImage(view->materialThumbnail(m)))); + if (cm == m) ui->comboMaterial->setCurrentIndex(i); + } + +} + + +void MaterialsEditor::materialThumbnailCreated(Material * m) { + //qDebug() << "materialThumbnailCreated" << m; + int i = indexByMaterial(m); + if (i < 0 || !view) return; + //qDebug() << "materialThumbnailCreated set to" << i; + ui->comboMaterial->setItemIcon(i, QIcon(QPixmap::fromImage(view->materialThumbnail(m)))); +} + + +void MaterialsEditor::on_comboMaterial_currentIndexChanged(int index) { + ui->widgetMaterial->setMaterial(currentMaterial()); +} + + +void MaterialsEditor::on_buttonRename_clicked() { + Material * m = currentMaterial(); + if (!m) return; + QString nn = QInputDialog::getText(this, "Materials editor", "Input new name:", QLineEdit::Normal, m->name); + if (nn.isEmpty()) return; + m->name = nn; + int ind = ui->comboMaterial->currentIndex(); + ui->comboMaterial->setItemText(ind, QString("[%1] " + nn).arg(ind + 1)); + ui->comboMaterial->setItemData(ind, nn); +} + + +void MaterialsEditor::on_buttonAdd_clicked() { + if (!view) return; + Material * m = view->scene()->newMaterial(); + materialsChanged(); + selectMaterial(m); +} + + +void MaterialsEditor::on_buttonClone_clicked() { + if (!view) return; + Material * curm = currentMaterial(); + if (!curm) return; + Material * m = view->scene()->newMaterial(curm->name); + QString nm = m->name; + *m = *curm; + m->name = nm; + m->_changed = true; + materialsChanged(); + selectMaterial(m); +} + + +void MaterialsEditor::on_buttonDelete_clicked() { + if (!view) return; + Material * m = currentMaterial(); + if (!m) return; + view->scene()->removeMaterial(m); + materialsChanged(); +} + + +void MaterialsEditor::on_buttonAssign_clicked() { + if (!view) return; + Material * m = currentMaterial(); + ObjectBaseList ol = view->selectedObjects(); + foreach (ObjectBase * o, ol) + o->setMaterial(m, true); +} + + +void MaterialsEditor::on_buttonUnset_clicked() { + if (!view) return; + ObjectBaseList ol = view->selectedObjects(); + foreach (ObjectBase * o, ol) + o->setMaterial(0, true); +} diff --git a/widgets/materials_editor.h b/widgets/materials_editor.h new file mode 100644 index 0000000..5cd704d --- /dev/null +++ b/widgets/materials_editor.h @@ -0,0 +1,66 @@ +/* + QGL MaterialsEditor + 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 . +*/ + +#ifndef MATERIALS_EDITOR_H +#define MATERIALS_EDITOR_H + +#include +#include "gltypes.h" + +namespace Ui { + class MaterialsEditor; +} + +class MaterialsEditor: public QWidget +{ + Q_OBJECT +public: + explicit MaterialsEditor(QWidget * parent = 0); + + void assignQGLView(QGLView * v); + +protected: + bool event(QEvent * e); + void changeEvent(QEvent * e); + Material * currentMaterial(); + void selectMaterial(Material * m); + int indexByMaterial(Material * m); + + Ui::MaterialsEditor * ui; + QGLView * view; + Material * mat; + +private slots: + void selectionChanged(); + void materialsChanged(); + void materialThumbnailCreated(Material * m); + + void on_comboMaterial_currentIndexChanged(int index); + void on_buttonRename_clicked(); + void on_buttonAdd_clicked(); + void on_buttonClone_clicked(); + void on_buttonDelete_clicked(); + void on_buttonAssign_clicked(); + void on_buttonUnset_clicked(); + +signals: + void changed(); + +}; + +#endif // MATERIALS_EDITOR_H diff --git a/widgets/materials_editor.ui b/widgets/materials_editor.ui new file mode 100644 index 0000000..e416569 --- /dev/null +++ b/widgets/materials_editor.ui @@ -0,0 +1,251 @@ + + + MaterialsEditor + + + + 0 + 0 + 386 + 520 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 10 + + + + + + + + Unset + + + + :/icons/dialog-cancel.png:/icons/dialog-cancel.png + + + + + + + Assign + + + + :/icons/go-jump.png:/icons/go-jump.png + + + + + + + Add + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + Delete + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + Rename ... + + + + :/icons/edit-rename.png:/icons/edit-rename.png + + + + + + + Clone + + + + :/icons/edit-copy.png:/icons/edit-copy.png + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + + + + + QFrame::NoFrame + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 386 + 440 + + + + true + + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Vertical + + + + 20 + 421 + + + + + + + + + + + + + EComboBox + QComboBox +
ecombobox.h
+
+ + MaterialEditor + QWidget +
material_editor.h
+ 1 +
+
+ + + + + + + + + materialChanged() + +
diff --git a/widgets/object_editor.cpp b/widgets/object_editor.cpp new file mode 100644 index 0000000..6507ead --- /dev/null +++ b/widgets/object_editor.cpp @@ -0,0 +1,268 @@ +/* + QGL ObjectEditor + 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 . +*/ + +#include "object_editor.h" +#include "ui_object_editor.h" +#include "qglview.h" +#include +#include + + +ObjectEditor::ObjectEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::ObjectEditor(); + ui->setupUi(this); + view = 0; + active = true; + ignore_next = false; + on_comboLightType_currentIndexChanged(0); + ui->widgetMain->setEnabled(false); + ui->labelAimDist->hide(); + ui->spinAimDist->hide(); + + QObjectList ol; + ol << ui->spinPosX << ui->spinPosY << ui->spinPosZ << + ui->spinRotationX << ui->spinRotationY << ui->spinRotationZ << + ui->spinScaleX << ui->spinScaleY << ui->spinScaleZ; + foreach (QObject * o, ol) + connect(o, SIGNAL(valueChanged(double)), this, SLOT(spinChanged(double))); + + ol.clear(); + ol << ui->spinLightIntensity << ui->spinLightDecayConst << ui->spinLightDecayLinear << + ui->spinLightDecayQuadratic << ui->spinLightAngleStart << ui->spinLightAngleEnd << + ui->spinAimDist; + foreach (QObject * o, ol) + connect(o, SIGNAL(valueChanged(double)), this, SLOT(spinLightChanged(double))); + + ol.clear(); + ol << ui->spinCameraFOV << ui->spinCameraDepthStart << ui->spinCameraRoll << ui->spinAimDist; + foreach (QObject * o, ol) + connect(o, SIGNAL(valueChanged(double)), this, SLOT(spinCameraChanged(double))); + + ol.clear(); + ol << ui->checkVisible << ui->checkCastShadows << ui->checkReceiveShadows << + ui->checkAcceptLight << ui->checkAcceptFog << + ui->checkCameraMirrorX << ui->checkCameraMirrorY; + foreach (QObject * o, ol) + connect(o, SIGNAL(toggled(bool)), this, SLOT(checkChanged(bool))); + +} + + +void ObjectEditor::assignQGLView(QGLView * v) { + view = v; + connect(view, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + connect(view, SIGNAL(objectsPositionChanged()), this, SLOT(selectionChanged())); + connect(view->scene(), SIGNAL(treeChanged()), this, SLOT(selectionChanged())); + selectionChanged(); +} + + +void ObjectEditor::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void ObjectEditor::selectionChanged() { + if (ignore_next) { + ignore_next = false; + return; + } + ui->widgetMain->setEnabled(false); + if (!view) return; + ObjectBaseList sol = view->selectedObjects(true); + if (sol.isEmpty()) { + ui->labelTitle->setText(tr("[No selected]")); + return; + } + ui->widgetMain->setEnabled(true); + if (sol.size() == 1) { + setObject(sol[0]); + return; + } + bool hl = !view->selectedLights().isEmpty(), hc = !view->selectedCameras().isEmpty(); + ui->groupLight ->setVisible(hl); + ui->groupCamera ->setVisible(hc); + ui->labelAimDist->setVisible(hl || hc); + ui->spinAimDist ->setVisible(hl || hc); + ui->labelTitle->setText(tr("[%1 objects]").arg(sol.size())); +} + + +void ObjectEditor::setObject(ObjectBase * o) { + active = false; + ui->labelTitle->setText(o->name()); + ui->spinPosX->setValue(o->posX()); + ui->spinPosY->setValue(o->posY()); + ui->spinPosZ->setValue(o->posZ()); + ui->spinRotationX->setValue(o->rotationX()); + ui->spinRotationY->setValue(o->rotationY()); + ui->spinRotationZ->setValue(o->rotationZ()); + ui->spinScaleX->setValue(o->scaleX()); + ui->spinScaleY->setValue(o->scaleY()); + ui->spinScaleZ->setValue(o->scaleZ()); + ui->checkVisible->setChecked(o->isVisible()); + ui->checkAcceptLight->setChecked(o->isAcceptLight()); + ui->checkAcceptFog->setChecked(o->isAcceptFog()); + ui->checkCastShadows->setChecked(o->isCastShadows()); + ui->checkReceiveShadows->setChecked(o->isReceiveShadows()); + ui->buttonColor->setColor(o->color()); + bool is_l = o->type() == ObjectBase::glLight; + bool is_c = o->type() == ObjectBase::glCamera; + ui->labelAimDist->setVisible(is_l || is_c); + ui->spinAimDist ->setVisible(is_l || is_c); + ui->groupLight ->setVisible(is_l); + ui->groupCamera ->setVisible(is_c); + if (is_l) { + Light * l = globject_cast(o); + //bool is_dir = l->light_type == Light::Directional, is_cone = l->light_type == Light::Cone; + ui->comboLightType->setCurrentIndex(l->light_type); + ui->spinLightIntensity->setValue(l->intensity); + ui->spinLightDecayConst->setValue(l->decay_const); + ui->spinLightDecayLinear->setValue(l->decay_linear); + ui->spinLightDecayQuadratic->setValue(l->decay_quadratic); + ui->spinLightAngleStart->setValue(l->angle_start); + ui->spinLightAngleEnd->setValue(l->angle_end); + ui->spinAimDist->setValue(l->distance()); + on_comboLightType_currentIndexChanged(ui->comboLightType->currentIndex()); + } + if (is_c) { + Camera * c = globject_cast(o); + ui->checkCameraMirrorX->setChecked(c->isMirrorX()); + ui->checkCameraMirrorY->setChecked(c->isMirrorY()); + ui->spinCameraFOV->setValue(c->FOV()); + ui->spinCameraRoll->setValue(c->angleRoll()); + ui->spinCameraDepthStart->setValue(c->depthStart()); + ui->spinAimDist->setValue(c->distance()); + } + active = true; +} + + +void ObjectEditor::on_spinLightAngleStart_valueChanged(double v) { + if (ui->spinLightAngleEnd->value() < v) + ui->spinLightAngleEnd->setValue(v); +} + + +void ObjectEditor::on_spinLightAngleEnd_valueChanged(double v) { + if (ui->spinLightAngleStart->value() > v) + ui->spinLightAngleStart->setValue(v); +} + + +void ObjectEditor::on_comboLightType_currentIndexChanged(int index) { + bool ang = (index == Light::Cone); + ui->labelLightAngle ->setVisible(ang); + ui->labelLightAngleDash->setVisible(ang); + ui->spinLightAngleStart->setVisible(ang); + ui->spinLightAngleEnd ->setVisible(ang); + if (!view || !active) return; + QList sll = view->selectedLights(); + foreach (Light * o, sll) { + o->light_type = (Light::Type)index; + o->apply(); + } +} + + +void ObjectEditor::on_buttonColor_colorChanged(const QColor & v) { + if (!view || !active) return; + ObjectBaseList sol = view->selectedObjects(); + foreach (ObjectBase * o, sol) + o->setColor(v); + QList sll = view->selectedLights(); + foreach (Light * o, sll) + o->apply(); +} + + +void ObjectEditor::spinChanged(double v) { + if (!view || !active) return; + ObjectBaseList sol = view->selectedObjects(true); + QObject * s = sender(); + foreach (ObjectBase * o, sol) { + if (s == ui->spinPosX ) o->setPosX (v); + if (s == ui->spinPosY ) o->setPosY (v); + if (s == ui->spinPosZ ) o->setPosZ (v); + if (s == ui->spinRotationX) o->setRotationX(v); + if (s == ui->spinRotationY) o->setRotationY(v); + if (s == ui->spinRotationZ) o->setRotationZ(v); + if (s == ui->spinScaleX ) o->setScaleX (v); + if (s == ui->spinScaleY ) o->setScaleY (v); + if (s == ui->spinScaleZ ) o->setScaleZ (v); + } + ignore_next = true; +} + + +void ObjectEditor::spinLightChanged(double v) { + if (!view || !active) return; + QList sll = view->selectedLights(); + QObject * s = sender(); + foreach (Light * o, sll) { + if (s == ui->spinLightIntensity ) o->intensity = v; + if (s == ui->spinLightDecayConst ) o->decay_const = v; + if (s == ui->spinLightDecayLinear ) o->decay_linear = v; + if (s == ui->spinLightDecayQuadratic) o->decay_quadratic = v; + if (s == ui->spinLightAngleStart ) o->angle_start = v; + if (s == ui->spinLightAngleEnd ) o->angle_end = v; + if (s == ui->spinAimDist ) o->setDistance(v); + o->apply(); + } + ignore_next = true; +} + + +void ObjectEditor::spinCameraChanged(double v) { + if (!view || !active) return; + QList scl = view->selectedCameras(); + QObject * s = sender(); + foreach (Camera * o, scl) { + if (s == ui->spinCameraFOV ) o->setFOV (v); + if (s == ui->spinCameraRoll ) o->setAngleRoll (v); + if (s == ui->spinCameraDepthStart) o->setDepthStart(v); + if (s == ui->spinAimDist ) o->setDistance (v); + } + ignore_next = true; +} + + +void ObjectEditor::checkChanged(bool v) { + if (!view || !active) return; + ObjectBaseList sol = view->selectedObjects(); + QList scl = view->selectedCameras(); + QObject * s = sender(); + foreach (ObjectBase * o, sol) { + if (s == ui->checkVisible ) o->setVisible (v); + if (s == ui->checkCastShadows ) o->setCastShadows (v); + if (s == ui->checkReceiveShadows) o->setReceiveShadows(v); + if (s == ui->checkAcceptLight ) o->setAcceptLight (v); + if (s == ui->checkAcceptFog ) o->setAcceptFog (v); + } + foreach (Camera * o, scl) { + if (s == ui->checkCameraMirrorX ) o->setMirrorX(v); + if (s == ui->checkCameraMirrorY ) o->setMirrorY(v); + } +} diff --git a/widgets/object_editor.h b/widgets/object_editor.h new file mode 100644 index 0000000..89311f1 --- /dev/null +++ b/widgets/object_editor.h @@ -0,0 +1,62 @@ +/* + QGL ObjectEditor + 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 . +*/ + +#ifndef OBJECT_EDITOR_H +#define OBJECT_EDITOR_H + +#include +#include "globject.h" + +namespace Ui { + class ObjectEditor; +} + +class ObjectEditor: public QWidget +{ + Q_OBJECT +public: + explicit ObjectEditor(QWidget * parent = 0); + + void assignQGLView(QGLView * v); + +protected: + void changeEvent(QEvent * e); + void setObject(ObjectBase * o); + + Ui::ObjectEditor * ui; + QGLView * view; + bool active, ignore_next; + +private slots: + void selectionChanged(); + void on_spinLightAngleStart_valueChanged(double v); + void on_spinLightAngleEnd_valueChanged(double v); + + void on_comboLightType_currentIndexChanged(int index); + void on_buttonColor_colorChanged(const QColor &v); + void spinChanged(double v); + void spinLightChanged(double v); + void spinCameraChanged(double v); + void checkChanged(bool v); + +signals: + void changed(); + +}; + +#endif // OBJECT_EDITOR_H diff --git a/widgets/object_editor.ui b/widgets/object_editor.ui new file mode 100644 index 0000000..58ec410 --- /dev/null +++ b/widgets/object_editor.ui @@ -0,0 +1,782 @@ + + + ObjectEditor + + + + 0 + 0 + 466 + 778 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + font: bold; + + + [No selected] + + + Qt::AlignCenter + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Position + + + + + + + 0 + 0 + + + + X: + + + + + + + + + + + 0 + 0 + + + + Y: + + + + + + + + + + + 0 + 0 + + + + Z: + + + + + + + + + + + + + Rotation + + + + + + + 0 + 0 + + + + X: + + + + + + + -360.000000000000000 + + + 360.000000000000000 + + + 1 + + + 4.500000000000000 + + + 90.000000000000000 + + + ° + + + QSlider::TicksAbove + + + 90 + + + + + + + + 0 + 0 + + + + Y: + + + + + + + -360.000000000000000 + + + 360.000000000000000 + + + 4.500000000000000 + + + 90.000000000000000 + + + ° + + + QSlider::TicksAbove + + + 90 + + + + + + + + 0 + 0 + + + + Z: + + + + + + + -360.000000000000000 + + + 360.000000000000000 + + + 4.500000000000000 + + + 90.000000000000000 + + + ° + + + QSlider::TicksAbove + + + 90 + + + + + + + + + + Scale + + + + + + + 0 + 0 + + + + X: + + + + + + + + + + + 0 + 0 + + + + Y: + + + + + + + + + + + 0 + 0 + + + + Z: + + + + + + + + + + + + + + + Accept fog + + + + + + + Accept light + + + + + + + Visible + + + + + + + Cast shadows + + + + + + + Receive shadows + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 0 + 0 + + + + Color: + + + + + + + + + + + 0 + 0 + + + + Aim distance: + + + + + + + + + + + + Light + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Type: + + + + + + + + Omni + + + + + Cone + + + + + Directional + + + + + + + + Intensity: + + + + + + + 0.000000000000000 + + + 10.000000000000000 + + + 1.000000000000000 + + + 3 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + 999.000000000000000 + + + + + + + Decay ^0: + + + + + + + 0.000000000000000 + + + 10.000000000000000 + + + 1.000000000000000 + + + 3 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + 999.000000000000000 + + + + + + + Decay ^1: + + + + + + + 0.000000000000000 + + + 10.000000000000000 + + + 0.000000000000000 + + + 3 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + 999.000000000000000 + + + + + + + Decay ^2: + + + + + + + 0.000000000000000 + + + 10.000000000000000 + + + 0.000000000000000 + + + 3 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + 999.000000000000000 + + + + + + + Angle: + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0.000000000000000 + + + 180.000000000000000 + + + 1 + + + 5.000000000000000 + + + 30.000000000000000 + + + ° + + + + + + + - + + + + + + + 0.000000000000000 + + + 180.000000000000000 + + + 1 + + + 5.000000000000000 + + + 30.000000000000000 + + + ° + + + + + + + + + + + + + Camera + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Depth start: + + + + + + + FOV: + + + + + + + 1.000000000000000 + + + 179.900000000000006 + + + 60.000000000000000 + + + 1 + + + 5.000000000000000 + + + 30.000000000000000 + + + ° + + + + + + + + + Mirror Y + + + + + + + Mirror X + + + + + + + + + 5 + + + 999999999.000000000000000 + + + 0.100000000000000 + + + + + + + -180.000000000000000 + + + 180.000000000000000 + + + 0.000000000000000 + + + 1 + + + 5.000000000000000 + + + 30.000000000000000 + + + ° + + + + + + + Roll: + + + + + + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+ + ScrollSpinBox + QWidget +
scroll_spin_box.h
+
+
+ + + + objectChanged() + +
diff --git a/widgets/primitiveeditor.cpp b/widgets/primitiveeditor.cpp new file mode 100644 index 0000000..bfa3299 --- /dev/null +++ b/widgets/primitiveeditor.cpp @@ -0,0 +1,259 @@ +/* + QGL PrimitiveEditor + 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 . +*/ + +#include "primitiveeditor.h" +#include "ui_primitiveeditor.h" +#include +#include "glprimitives.h" +#include "glmesh.h" + + +PrimitiveEditor::PrimitiveEditor(QWidget *parent) : QWidget(parent), ui(new Ui::PrimitiveEditor) { + view = 0; + can_replace = false; + ui->setupUi(this); +#if QT_VERSION >= QT_VERSION_CHECK(5,12,0) + ui->spinSegments->setStepType(QAbstractSpinBox::AdaptiveDecimalStepType); + ui->spinSegments2->setStepType(QAbstractSpinBox::AdaptiveDecimalStepType); +#endif + editors[Plane] << ui->widgetWidth; + editors[Plane] << ui->widgetLength; + editors[Cube] << ui->widgetWidth; + editors[Cube] << ui->widgetLength; + editors[Cube] << ui->widgetHeight; + editors[Ellipsoid] << ui->widgetRadius1; + editors[Ellipsoid] << ui->widgetSegments; + editors[Ellipsoid] << ui->widgetSegments2; + editors[Ellipsoid] << ui->widgetAngle; + editors[Disc] << ui->widgetRadius1; + editors[Disc] << ui->widgetSegments; + editors[Disc] << ui->widgetAngle; + editors[Cone] << ui->widgetRadius1; + editors[Cone] << ui->widgetHeight; + editors[Cone] << ui->widgetSegments; + editors[Cylinder] << ui->widgetRadius1; + editors[Cylinder] << ui->widgetHeight; + editors[Cylinder] << ui->widgetSegments; + editors[Cylinder] << ui->widgetAngle; + editors[Torus] << ui->widgetRadius1; + editors[Torus] << ui->widgetRadius2; + editors[Torus] << ui->widgetSegments; + editors[Torus] << ui->widgetSegments2; + editors[Torus] << ui->widgetAngle; + QSet all; + QMetaEnum me = metaObject()->enumerator(0); + for (int i=0; icomboPrimitives->addItem(me.key(i)); + all.unite(editors[(PrimitiveType)me.value(i)].toSet()); + } + all_editors = all.toList(); + foreach (QWidget * w, all_editors) { + if (w->layout()) w->layout()->setContentsMargins(0, layout()->spacing(), 0, 0); + } + + ui->comboPrimitives->setCurrentIndex(Plane); + on_comboPrimitives_currentIndexChanged(ui->comboPrimitives->currentIndex()); +} + + +PrimitiveEditor::~PrimitiveEditor() { + delete ui; +} + + +void PrimitiveEditor::assignQGLView(QGLView * v) { + view = v; + connect(view, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + selectionChanged(); +} + + +Mesh * PrimitiveEditor::createMesh(QVariantList & params) { + Mesh * m = 0; + PrimitiveType pt = (PrimitiveType)ui->comboPrimitives->currentIndex(); + params << pt; + switch (pt) { + case Plane: + m = Primitive::plane(ui->spinWidth->value(), + ui->spinLength->value()); + params << ui->spinWidth->value() + << ui->spinLength->value(); + break; + case Cube: + m = Primitive::cube(ui->spinWidth->value(), + ui->spinLength->value(), + ui->spinHeight->value()); + params << ui->spinWidth->value() + << ui->spinLength->value() + << ui->spinHeight->value(); + break; + case Ellipsoid: + m = Primitive::ellipsoid(ui->spinSegments->value(), + ui->spinSegments2->value(), + ui->spinRadius->value(), + ui->spinAngle->value()); + params << ui->spinSegments->value() + << ui->spinSegments2->value() + << ui->spinRadius->value() + << ui->spinAngle->value(); + break; + case Disc: + m = Primitive::disc(ui->spinSegments->value(), + ui->spinRadius->value(), + ui->spinAngle->value()); + params << ui->spinSegments->value() + << ui->spinRadius->value() + << ui->spinAngle->value(); + break; + case Cone: + m = Primitive::cone(ui->spinSegments->value(), + ui->spinRadius->value(), + ui->spinHeight->value()); + params << ui->spinSegments->value() + << ui->spinRadius->value() + << ui->spinHeight->value(); + break; + case Cylinder: + m = Primitive::cylinder(ui->spinSegments->value(), + ui->spinRadius->value(), + ui->spinHeight->value(), + ui->spinAngle->value()); + params << ui->spinSegments->value() + << ui->spinRadius->value() + << ui->spinHeight->value() + << ui->spinAngle->value(); + break; + case Torus: + m = Primitive::torus(ui->spinSegments->value(), + ui->spinSegments2->value(), + ui->spinRadius->value(), + ui->spinRadius2->value(), + ui->spinAngle->value()); + params << ui->spinSegments->value() + << ui->spinSegments2->value() + << ui->spinRadius->value() + << ui->spinRadius2->value() + << ui->spinAngle->value(); + break; + default: return 0; + } + params << ui->flipNormals->isChecked(); + params << ui->colorButton->color(); + if (ui->flipNormals->isChecked()) + m->flipNormals(); + return m; +} + + +void PrimitiveEditor::showEditors() { + foreach (QWidget * w, all_editors) w->hide(); + PrimitiveType pt = (PrimitiveType)ui->comboPrimitives->currentIndex(); + QList wds = editors[pt]; + foreach (QWidget * w, wds) w->show(); +} + + +void PrimitiveEditor::selectionChanged() { + ObjectBase * so = view->selectedObject();\ + can_replace = false; + if (so) { + QVariantList vl = so->property("primitive", &can_replace).toList(); + if (can_replace && !vl.isEmpty()) { + PrimitiveType pt = (PrimitiveType)vl.takeFirst().toInt(); + ui->comboPrimitives->setCurrentIndex(pt); + switch (pt) { + case Plane: + ui->spinWidth->setValue(vl.takeFirst().toDouble()); + ui->spinLength->setValue(vl.takeFirst().toDouble()); + break; + case Cube: + ui->spinWidth->setValue(vl.takeFirst().toDouble()); + ui->spinLength->setValue(vl.takeFirst().toDouble()); + ui->spinHeight->setValue(vl.takeFirst().toDouble()); + break; + case Ellipsoid: + ui->spinSegments->setValue(vl.takeFirst().toDouble()); + ui->spinSegments2->setValue(vl.takeFirst().toDouble()); + ui->spinRadius->setValue(vl.takeFirst().toDouble()); + ui->spinAngle->setValue(vl.takeFirst().toDouble()); + break; + case Disc: + ui->spinSegments->setValue(vl.takeFirst().toDouble()); + ui->spinRadius->setValue(vl.takeFirst().toDouble()); + ui->spinAngle->setValue(vl.takeFirst().toDouble()); + break; + case Cone: + ui->spinSegments->setValue(vl.takeFirst().toDouble()); + ui->spinRadius->setValue(vl.takeFirst().toDouble()); + ui->spinHeight->setValue(vl.takeFirst().toDouble()); + break; + case Cylinder: + ui->spinSegments->setValue(vl.takeFirst().toDouble()); + ui->spinRadius->setValue(vl.takeFirst().toDouble()); + ui->spinHeight->setValue(vl.takeFirst().toDouble()); + ui->spinAngle->setValue(vl.takeFirst().toDouble()); + break; + case Torus: + ui->spinSegments->setValue(vl.takeFirst().toDouble()); + ui->spinSegments2->setValue(vl.takeFirst().toDouble()); + ui->spinRadius->setValue(vl.takeFirst().toDouble()); + ui->spinRadius2->setValue(vl.takeFirst().toDouble()); + ui->spinAngle->setValue(vl.takeFirst().toDouble()); + break; + } + ui->flipNormals->setChecked(vl.takeFirst().toBool()); + ui->colorButton->setColor(vl.takeFirst().value()); + } + } +} + + +void PrimitiveEditor::replaceMesh() { + if (!view) return; + if (!can_replace) return; + ObjectBase * so = view->selectedObject(); + if (!so) return; + QVariantList params; + Mesh * m = createMesh(params); + if (!m) return; + so->setMesh(m); + so->setColor(ui->colorButton->color()); + so->setName(ui->comboPrimitives->currentText()); + so->setProperty("primitive", params); + delete m; +} + + +void PrimitiveEditor::on_buttonAdd_clicked() { + if (!view) return; + QVariantList params; + Mesh * m = createMesh(params); + if (!m) return; + ObjectBase * o = new ObjectBase(m); + o->setColor(ui->colorButton->color()); + o->setName(ui->comboPrimitives->currentText()); + o->setProperty("primitive", params); + view->scene()->addObject(o); + view->scene()->selectObject(o); + delete m; +} + + +void PrimitiveEditor::on_comboPrimitives_currentIndexChanged(int index) { + showEditors(); +} diff --git a/widgets/primitiveeditor.h b/widgets/primitiveeditor.h new file mode 100644 index 0000000..7d1b5a4 --- /dev/null +++ b/widgets/primitiveeditor.h @@ -0,0 +1,68 @@ +/* + QGL PrimitiveEditor + 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 . +*/ + +#ifndef PRIMITIVEEDITOR_H +#define PRIMITIVEEDITOR_H + +#include +#include "qglview.h" + + +namespace Ui { +class PrimitiveEditor; +} + + +class PrimitiveEditor : public QWidget { + Q_OBJECT +public: + enum PrimitiveType { + Plane, + Cube, + Ellipsoid, + Disc, + Cone, + Cylinder, + Torus + }; + Q_ENUMS(PrimitiveType) + + explicit PrimitiveEditor(QWidget *parent = nullptr); + ~PrimitiveEditor(); + + void assignQGLView(QGLView * v); + +protected: + Mesh * createMesh(QVariantList & params); + void showEditors(); + + Ui::PrimitiveEditor *ui; + QGLView * view; + QMap > editors; + QList all_editors; + bool can_replace; + +private slots: + void selectionChanged(); + void replaceMesh(); + void on_buttonAdd_clicked(); + void on_comboPrimitives_currentIndexChanged(int index); +}; + + +#endif // PRIMITIVEEDITOR_H diff --git a/widgets/primitiveeditor.ui b/widgets/primitiveeditor.ui new file mode 100644 index 0000000..9238a84 --- /dev/null +++ b/widgets/primitiveeditor.ui @@ -0,0 +1,597 @@ + + + PrimitiveEditor + + + + 0 + 0 + 360 + 536 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + + + Width: + + + + + + + 1.000000000000000 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Length: + + + + + + + 1.000000000000000 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Height + + + + + + + 1.000000000000000 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Radius 1: + + + + + + + 1.000000000000000 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Radius 2: + + + + + + + 1.000000000000000 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Angle: + + + + + + + 360.000000000000000 + + + 360.000000000000000 + + + 4.500000000000000 + + + 90.000000000000000 + + + ° + + + QSlider::TicksAbove + + + 90 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Segments: + + + + + + + 1 + + + 1000 + + + 1 + + + 16 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Segments 2: + + + + + + + 1 + + + 1000 + + + 1 + + + 16 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + Color: + + + + + + + + 255 + 255 + 255 + + + + + + + + + + + + + + Flip normals + + + + + + + Qt::Vertical + + + + 20 + 176 + + + + + + + + + + Add + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+ + EComboBox + QComboBox +
ecombobox.h
+
+ + ScrollSpinBox + QWidget +
scroll_spin_box.h
+
+
+ + + + + + spinWidth + valueChanged(double) + PrimitiveEditor + replaceMesh() + + + 331 + 51 + + + 368 + 45 + + + + + spinLength + valueChanged(double) + PrimitiveEditor + replaceMesh() + + + 330 + 84 + + + 369 + 82 + + + + + spinHeight + valueChanged(double) + PrimitiveEditor + replaceMesh() + + + 334 + 116 + + + 366 + 114 + + + + + spinRadius + valueChanged(double) + PrimitiveEditor + replaceMesh() + + + 332 + 144 + + + 370 + 145 + + + + + spinRadius2 + valueChanged(double) + PrimitiveEditor + replaceMesh() + + + 334 + 186 + + + 371 + 181 + + + + + spinAngle + valueChanged(double) + PrimitiveEditor + replaceMesh() + + + 335 + 210 + + + 372 + 213 + + + + + spinSegments + valueChanged(int) + PrimitiveEditor + replaceMesh() + + + 329 + 246 + + + 370 + 247 + + + + + spinSegments2 + valueChanged(int) + PrimitiveEditor + replaceMesh() + + + 336 + 284 + + + 372 + 279 + + + + + flipNormals + toggled(bool) + PrimitiveEditor + replaceMesh() + + + 70 + 344 + + + 370 + 341 + + + + + comboPrimitives + currentIndexChanged(int) + PrimitiveEditor + replaceMesh() + + + 348 + 9 + + + 367 + 9 + + + + + colorButton + colorChanged(QColor) + PrimitiveEditor + replaceMesh() + + + 271 + 285 + + + 179 + 267 + + + + + + replaceMesh() + +
diff --git a/widgets/propertyeditor.cpp b/widgets/propertyeditor.cpp new file mode 100644 index 0000000..b4c568e --- /dev/null +++ b/widgets/propertyeditor.cpp @@ -0,0 +1,426 @@ +/* + QGL PropertyEditor + 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 . +*/ + +#include "propertyeditor.h" + + +QWidget * Delegate::widgetForProperty(QWidget * parent, const QModelIndex & index) const { + QWidget * w = 0; + int type = 0; + QVariant value = index.data(Qt::UserRole); + if (index.data(Qt::UserRole + 2).toString() == "__flags") return 0; + if (index.data(Qt::UserRole + 1).toString() == "__flag") { + qulonglong key = index.data(Qt::UserRole).toULongLong(); + value = index.parent().data(Qt::UserRole); + //QMetaProperty prop = index.parent().data(Qt::UserRole + 1).value(); + w = new QCheckBox(parent); type = 14; ((QCheckBox*)w)->setChecked(((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0)); + ((QCheckBox*)w)->setText("0x" + QString::number(key, 16).toUpper()); + connect((QCheckBox*)w, SIGNAL(clicked(bool)), this, SLOT(changedFlag())); + //qDebug() << prop.enumerator().name(); + } else { + if (value.canConvert()) { + PropertyValuePair prop = value.value(); + if (prop.first.isEnumType()) { + w = new QComboBox(parent); type = 13; ((QComboBox*)w)->setCurrentIndex(value.toInt()); + w->setProperty("__prop", QVariant::fromValue(prop.first)); + QMetaEnum menum = prop.first.enumerator(); + for (int i = 0; i < menum.keyCount(); ++i) { + ((QComboBox*)w)->addItem(QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")", menum.value(i)); + if (menum.value(i) == prop.second.toInt()) + ((QComboBox*)w)->setCurrentIndex(i); + } + connect((QComboBox*)w, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); + } + } else { + switch (value.type()) { + case QVariant::Int: w = new QSpinBox(parent); type = 2; ((QSpinBox*)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::UInt: w = new QSpinBox(parent); type = 3; ((QSpinBox*)w)->setRange(0, 0xFFFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::LongLong: w = new QSpinBox(parent); type = 4; ((QSpinBox*)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::ULongLong: w = new QSpinBox(parent); type = 5; ((QSpinBox*)w)->setRange(0, 0xFFFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::Double: w = new QDoubleSpinBox(parent); type = 6; ((QDoubleSpinBox*)w)->setRange(-999999999, 999999999); ((QDoubleSpinBox*)w)->setDecimals(3); connect((QDoubleSpinBox*)w, SIGNAL(valueChanged(double)), this, SLOT(changed())); break; + case QVariant::Bool: w = new QCheckBox(parent); type = 7; ((QCheckBox*)w)->setChecked(value.toBool()); connect((QCheckBox*)w, SIGNAL(toggled(bool)), this, SLOT(changed())); break; + case QVariant::Color: w = new ColorButton(parent); type = 8; ((ColorButton*)w)->setUseAlphaChannel(true); ((ColorButton*)w)->setColor(value.value()); connect((ColorButton*)w, SIGNAL(colorChanged(QColor)), this, SLOT(changed())); break; + case QVariant::Point: w = new QPointEdit(parent); type = 9; ((QPointEdit*)w)->setDecimals(0); ((QPointEdit*)w)->setValue(QPointF(value.toPoint())); connect((QPointEdit*)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed())); break; + case QVariant::PointF: w = new QPointEdit(parent); type = 10; ((QPointEdit*)w)->setDecimals(3); ((QPointEdit*)w)->setValue(value.toPointF()); connect((QPointEdit*)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed())); break; + case QVariant::Rect: w = new QRectEdit(parent); type = 11; ((QRectEdit*)w)->setDecimals(0); ((QRectEdit*)w)->setValue(QRectF(value.toRect())); connect((QRectEdit*)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed())); break; + case QVariant::RectF: w = new QRectEdit(parent); type = 12; ((QRectEdit*)w)->setDecimals(3); ((QRectEdit*)w)->setValue(value.toRectF()); connect((QRectEdit*)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed())); break; + case QVariant::String: default: w = new CLineEdit(parent); type = 1; ((CLineEdit*)w)->setDefaultText(value.toString()); connect((CLineEdit*)w, SIGNAL(textChanged(QString)), this, SLOT(changed())); break; + } + } + } + if (w == 0) return 0; + /*QPalette pal = w->palette(); + pal.setColor(QPalette::Window, Qt::white); + w->setPalette(pal);*/ + w->setAutoFillBackground(true); + w->setProperty("__type", type); + return w; +} + + +void Delegate::setWidgetProperty(QWidget * w, const QVariant & value) const { + if (w == 0) return; + switch (w->property("__type").toInt()) { + case 1: ((CLineEdit*)w)->setText(value.toString()); break; + case 2: case 3: case 4: case 5: ((QSpinBox*)w)->setValue(value.toInt()); break; + case 6: ((QDoubleSpinBox*)w)->setValue(value.toDouble()); break; + case 7: ((QCheckBox*)w)->setChecked(value.toBool()); break; + case 8: ((ColorButton*)w)->setColor(value.value()); break; + case 9: ((QPointEdit*)w)->setValue(value.value()); break; + case 10: ((QPointEdit*)w)->setValue(value.value()); break; + case 11: ((QRectEdit*)w)->setValue(value.value()); break; + case 12: ((QRectEdit*)w)->setValue(value.value()); break; + } +} + + +const QVariant Delegate::widgetProperty(QWidget * w) const { + if (w == 0) return QVariant(); + switch (w->property("__type").toInt()) { + case 1: return QVariant::fromValue(((CLineEdit*)w)->text()); break; + case 2: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 3: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 4: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 5: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 6: return QVariant::fromValue(((QDoubleSpinBox*)w)->value()); break; + case 7: return QVariant::fromValue(((QCheckBox*)w)->isChecked()); break; + case 8: return QVariant::fromValue(((ColorButton*)w)->color()); break; + case 9: return QVariant::fromValue(((QPointEdit*)w)->value().toPoint()); break; + case 10: return QVariant::fromValue(((QPointEdit*)w)->value()); break; + case 11: return QVariant::fromValue(((QRectEdit*)w)->value().toRect()); break; + case 12: return QVariant::fromValue(((QRectEdit*)w)->value()); break; + case 13: return QVariant::fromValue(PropertyValuePair(w->property("__prop").value(), ((QComboBox*)w)->itemData(((QComboBox*)w)->currentIndex()))); break; + default: return QVariant(); break; + } + return QVariant(); +} + + +void Delegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { + if (index.data(Qt::UserRole + 1).toString() != "__flag") + model->setData(index, widgetProperty(editor), Qt::UserRole); +} + + +void Delegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { + QStyledItemDelegate::paint(painter, option, index); + QVariant value = index.data(Qt::UserRole); + QStyle * style = QApplication::style(); + QStyleOption * so = 0; + QStyleOptionComplex * soc = 0; + QString text; + QRect rect; + QPalette::ColorRole role = (option.state.testFlag(QStyle::State_Selected) && option.state.testFlag(QStyle::State_Active) ? QPalette::HighlightedText : QPalette::WindowText); + if (index.data(Qt::UserRole + 2).toString() == "__flags") { + text = "0x" + QString::number(value.toInt(), 16).toUpper(); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + return; + } + if (index.data(Qt::UserRole + 1) == "__flag") { + qulonglong key = index.data(Qt::UserRole).toULongLong(); + value = index.parent().data(Qt::UserRole); + so = new QStyleOptionButton(); + so->rect = option.rect; + so->palette = option.palette; + so->fontMetrics = option.fontMetrics; + ((QStyleOptionButton*)so)->state = (((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0) ? QStyle::State_On : QStyle::State_Off) | option.state; + ((QStyleOptionButton*)so)->text = "0x" + QString::number(key, 16).toUpper(); + if (option.state.testFlag(QStyle::State_Selected)) + so->palette.setColor(QPalette::WindowText, so->palette.color(QPalette::HighlightedText)); + style->drawControl(QStyle::CE_CheckBox, so, painter); + } else { + if (value.canConvert()) { + PropertyValuePair prop = value.value(); + if (prop.first.isEnumType()) { + QMetaEnum menum = prop.first.enumerator(); + for (int i = 0; i < menum.keyCount(); ++i) { + if (menum.value(i) == prop.second.toInt()) { + text = QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")"; + break; + } + } + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + } + } else { + switch (value.type()) { + case QVariant::Int: + text.setNum(value.toInt()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::UInt: + text.setNum(value.toUInt()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::LongLong: + text.setNum(value.toLongLong()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::ULongLong: + text.setNum(value.toULongLong()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::Double: + text.setNum(value.toDouble(), 'f', 3); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::Bool: + so = new QStyleOptionButton(); + so->rect = option.rect; + so->state = option.state; + so->palette = option.palette; + so->fontMetrics = option.fontMetrics; + ((QStyleOptionButton*)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off) | option.state; + style->drawControl(QStyle::CE_CheckBox, so, painter); + break; + case QVariant::Color: + rect = option.rect;//style->subElementRect(QStyle::QStyle::SE_FrameContents, so); + rect.setRect(rect.x() + 3, rect.y() + 3, rect.width() - 6, rect.height() - 6); + painter->fillRect(rect, ab); + painter->fillRect(rect, value.value()); + break; + case QVariant::Point: + text = pointString(value.toPoint()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::PointF: + text = pointString(value.toPointF()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::Rect: + text = rectString(value.toRect()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::RectF: + text = rectString(value.toRectF()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::String: default: + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, value.toString()), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, value.toString(), role); + break; + } + } + } + /*so = new QStyleOptionFrame(); + so->rect = option.rect; + so->state = option.state; + so->palette = option.palette; + so->fontMetrics = option.fontMetrics; + ((QStyleOptionFrame*)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off); + style->drawPrimitive(QStyle::PE_PanelLineEdit, so, painter); + style->drawPrimitive(QStyle::PE_FrameLineEdit, so, painter); + break;*/ + if (so != 0) delete so; + if (soc != 0) delete soc; + +} + + +void Delegate::changedFlag() { + QAbstractItemModel * model = const_cast(cmi.model()); + model->setData(cmi, qobject_cast(sender())->isChecked(), Qt::UserRole + 3); + QModelIndex p = cmi.parent(), mi; + int row = 0; + qulonglong val = 0; + QList chldr; + mi = p.child(row, 1); + while (mi.isValid()) { + chldr << mi; + model->setData(mi, !mi.data(Qt::UserRole + 4).toBool(), Qt::UserRole + 4); + mi = p.child(++row, 1); + } + bool cc = cmi.data(Qt::UserRole + 3).toBool(); + qulonglong cv = cmi.data(Qt::UserRole).toULongLong(); + //qDebug() << "*****"; + if (cc && cv == 0) { + val = 0; + //qDebug() << "null" << cv; + } else { + if (!cc && cv != 0) { + //qDebug() << "uncheck" << cv; + for (int i = 0; i < chldr.size(); ++i) { + if (chldr[i] == cmi) continue; + //qDebug() << (chldr[i].data(Qt::UserRole).toULongLong() & cv); + if (chldr[i].data(Qt::UserRole).toULongLong() & cv) + model->setData(chldr[i], false, Qt::UserRole + 3); + } + } + for (int i = 0; i < chldr.size(); ++i) { + //qDebug() << chldr[i].data(Qt::UserRole + 3).toBool(); + if (chldr[i].data(Qt::UserRole + 3).toBool()) + val |= chldr[i].data(Qt::UserRole).toULongLong(); + } + } + for (int i = 0; i < chldr.size(); ++i) { + if (chldr[i] == cmi) continue; + cv = chldr[i].data(Qt::UserRole).toULongLong(); + model->setData(chldr[i], ((val & cv) == cv && cv != 0) || (val == 0 && cv == 0), Qt::UserRole + 3); + } + //qDebug() << val; + model->setData(p, val, Qt::UserRole); + model->setData(p.sibling(p.row(), 1), val, Qt::UserRole); +} + + + +PropertyEditor::PropertyEditor(QWidget * parent): QTreeWidget(parent) { + object = 0; + active_ = false; + configTree(); + connect(this, SIGNAL(itemClicked(QTreeWidgetItem * , int)), this, SLOT(itemClicked(QTreeWidgetItem * , int))); + connect(this, SIGNAL(itemChanged(QTreeWidgetItem * , int)), this, SLOT(itemChanged(QTreeWidgetItem * , int))); +} + + +PropertyEditor::~PropertyEditor() { + +} + + +void PropertyEditor::changeEvent(QEvent * e) { + QTreeWidget::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + configTree(); + return; + } +} + + +void PropertyEditor::configTree() { + setColumnCount(2); + setRootIsDecorated(false); + setColumnWidth(0, 170); + setColumnWidth(1, 10); + header()->setStretchLastSection(true); + QStringList lbls; + lbls << tr("Property") << tr("Value"); + setHeaderLabels(lbls); + setAlternatingRowColors(true); + setItemDelegateForColumn(1, new Delegate()); + +} + + +void PropertyEditor::itemClicked(QTreeWidgetItem * item, int column) { + if (column == 0) + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + else { + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + editItem(item, 1); + } +} + + +void PropertyEditor::itemChanged(QTreeWidgetItem * item, int column) { + if (!active_) return; + if (column != 1) return; + QVariant value = item->data(1, Qt::UserRole); + if (value.canConvert()) { + value = value.value().second; + } + object->setProperty(item->text(0).toLatin1(), value); +} + + +void PropertyEditor::rebuild() { + clear(); + configTree(); + if (object == 0) return; + active_ = false; + const QMetaObject * mo = object->metaObject(); + QList mol; + while (mo != 0) { + mol.push_front(mo); + mo = mo->superClass(); + } + int ps, pe; + QTreeWidgetItem * ti, * tli, * tfi; + QVariant value; +// QWidget * pw = 0; + int chue = 0; + QColor bc; + font_b = font(); + font_b.setBold(true); + foreach (const QMetaObject * o, mol) { + ps = o->propertyOffset(); + pe = o->propertyCount();// - ps; + //qDebug() << i->className() << ps << pe; + tli = new QTreeWidgetItem(); + tli->setText(0, o->className()); + tli->setFont(0, font_b); + setItemBackColor(tli, Qt::darkGray); + setItemForeColor(tli, Qt::white); + addTopLevelItem(tli); + setFirstItemColumnSpanned(tli, true); + tli->setExpanded(true); + for (int i = ps; i < pe; ++i) { + props << o->property(i); + value = o->property(i).read(object); + ti = new QTreeWidgetItem(); + ti->setSizeHint(1, QSize(20, 20)); + bc.setHsv(chue, 60, 245 + (i % 2) * 20 - 10); + setItemBackColor(ti, bc); + ti->setText(0, o->property(i).name()); + if (props.back().isFlagType()) { + QMetaEnum menum = props.back().enumerator(); + for (int j = 0; j < menum.keyCount(); ++j) { + tfi = new QTreeWidgetItem(); + tfi->setText(0, menum.key(j)); + tfi->setData(1, Qt::UserRole, menum.value(j)); + tfi->setData(1, Qt::UserRole + 1, "__flag"); + tfi->setData(1, Qt::UserRole + 2, value.toULongLong()); + tfi->setData(1, Qt::UserRole + 3, (value.toULongLong() & menum.value(j)) > 0); + tfi->setSizeHint(1, QSize(20, 20)); + bc.setHsv(chue, 60, 245 + ((i + j + 1) % 2) * 20 - 10); + setItemBackColor(tfi, bc); + ti->addChild(tfi); + } + ti->setData(0, Qt::UserRole, value); + ti->setData(1, Qt::UserRole, value); + ti->setData(1, Qt::UserRole + 2, "__flags"); + ti->setData(0, Qt::UserRole + 1, QVariant::fromValue(props.back())); + } + else if (props.back().isEnumType()) + value.setValue(PropertyValuePair(props.back(), value)); + //ti->setText(1, value.toString()); + ti->setData(1, Qt::UserRole, value); + tli->addChild(ti); + //const_cast(indexFromItem(ti, 1)).; + //if (pw != 0) setItemWidget(ti, 1, pw); + } + chue += 60; + chue %= 360; + } + active_ = true; +} + + +void PropertyEditor::refresh() { + +} diff --git a/widgets/propertyeditor.h b/widgets/propertyeditor.h new file mode 100644 index 0000000..143e32c --- /dev/null +++ b/widgets/propertyeditor.h @@ -0,0 +1,95 @@ +/* + QGL PropertyEditor + 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 . +*/ + +#ifndef PROPERTYEDITOR_H +#define PROPERTYEDITOR_H + +#include +#include +#include +#include +#include +#include +#include + + +class Delegate: public QStyledItemDelegate { + Q_OBJECT +public: + Delegate(QObject * parent = 0): QStyledItemDelegate() {ab = QBrush(QImage(":/icons/alpha.png"));} + + QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {cmi = const_cast(index); return widgetForProperty(parent, index);} + void setEditorData(QWidget * editor, const QModelIndex & index) const {setWidgetProperty(editor, index.data(Qt::UserRole));} + void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const; + void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const {editor->setGeometry(option.rect);} + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; + +private: + QWidget * widgetForProperty(QWidget * parent, const QModelIndex & index) const; + void setWidgetProperty(QWidget * w, const QVariant & value) const; + const QVariant widgetProperty(QWidget * w) const; + QString pointString(const QPoint & p) const {return QString::number(p.x()) + " x " + QString::number(p.y());} + QString pointString(const QPointF & p) const {return QString::number(p.x()) + " x " + QString::number(p.y());} + QString rectString(const QRect & r) const {return QString::number(r.x()) + " x " + QString::number(r.y()) + " : " + + QString::number(r.width()) + " x " + QString::number(r.height());} + QString rectString(const QRectF & r) const {return QString::number(r.x()) + " x " + QString::number(r.y()) + " : " + + QString::number(r.width()) + " x " + QString::number(r.height());} + + QBrush ab; + mutable QModelIndex cmi; + +private slots: + void changed() {setModelData((QWidget * )sender(), const_cast(cmi.model()), cmi);} + void changedFlag(); + +}; + +typedef QPair PropertyValuePair; +Q_DECLARE_METATYPE (PropertyValuePair) +Q_DECLARE_METATYPE (QMetaProperty) + +class PropertyEditor: public QTreeWidget { + Q_OBJECT +public: + explicit PropertyEditor(QWidget * parent = 0); + virtual ~PropertyEditor(); + + void assignObject(QObject * o) {object = o; rebuild();} + +protected: + void changeEvent(QEvent * e); + +private: + void configTree(); + void setItemBackColor(QTreeWidgetItem * i, const QColor & c) {i->setBackgroundColor(0, c); i->setBackgroundColor(1, c);} + void setItemForeColor(QTreeWidgetItem * i, const QColor & c) {i->setForeground(0, c); i->setForeground(1, c);} + void rebuild(); + void refresh(); + + QObject * object; + QFont font_b; + QList props; + bool active_; + +private slots: + void itemClicked(QTreeWidgetItem * item, int column); + void itemChanged(QTreeWidgetItem * item, int column); + +}; + +#endif // PROPERTYEDITOR_H diff --git a/widgets/scene_tree.cpp b/widgets/scene_tree.cpp new file mode 100644 index 0000000..b137d53 --- /dev/null +++ b/widgets/scene_tree.cpp @@ -0,0 +1,504 @@ +/* + QGL SceneTree + 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 . +*/ + +#include "scene_tree.h" +#include "ui_scene_tree.h" +#include "glcamera.h" +#include "qglview.h" +#include +#include +#include +#include + +enum Column { + cName, + cVis, + cMaterial +}; +enum ItemRole { + irObject = Qt::UserRole, + irType = Qt::UserRole + 1, +}; + +enum ObjectType { + otNode = 1, + otMesh = 2, + otLight = 4, + otCamera = 8, +}; + + +QAction * newSeparator() { + QAction * s = new QAction(); + s->setSeparator(true); + return s; +} + + +SceneTree::SceneTree(QWidget * parent): QWidget(parent) { + ui = new Ui::SceneTree(); + ui->setupUi(this); + ui->treeObjects->header()->setSectionResizeMode(cVis, QHeaderView::ResizeToContents); + ui->treeObjects->header()->setSectionsMovable(false); + ui->treeObjects->header()->swapSections(cName, cVis); + icon_empty = QIcon(":/icons/type-empty.png"); + icon_geo = QIcon(":/icons/type-geo.png"); + icon_camera = QIcon(":/icons/type-camera.png"); + icon_light = QIcon(":/icons/type-light.png"); + icon_vis[0] = QIcon(":/icons/layer-visible-off.png"); + icon_vis[1] = QIcon(":/icons/layer-visible-on.png"); + ui->treeObjects->addActions(actionsSelection()); + ui->treeObjects->addAction (newSeparator()); + ui->treeObjects->addActions(actionsAdd()); + ui->buttonFilter->addActions(QList() << ui->actionFilter_node << ui->actionFilter_mesh << ui->actionFilter_light << ui->actionFilter_camera); + view = 0; + hidden_by_filter = obj_count = 0; + block_tree = false; + connect(ui->treeObjects->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(treeObjects_selectionCnahged())); +} + + +SceneTree::~SceneTree() { + delete ui; +} + + +void SceneTree::assignQGLView(QGLView * v) { + view = v; + objectsTreeChanged(); + if (!view) return; + connect(view, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + connect(view, SIGNAL(materialsChanged()), this, SLOT(materialsChanged())); + connect(view->scene(), SIGNAL(treeChanged()), this, SLOT(objectsTreeChanged())); + connect(view->scene(), SIGNAL(__objectDeleted(ObjectBase*)), this, SLOT(__objectDeleted(ObjectBase*))); + view->setContextActions(actionsSelection()); + checkActions(); +} + + +QList SceneTree::actionsAdd() { + QList ret; + ret << ui->actionAdd_node << ui->actionAdd_light << ui->actionAdd_camera; + return ret; +} + + +QList SceneTree::actionsSelection() { + QList ret; + ret << ui->actionFocus << newSeparator() + << ui->actionGroup << ui->actionClone << newSeparator() + << ui->actionSelect_parent << ui->actionSelect_by_mesh << ui->actionSelect_by_material << newSeparator() + << ui->actionTransfer_transform_to_children << newSeparator() + << ui->actionActive_camera << ui->actionDefault_camera << newSeparator() + << ui->actionRemove; + return ret; +} + + +void SceneTree::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + return; + } +} + + +void SceneTree::rememberExpanded(QTreeWidgetItem * ti) { + for (int i = 0; i < ti->childCount(); ++i) { + QTreeWidgetItem * ci = ti->child(i); + if (ci->isExpanded()) + expanded_ << itemObject(ci); + rememberExpanded(ci); + } +} + + +void SceneTree::restoreExpanded(QTreeWidgetItem * ti) { + for (int i = 0; i < ti->childCount(); ++i) { + QTreeWidgetItem * ci = ti->child(i); + ci->setExpanded(expanded_.contains(itemObject(ci))); + restoreExpanded(ci); + } +} + + +void SceneTree::makeObjetTree(ObjectBase * o, QTreeWidgetItem * ti) { + ++obj_count; + for (int i = 0; i < o->childCount(); ++i) { + ObjectBase * co = o->child(i); + QTreeWidgetItem * ci = new QTreeWidgetItem(ti); + ci->setText(cName, co->name()); + ci->setCheckState(cVis, co->isVisible() ? Qt::Checked : Qt::Unchecked); + ci->setIcon(cVis, icon_vis[co->isVisible(true)]); + if (co->material()) + ci->setText(cMaterial, co->material()->name); + ci->setFlags(ci->flags() | Qt::ItemIsEditable); + ObjectType t = otNode; + switch (co->type()) { + case ObjectBase::glMesh: + if (co->mesh()) { + t = otMesh; + ci->setIcon(cName, icon_geo); + geo_items << ci; + } else { + ci->setIcon(cName, icon_empty); + } + break; + case ObjectBase::glLight: + t = otLight; + ci->setIcon(cName, icon_light); + break; + case ObjectBase::glCamera: + t = otCamera; + ci->setIcon(cName, icon_camera); + ci->setText(cVis, (co == view->camera()) ? "*" : ""); + cam_items << ci; + break; + default: break; + } + ci->setData(cName, irObject, quintptr(co)); + ci->setData(cName, irType, int(t)); + ci->setSelected(co->isSelected()); + makeObjetTree(co, ci); + } +} + + +ObjectBase * SceneTree::itemObject(QTreeWidgetItem * item) const { + if (!item) return 0; + return (ObjectBase*)(item->data(cName, irObject).toULongLong()); +} + + +int SceneTree::itemType(QTreeWidgetItem * item) const { + if (!item) return otNode; + return item->data(cName, irType).toInt(); +} + + +void SceneTree::selectionChanged() { + if (block_tree) return; + block_tree = true; + QList il = ui->treeObjects->findItems("", Qt::MatchContains | Qt::MatchRecursive); + const ObjectBase * fo = 0; + if (view->selectedObjects().size() == 1) + fo = view->selectedObject(); + foreach (QTreeWidgetItem * i, il) { + ObjectBase * o = itemObject(i); + i->setSelected(o->isSelected()); + if (fo && (fo == o)) { + ui->treeObjects->setCurrentItem(i); + } + } + block_tree = false; + checkActions(); +} + + +void SceneTree::materialsChanged() { + foreach (QTreeWidgetItem * i, geo_items) { + ObjectBase * o = itemObject(i); + if (!o) continue; + if (o->material()) + i->setText(cMaterial, o->material()->name); + } +} + + +void SceneTree::cameraChanged() { + if (!view) return; + foreach (QTreeWidgetItem * i, cam_items) { + ObjectBase * o = itemObject(i); + if (!o) continue; + i->setText(cVis, (o == view->camera()) ? "*" : ""); + } +} + + +bool SceneTree::filterTree(QTreeWidgetItem * ti, const QString & filter, int types) { + bool ret = false; + for (int i = 0; i < ti->childCount(); ++i) { + QTreeWidgetItem * ci = ti->child(i); + QString cit = ci->text(cName); + int t = itemType(ci); + if (ci->childCount() > 0) { + if (!filterTree(ci, filter, types)) { + ci->setHidden(true); + ++hidden_by_filter; + continue; + } + ci->setHidden(false); + ret = true; + } else { + bool f = false; + if (filter.isEmpty()) { + f = true; + } else { + f = f || cit.contains(filter, Qt::CaseInsensitive); + } + if ((types & t) != t) + f = false; + ci->setHidden(!f); + if (f) ret = true; + else ++hidden_by_filter; + } + } + return ret; +} + + +void SceneTree::checkActions() { + bool has_1 = false, has_m = false; + if (view) { + has_1 = !view->selectedObjects().isEmpty(); + has_m = view->selectedObjects().size() > 1; + } + ui->actionFocus ->setEnabled(has_1); + ui->actionRemove->setEnabled(has_1); + ui->actionClone ->setEnabled(has_1); + ui->actionGroup->setEnabled(has_m); + ui->actionTransfer_transform_to_children->setEnabled(has_1); + ui->actionSelect_parent->setEnabled(has_1); + ui->actionSelect_by_mesh->setEnabled(has_1); + ui->actionSelect_by_material->setEnabled(has_1); + ui->actionActive_camera->setEnabled(has_1); +} + + +void SceneTree::treeObjects_selectionCnahged() { + if (block_tree || !view) return; + block_tree = true; + view->scene()->clearSelection(); + ObjectBaseList sol; + QList til = ui->treeObjects->selectedItems(); + foreach (QTreeWidgetItem * i, til) + sol << itemObject(i); + view->scene()->selectObjects(sol); + block_tree = false; + checkActions(); +} + + +void SceneTree::filter() { + int types = 0; + if (ui->actionFilter_node ->isChecked()) types |= otNode ; + if (ui->actionFilter_mesh ->isChecked()) types |= otMesh ; + if (ui->actionFilter_light ->isChecked()) types |= otLight ; + if (ui->actionFilter_camera->isChecked()) types |= otCamera; + if (types == 0) types = 0xFF; + hidden_by_filter = 0; + filterTree(ui->treeObjects->invisibleRootItem(), ui->lineFilter->text(), types); + ui->treeObjects->invisibleRootItem()->setHidden(false); + ui->labelCounts->setText(tr("%1 objects, %2 hide by filter").arg(obj_count).arg(hidden_by_filter)); +} + + +void SceneTree::__objectDeleted(ObjectBase * o) { + for (int i = 0; i < geo_items.size(); ++i) + if (itemObject(geo_items[i]) == o) { + geo_items.removeAt(i); + --i; + } + for (int i = 0; i < cam_items.size(); ++i) + if (itemObject(cam_items[i]) == o) { + cam_items.removeAt(i); + --i; + } +} + + +void SceneTree::on_treeObjects_itemChanged(QTreeWidgetItem * item, int column) { + if (block_tree) return; + if (column == cName) { + ObjectBase * o = itemObject(item); + if (o) o->setName(item->text(cName)); + } + if (column == cVis) { + bool vis = item->checkState(cVis) == Qt::Checked; + QList til = ui->treeObjects->selectedItems(); + if (!til.contains(item)) { + til.clear(); + til << item; + } + foreach (QTreeWidgetItem * ti, til) { + //itemObject(ti)->setVisible(vis); + itemObject(ti)->setVisible(vis); + } + } +} + + +void SceneTree::on_treeObjects_itemMoved(QTreeWidgetItem * item, QTreeWidgetItem * new_parent) { + ObjectBase * co = itemObject(item); + if (!co->hasParent()) return; + //co->parent()->removeChild(co); + if (new_parent == ui->treeObjects->invisibleRootItem()) { + view->scene()->rootObject()->addChild(co); + } else { + ObjectBase * po = itemObject(new_parent); + if (po) + po->addChild(co); + } +} + + +void SceneTree::on_actionAdd_node_triggered() { + if (!view) return; + ObjectBase * no = new ObjectBase(); + view->scene()->addObject(no); + view->scene()->selectObject(no); +} + + +void SceneTree::on_actionAdd_light_triggered() { + if (!view) return; + ObjectBase * no = new Light(); + view->scene()->addObject(no); + view->scene()->selectObject(no); +} + + +void SceneTree::on_actionAdd_camera_triggered() { + if (!view) return; + ObjectBase * no = new Camera(); + view->scene()->addObject(no); + view->scene()->selectObject(no); +} + + +void SceneTree::on_actionClone_triggered() { + if (!view) return; + QList sil = ui->treeObjects->selectedItems(); + ObjectBaseList col; + foreach (QTreeWidgetItem * i, sil) { + ObjectBase * o = itemObject(i); + if (!o) continue; + ObjectBase * no = o->clone(); + o->parent()->addChild(no); + col << no; + } + view->scene()->selectObjects(col); +} + + +void SceneTree::on_actionGroup_triggered() { + if (!view) return; + ObjectBaseList sol = view->scene()->selectedObjects(true); + ObjectBase * cp = sol[0]->parent(); + ObjectBase * nr = new ObjectBase(); + cp->addChild(nr); + foreach (ObjectBase * o, sol) + nr->addChild(o); + view->scene()->selectObject(nr); +} + + +void SceneTree::on_actionTransfer_transform_to_children_triggered() { + if (!view) return; + ObjectBaseList sol = view->scene()->selectedObjects(true); + foreach (ObjectBase * o, sol) + o->transferTransformToChildren(); +} + + +void SceneTree::on_actionActive_camera_triggered() { + if (!view) return; + ObjectBase * o = view->scene()->selectedObject(); + if (!o) return; + if (o->type() != ObjectBase::glCamera) return; + view->setCamera((Camera *)o); + cameraChanged(); +} + + +void SceneTree::on_actionDefault_camera_triggered() { + if (!view) return; + view->setDefaultCamera(); + cameraChanged(); +} + + +void SceneTree::on_actionSelect_parent_triggered() { + if (!view) return; + ObjectBaseList sol = view->scene()->selectedObjects(true); + QSet nsl; + foreach (ObjectBase * o, sol) { + ObjectBase * po = o->parent(); + if (po != view->scene()->rootObject()) + o = po; + nsl << o; + } + view->scene()->selectObjects(nsl.toList()); +} + + +void SceneTree::on_actionSelect_by_mesh_triggered() { + view->scene()->selectObjectsByMesh(); +} + + +void SceneTree::on_actionSelect_by_material_triggered() { + view->scene()->selectObjectsByMaterial(); +} + + +void SceneTree::removeObjects() { + if (!view) return; + QList sil = ui->treeObjects->selectedItems(); + foreach (QTreeWidgetItem * i, sil) { + ObjectBase * o = itemObject(i); + if (o) delete o; + } + qDeleteAll(sil); +} + + +void SceneTree::focusObjects() { + if (!view) return; + if (!view->camera()) return; + Box3D bb; + ObjectBaseList ol = view->selectedObjects(); + foreach (ObjectBase * o, ol) { + o->calculateBoundingBox(); + bb |= o->boundingBox(); + } + view->focusOn(bb); +} + + +void SceneTree::objectsTreeChanged() { + int vpos = ui->treeObjects->verticalScrollBar()->value(); + expanded_.clear(); + geo_items.clear(); + cam_items.clear(); + rememberExpanded(ui->treeObjects->invisibleRootItem()); + block_tree = true; + ui->treeObjects->clear(); + block_tree = false; + if (!view) return; + block_tree = true; + obj_count = 0; + makeObjetTree(view->scene()->rootObject(), ui->treeObjects->invisibleRootItem()); + --obj_count; + restoreExpanded(ui->treeObjects->invisibleRootItem()); + block_tree = false; + filter(); + QApplication::processEvents(); + ui->treeObjects->verticalScrollBar()->setValue(vpos); +} diff --git a/widgets/scene_tree.h b/widgets/scene_tree.h new file mode 100644 index 0000000..bf16a55 --- /dev/null +++ b/widgets/scene_tree.h @@ -0,0 +1,100 @@ +/* + QGL SceneTree + 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 . +*/ + +#ifndef SCENE_TREE_H +#define SCENE_TREE_H + +#include +#include +#include "glscene.h" + +class QTreeWidgetItem; + +namespace Ui { + class SceneTree; +} + +class SceneTree: public QWidget +{ + Q_OBJECT +public: + SceneTree(QWidget * parent = 0); + ~SceneTree(); + + void assignQGLView(QGLView * v); + QList actionsAdd(); + QList actionsSelection(); + +private: + void changeEvent(QEvent * e); + void rememberExpanded(QTreeWidgetItem * ti); + void restoreExpanded(QTreeWidgetItem * ti); + void makeObjetTree(ObjectBase * o, QTreeWidgetItem * ti); + ObjectBase * itemObject(QTreeWidgetItem * item) const; + int itemType(QTreeWidgetItem * item) const; + bool filterTree(QTreeWidgetItem * ti, const QString & filter, int types); + void checkActions(); + + Ui::SceneTree * ui; + bool block_tree; + int hidden_by_filter, obj_count; + QIcon icon_empty, icon_geo, icon_camera, icon_light, icon_vis[2]; + QSet expanded_; + QList geo_items, cam_items; + QGLView * view; + +private slots: + void treeObjects_selectionCnahged(); + void on_treeObjects_itemChanged(QTreeWidgetItem * item, int column); + void on_treeObjects_itemMoved (QTreeWidgetItem * item, QTreeWidgetItem * new_parent); + + void on_actionAdd_node_triggered(); + void on_actionAdd_light_triggered(); + void on_actionAdd_camera_triggered(); + + void on_actionFocus_triggered() {focusObjects();} + void on_actionRemove_triggered() {removeObjects();} + void on_actionClone_triggered(); + void on_actionGroup_triggered(); + void on_actionTransfer_transform_to_children_triggered(); + + void on_actionActive_camera_triggered(); + void on_actionDefault_camera_triggered(); + + void on_actionSelect_parent_triggered(); + void on_actionSelect_by_mesh_triggered(); + void on_actionSelect_by_material_triggered(); + + void removeObjects(); + void focusObjects(); + void objectsTreeChanged(); + void selectionChanged(); + void materialsChanged(); + void cameraChanged(); + void filter(); + void __objectDeleted(ObjectBase * o); + +public slots: + +signals: + +private: + +}; + +#endif // SCENE_TREE_H diff --git a/widgets/scene_tree.ui b/widgets/scene_tree.ui new file mode 100644 index 0000000..ec57188 --- /dev/null +++ b/widgets/scene_tree.ui @@ -0,0 +1,425 @@ + + + SceneTree + + + + 0 + 0 + 442 + 606 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Filter: + + + + + + + + + + + :/icons/configure.png:/icons/configure.png + + + QToolButton::InstantPopup + + + + + + + Qt::Vertical + + + + + + + Expand tree + + + + :/icons/expand.png:/icons/expand.png + + + + + + + Collapse tree + + + + :/icons/collapse.png:/icons/collapse.png + + + + + + + + + + + + + + + + Qt::ActionsContextMenu + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::ScrollPerPixel + + + true + + + false + + + + Name + + + + + Vis + + + + + Material + + + + + + + + + :/icons/type-camera.png:/icons/type-camera.png + + + Focus + + + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + Remove + + + + + + :/icons/edit-copy.png:/icons/edit-copy.png + + + Clone + + + + + + :/icons/group.png:/icons/group.png + + + Group + + + + + + :/icons/go-top.png:/icons/go-top.png + + + Select parent + + + + + + :/icons/type-geo.png:/icons/type-geo.png + + + Select by mesh + + + + + + :/icons/format-fill-color.png:/icons/format-fill-color.png + + + Select by material + + + + + + :/icons/add-type-empty.png:/icons/add-type-empty.png + + + Add node + + + + + + :/icons/add-type-light.png:/icons/add-type-light.png + + + Add light + + + + + + :/icons/add-type-camera.png:/icons/add-type-camera.png + + + Add camera + + + + + true + + + + :/icons/type-empty.png:/icons/type-empty.png + + + Nodes + + + + + true + + + + :/icons/type-geo.png:/icons/type-geo.png + + + Meshes + + + + + true + + + + :/icons/type-light.png:/icons/type-light.png + + + Lights + + + + + true + + + + :/icons/type-camera.png:/icons/type-camera.png + + + Cameras + + + + + Transfer transform to children + + + + + + :/icons/type-camera.png:/icons/type-camera.png + + + Active camera + + + + + + :/icons/type-camera.png:/icons/type-camera.png + + + Default camera + + + + + + CLineEdit + QLineEdit +
clineedit.h
+
+ + InternalMoveTreeWidget + QTreeWidget +
treewidget_p.h
+
+
+ + + + + + + + + + buttonExpand + clicked() + treeObjects + expandAll() + + + 395 + 164 + + + 299 + 359 + + + + + buttonCollapse + clicked() + treeObjects + collapseAll() + + + 427 + 164 + + + 332 + 369 + + + + + lineFilter + textChanged(QString) + SceneTree + filter() + + + 273 + 130 + + + 355 + 37 + + + + + actionFilter_camera + toggled(bool) + SceneTree + filter() + + + -1 + -1 + + + 414 + 302 + + + + + actionFilter_mesh + toggled(bool) + SceneTree + filter() + + + -1 + -1 + + + 414 + 302 + + + + + actionFilter_light + toggled(bool) + SceneTree + filter() + + + -1 + -1 + + + 414 + 302 + + + + + actionFilter_node + toggled(bool) + SceneTree + filter() + + + -1 + -1 + + + 414 + 302 + + + + + + removeObjects() + focusObjects() + filter() + +
diff --git a/widgets/treewidget_p.h b/widgets/treewidget_p.h new file mode 100644 index 0000000..18cce4b --- /dev/null +++ b/widgets/treewidget_p.h @@ -0,0 +1,51 @@ +/* + QGL SceneTree + 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 . +*/ + +#ifndef TREEWIDGET_H +#define TREEWIDGET_H + +#include +#include +#include +#include + +class InternalMoveTreeWidget: public QTreeWidget +{ + Q_OBJECT +public: + InternalMoveTreeWidget(QWidget * parent = 0): QTreeWidget(parent) {} + +protected: + virtual void dropEvent(QDropEvent * e) { + QList sil = selectedItems(); + if (sil.isEmpty()) return; + QTreeWidget::dropEvent(e); + foreach (QTreeWidgetItem * ti, sil) { + QTreeWidgetItem * ti_p = ti->parent(); + if (!ti_p) ti_p = invisibleRootItem(); + int ti_ppos = ti_p->indexOfChild(ti); + emit itemMoved(ti, ti_p, ti_ppos); + } + } + +signals: + void itemMoved(QTreeWidgetItem * item, QTreeWidgetItem * new_parent, int new_index); + +}; + +#endif // TREEWIDGET_H diff --git a/widgets/view_editor.cpp b/widgets/view_editor.cpp new file mode 100644 index 0000000..ab48597 --- /dev/null +++ b/widgets/view_editor.cpp @@ -0,0 +1,179 @@ +/* + QGL ViewEditor + 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 . +*/ + +#include "view_editor.h" +#include "ui_view_editor.h" +#include +#include +#include + + +ViewEditor::ViewEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::ViewEditor(); + ui->setupUi(this); + view = 0; + active = true; + ui->checkCameraLight->setCheckState(Qt::PartiallyChecked); +#if QT_VERSION >= QT_VERSION_CHECK(5,12,0) + ui->spinDepthStart->setStepType(QAbstractSpinBox::AdaptiveDecimalStepType); +#endif +} + + +void ViewEditor::assignQGLView(QGLView * v) { + view = v; + if (!view) return; + active = false; + ui->spinFOV->setValue(view->FOV()); + ui->spinDepthStart->setValue(view->depthStart()); + ui->groupHoverHalo->setChecked(view->isHoverHaloEnabled()); + ui->groupSelectionHalo->setChecked(view->isSelectionHaloEnabled()); + ui->spinHoverHaloFill->setValue(view->hoverHaloFillAlpha()); + ui->spinSelectionHaloFill->setValue(view->selectionHaloFillAlpha()); + ui->colorHoverHalo->setColor(view->hoverHaloColor()); + ui->colorSelectionHalo->setColor(view->selectionHaloColor()); + ui->checkFXAA->setChecked(view->isFeatureEnabled(QGLView::qglFXAA)); + ui->checkCameraOrbit->setChecked(view->isCameraOrbit()); + ui->checkCameraLight->setCheckState((Qt::CheckState)view->cameraLightMode()); + ui->checkService->setChecked(view->isServiceMode()); + ui->lineHDR->setProperty("GLpath", view->environmentMapFile()); + ui->lineHDR->setText(QFileInfo(view->environmentMapFile()).fileName()); + active = true; +} + + +void ViewEditor::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void ViewEditor::on_spinFOV_valueChanged(double val) { + if (!view || !active) return; + view->setFOV(val); +} + + +void ViewEditor::on_spinDepthStart_valueChanged(double val) { + if (!view || !active) return; + view->setDepthStart(val); +} + + +void ViewEditor::on_spinViewGamma_valueChanged(double val) { + if (!view || !active) return; + view->setGamma(val); +} + + +void ViewEditor::on_comboViewRenderMode_currentIndexChanged(int val) { + if (!view || !active) return; + static int modes[] = {GL_POINT, GL_LINE, GL_FILL}; + view->setRenderMode((ObjectBase::RenderMode)modes[val]); +} + + +void ViewEditor::on_groupHoverHalo_clicked(bool val) { + if (!view || !active) return; + view->setHoverHaloEnabled(val); +} + + +void ViewEditor::on_groupSelectionHalo_clicked(bool val) { + if (!view || !active) return; + view->setSelectionHaloEnabled(val); +} + + +void ViewEditor::on_spinHoverHaloFill_valueChanged(double val) { + if (!view || !active) return; + view->setHoverHaloFillAlpha(val); +} + + +void ViewEditor::on_spinSelectionHaloFill_valueChanged(double val) { + if (!view || !active) return; + view->setSelectionHaloFillAlpha(val); +} + + +void ViewEditor::on_colorHoverHalo_colorChanged(QColor color) { + if (!view || !active) return; + view->setHoverHaloColor(color); +} + + +void ViewEditor::on_colorSelectionHalo_colorChanged(QColor color) { + if (!view || !active) return; + view->setSelectionHaloColor(color); +} + + +void ViewEditor::on_checkAutoExposure_toggled(bool val) { + if (!view || !active) return; + view->setAutoExposure(val); +} + + +void ViewEditor::on_checkFXAA_clicked(bool val) { + if (!view || !active) return; + view->setFeature(QGLView::qglFXAA, val); +} + + +void ViewEditor::on_checkCameraOrbit_clicked(bool val) { + if (!view || !active) return; + view->setCameraOrbit(val); +} + + +void ViewEditor::on_checkService_clicked(bool val) { + if (!view || !active) return; + view->setServiceMode(val); +} + + +void ViewEditor::on_checkCameraLight_stateChanged(int s) { + if (!view || !active) return; + view->setCameraLightMode((QGLView::CameraLightMode)s); + +} + + +void ViewEditor::on_buttonHDRClear_clicked() { + if (!view || !active) return; + ui->lineHDR->setText(""); + ui->lineHDR->setProperty("GLpath", ""); + view->setEnvironmentMapFile(""); +} + + +void ViewEditor::on_buttonHDRSelect_clicked() { + if (!view || !active) return; + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineHDR->property("GLpath").toString(), "Radiance HDR(*.hdr)"); + if (str.isEmpty()) return; + ui->lineHDR->setText(QFileInfo(str).fileName()); + ui->lineHDR->setProperty("GLpath", str); + view->setEnvironmentMapFile(str); +} diff --git a/widgets/view_editor.h b/widgets/view_editor.h new file mode 100644 index 0000000..405fa4b --- /dev/null +++ b/widgets/view_editor.h @@ -0,0 +1,67 @@ +/* + QGL ViewEditor + 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 . +*/ + +#ifndef VIEW_EDITOR_H +#define VIEW_EDITOR_H + +#include +#include "qglview.h" + +namespace Ui { + class ViewEditor; +} + +class ViewEditor: public QWidget +{ + Q_OBJECT +public: + explicit ViewEditor(QWidget * parent = 0); + + void assignQGLView(QGLView * v); + +protected: + void changeEvent(QEvent * e); + + Ui::ViewEditor * ui; + QGLView * view; + bool active; + +private slots: + void on_spinFOV_valueChanged(double val); + void on_spinDepthStart_valueChanged(double val); + void on_spinViewGamma_valueChanged(double val); + void on_comboViewRenderMode_currentIndexChanged(int val); + void on_groupHoverHalo_clicked(bool val); + void on_groupSelectionHalo_clicked(bool val); + void on_spinHoverHaloFill_valueChanged(double val); + void on_spinSelectionHaloFill_valueChanged(double val); + void on_colorHoverHalo_colorChanged(QColor color); + void on_colorSelectionHalo_colorChanged(QColor color); + void on_checkAutoExposure_toggled(bool val); + void on_checkFXAA_clicked(bool val); + void on_checkCameraOrbit_clicked(bool val); + void on_checkService_clicked(bool val); + void on_checkCameraLight_stateChanged(int s); + void on_buttonHDRClear_clicked(); + void on_buttonHDRSelect_clicked(); + +signals: + +}; + +#endif // VIEW_EDITOR_H diff --git a/widgets/view_editor.ui b/widgets/view_editor.ui new file mode 100644 index 0000000..744b0a5 --- /dev/null +++ b/widgets/view_editor.ui @@ -0,0 +1,511 @@ + + + ViewEditor + + + + 0 + 0 + 328 + 538 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Draw mode: + + + + + + + 2 + + + + Point + + + + + Wireframe + + + + + Solid + + + + + + + + Gamma: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 0.010000000000000 + + + 5.000000000000000 + + + 1.000000000000000 + + + 2 + + + + + + + Env HDR: + + + + + + + 2 + + + + + + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + + :/icons/document-open.png:/icons/document-open.png + + + + + + + + + + + + + + FXAA + + + + + + + Auto exposure + + + + + + + Service mode + + + true + + + + + + + Camera light + + + true + + + + + + + + + Camera + + + + + + + + Orbit + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 1 + + + + + + + + FOV: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 0.100000000000000 + + + 179.900000000000006 + + + 60.000000000000000 + + + + + + + + + + + Depth start: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 3 + + + 999999999.000000000000000 + + + 1.000000000000000 + + + + + + + + + + + + Hover halo + + + true + + + + + + Fill: + + + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.300000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + + + + + + 255 + 0 + 251 + + + + true + + + + + + + + + + Selection halo + + + true + + + + + + Fill: + + + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.300000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + + + + + + 0 + 143 + 239 + + + + true + + + + + + + + + + + + + 0 + 0 + + + + Fog + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Density: + + + + + + + + + 0.000000000000000 + + + 10.000000000000000 + + + 0.000000000000000 + + + 3 + + + 0.050000000000000 + + + 0.100000000000000 + + + + + + + + 128 + 128 + 128 + + + + + + + + + + Decay: + + + + + + + 0.000000000000000 + + + 9999.000000000000000 + + + 10.000000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + true + + + 9999.000000000000000 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+
+ + + + + +
diff --git a/widgets/widgets.qrc b/widgets/widgets.qrc new file mode 100644 index 0000000..c83819f --- /dev/null +++ b/widgets/widgets.qrc @@ -0,0 +1,18 @@ + + + ../icons/go-jump.png + ../icons/dialog-close.png + ../icons/dialog-cancel.png + ../icons/edit-clear.png + ../icons/configure.png + ../icons/document-save.png + ../icons/edit-find.png + ../icons/list-add.png + ../icons/edit-delete.png + ../icons/edit-copy.png + ../icons/edit-paste.png + ../icons/document-edit.png + ../icons/group.png + ../icons/format-fill-color.png + +