source-tree refactoring
9
src/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
#if (NOT DEFINED ANDROID_PLATFORM)
|
||||
# if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/plugin")
|
||||
# #add_subdirectory(plugin)
|
||||
# endif()
|
||||
#endif()
|
||||
foreach (_d "core" "widgets" "qglview_test") # "plugin")
|
||||
add_subdirectory("${_d}")
|
||||
endforeach()
|
||||
shstk_copy_to_parent()
|
||||
35
src/core/CMakeLists.txt
Normal file
@@ -0,0 +1,35 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(qglengine_core)
|
||||
if (POLICY CMP0017)
|
||||
cmake_policy(SET CMP0017 NEW)
|
||||
endif()
|
||||
qad_find_qt(Core Gui OpenGL OpenGLWidgets Xml)
|
||||
find_package(OpenGL REQUIRED)
|
||||
qad_sources(SRC)
|
||||
set(_includes "${_qglengine_root_build}")
|
||||
foreach (_d "formats" "core" "scene" "render")
|
||||
qad_sources(FSRC DIR "${_d}")
|
||||
list(APPEND SRC ${FSRC})
|
||||
list(APPEND _includes "${CMAKE_CURRENT_SOURCE_DIR}/${_d}")
|
||||
endforeach()
|
||||
qad_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} ${QGLEngine_LIB_TYPE}
|
||||
LABEL "QGLEngine core library"
|
||||
FULLNAME "${QGLEngine_DOMAIN}.qglengine_core"
|
||||
COMPANY "${QGLEngine_COMPANY}"
|
||||
INFO "QGLEngine core library")
|
||||
make_rc(${PROJECT_NAME} _RC)
|
||||
qad_add_library(${PROJECT_NAME} ${QGLEngine_LIB_TYPE} out_CPP ${_RC})
|
||||
qad_generate_export_header(${PROJECT_NAME})
|
||||
list(APPEND out_HDR "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_export.h")
|
||||
qad_target_include_directories(${PROJECT_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" ${_includes})
|
||||
qad_target_link_libraries(${PROJECT_NAME} QAD::Widgets assimp ${OPENGL_LIBRARIES})
|
||||
message(STATUS "Building QGLEngine version ${QGLEngine_VERSION} (${QGLEngine_LIB_TYPE_MSG}) for ${QtVersions}")
|
||||
|
||||
list(APPEND QT_MULTILIB_LIST ${PROJECT_NAME})
|
||||
shstk_copy_to_parent()
|
||||
#message(STATUS "Building ${PROJECT_NAME}")
|
||||
shstk_qad_install("qglengine" FALSE "${PROJECT_NAME}" "${out_HDR}" "out_QM")
|
||||
BIN
src/core/coeffs_brdf.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
94
src/core/core/glbuffer.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
QGL Buffer
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "glbuffer.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
|
||||
|
||||
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::reinit() {
|
||||
buffer_ = 0;
|
||||
prev_size = 0;
|
||||
}
|
||||
|
||||
|
||||
void Buffer::bind(QOpenGLExtraFunctions * f) {
|
||||
// qDebug() << "bind" << target_ << buffer_;
|
||||
f->glBindBuffer(target_, buffer_);
|
||||
}
|
||||
|
||||
|
||||
void Buffer::release(QOpenGLExtraFunctions * f) {
|
||||
f->glBindBuffer(target_, 0);
|
||||
}
|
||||
|
||||
|
||||
void * Buffer::map(QOpenGLExtraFunctions * f, GLbitfield mode, int size) {
|
||||
if (size < 0) size = prev_size;
|
||||
return f->glMapBufferRange(target_, 0, size, mode);
|
||||
}
|
||||
|
||||
|
||||
void Buffer::unmap(QOpenGLExtraFunctions * f) {
|
||||
f->glUnmapBuffer(target_);
|
||||
}
|
||||
|
||||
|
||||
bool Buffer::resize(QOpenGLExtraFunctions * f, int new_size) {
|
||||
if (new_size <= 0) return false;
|
||||
// qDebug() << "check resize buffer" << buffer_ << "bytes" << new_size << ", old =" << prev_size;
|
||||
if (new_size <= prev_size) return false;
|
||||
prev_size = new_size;
|
||||
// qDebug() << "resize buffer " << buffer_ << target_ << "for" << new_size << "bytes";
|
||||
f->glBufferData(target_, new_size, 0, usage_);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Buffer::load(QOpenGLExtraFunctions * f, const void * data, int size, int offset) {
|
||||
if (!data || size <= 0) return;
|
||||
// qDebug() << "load buffer" << buffer_ << "bytes" << size;
|
||||
f->glBufferSubData(target_, offset, size, data);
|
||||
}
|
||||
58
src/core/core/glbuffer.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
QGL Buffer
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLBUFFER_H
|
||||
#define GLBUFFER_H
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT Buffer {
|
||||
friend class ObjectBase;
|
||||
|
||||
public:
|
||||
Buffer(GLenum target, GLenum usage = GL_DYNAMIC_DRAW);
|
||||
~Buffer();
|
||||
|
||||
void init(QOpenGLExtraFunctions * f);
|
||||
void destroy(QOpenGLExtraFunctions * f);
|
||||
void reinit();
|
||||
|
||||
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
|
||||
237
src/core/core/glcubemap.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
QGL CubeTexture
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glcubemap.h"
|
||||
|
||||
#include "gltypes.h"
|
||||
#include "hdr_p.h"
|
||||
|
||||
using namespace QGLEngineShaders;
|
||||
|
||||
|
||||
QVector<QVector3D> loadFileHDR(const QString & path, QSize * size) {
|
||||
if (size) *size = QSize();
|
||||
QVector<QVector3D> ret;
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qDebug() << "[QGLEngine] Can`t open" << path;
|
||||
return ret;
|
||||
}
|
||||
QTextStream ts(&f);
|
||||
QString line = ts.readLine(256);
|
||||
if (line != "#?RADIANCE") return ret;
|
||||
QSize sz;
|
||||
while (!ts.atEnd()) {
|
||||
line = ts.readLine(256);
|
||||
if (line.startsWith("FORMAT")) {
|
||||
line.remove(0, 7);
|
||||
if (!line.startsWith("32-bit")) {
|
||||
qDebug() << "[QGLEngine] File" << path << "has unknown format!";
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (line.mid(1, 2) == "Y ") {
|
||||
QStringList sl = line.trimmed().split(" ");
|
||||
sl.removeAll("");
|
||||
if (sl.size() != 4) {
|
||||
qDebug() << "[QGLEngine] File" << path << "has unknown size!";
|
||||
return ret;
|
||||
}
|
||||
sz.setWidth(sl[3].toInt());
|
||||
sz.setHeight(sl[1].toInt());
|
||||
// qDebug() << "found size" << sz;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sz.isEmpty()) return ret;
|
||||
f.seek(ts.pos());
|
||||
QDataStream ds(&f);
|
||||
int count = sz.width() * sz.height();
|
||||
QVector<float> data(count * 3);
|
||||
if (!RGBE_ReadPixels_RLE(&ds, data.data(), sz.width(), sz.height())) return ret;
|
||||
|
||||
if (size) *size = sz;
|
||||
ret.resize(count);
|
||||
// QColor col;
|
||||
// QImage im(sz, QImage::Format_ARGB32);
|
||||
// QRgb * imdata = (QRgb*)im.bits();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
QVector3D p(pow(data[i * 3 + 2], 1. / 2.2), pow(data[i * 3 + 1], 1. / 2.2), pow(data[i * 3 + 0], 1. / 2.2));
|
||||
ret[i] = p;
|
||||
// col = QColor::fromRgbF(piClamp(p[0], 0.f, 1.f),
|
||||
// piClamp(p[1], 0.f, 1.f),
|
||||
// piClamp(p[2], 0.f, 1.f));
|
||||
// imdata[i] = col.rgb();
|
||||
}
|
||||
// im.save("_hdr.png");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QVector<QVector3D> faceHDR(const QVector<QVector3D> & data, QSize sz, QSize & fsz, int face) {
|
||||
QVector<QVector3D> ret;
|
||||
if (data.isEmpty() || sz.isNull()) return ret;
|
||||
QRect fr;
|
||||
int fw = sz.width() / 4;
|
||||
int fh = sz.height() / 3;
|
||||
fsz = QSize(fw, fh);
|
||||
ret.reserve(fw * fh);
|
||||
switch (face) {
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
|
||||
fr.setRect(fw, fh, fw, fh);
|
||||
for (int x = fr.left(); x <= fr.right(); ++x) {
|
||||
for (int y = fr.top(); y <= fr.bottom(); ++y) {
|
||||
ret << data[y * sz.width() + x];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
|
||||
fr.setRect(fw * 3, fh, fw, fh);
|
||||
for (int x = fr.right(); x >= fr.left(); --x) {
|
||||
for (int y = fr.bottom(); y >= fr.top(); --y) {
|
||||
ret << data[y * sz.width() + x];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
|
||||
fr.setRect(0, fh, fw, fh);
|
||||
for (int y = fr.bottom(); y >= fr.top(); --y) {
|
||||
for (int x = fr.left(); x <= fr.right(); ++x) {
|
||||
ret << data[y * sz.width() + x];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
|
||||
fr.setRect(fw * 2, fh, fw, fh);
|
||||
for (int y = fr.top(); y <= fr.bottom(); ++y) {
|
||||
for (int x = fr.right(); x >= fr.left(); --x) {
|
||||
ret << data[y * sz.width() + x];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
|
||||
fr.setRect(fw, 0, fw, fh);
|
||||
for (int x = fr.left(); x <= fr.right(); ++x) {
|
||||
for (int y = fr.top(); y <= fr.bottom(); ++y) {
|
||||
ret << data[y * sz.width() + x];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
|
||||
fr.setRect(fw, fh * 2, fw, fh);
|
||||
for (int x = fr.left(); x <= fr.right(); ++x) {
|
||||
for (int y = fr.top(); y <= fr.bottom(); ++y) {
|
||||
ret << data[y * sz.width() + x];
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (fr.isEmpty()) return ret;
|
||||
// qDebug() << ret.size() << fr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
CubeTexture::CubeTexture(QOpenGLExtraFunctions * f_, int _size, const GLenum & _format): f(f_) {
|
||||
size = _size;
|
||||
format_ = _format;
|
||||
id_ = 0;
|
||||
changed_ = false;
|
||||
}
|
||||
|
||||
|
||||
bool CubeTexture::init() {
|
||||
if (isInit()) return true;
|
||||
f->glGenTextures(1, &id_);
|
||||
f->glBindTexture(GL_TEXTURE_CUBE_MAP, id_);
|
||||
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
f->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
changed_ = false;
|
||||
return id_ > 0;
|
||||
}
|
||||
|
||||
|
||||
void CubeTexture::destroy() {
|
||||
if (!isInit()) return;
|
||||
f->glDeleteTextures(1, &id_);
|
||||
id_ = 0;
|
||||
}
|
||||
|
||||
|
||||
void CubeTexture::bind(int channel) {
|
||||
init();
|
||||
f->glActiveTexture(GL_TEXTURE0 + channel);
|
||||
f->glBindTexture(GL_TEXTURE_CUBE_MAP, id_);
|
||||
}
|
||||
|
||||
|
||||
void CubeTexture::release() {
|
||||
f->glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
}
|
||||
|
||||
|
||||
void CubeTexture::loadHDR(const QVector<QVector3D> & data, QSize sz) {
|
||||
bind();
|
||||
QSize fsz;
|
||||
QVector<QVector3D> fd;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
fd = faceHDR(data, sz, fsz, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i);
|
||||
// qDebug() << "load cube" << fd[0];
|
||||
f->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
|
||||
0,
|
||||
format_,
|
||||
fsz.width(),
|
||||
fsz.height(),
|
||||
0,
|
||||
GL_RGB,
|
||||
GL_FLOAT,
|
||||
fd.isEmpty() ? 0 : fd.constData());
|
||||
// qDebug() << QString::number(GetLastError(), 16);
|
||||
}
|
||||
f->glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||
}
|
||||
|
||||
|
||||
void CubeTexture::setFileHDR(const QString & path) {
|
||||
hdr_path = path;
|
||||
changed_ = true;
|
||||
}
|
||||
|
||||
|
||||
void CubeTexture::load() {
|
||||
if (!changed_) return;
|
||||
init();
|
||||
if (!hdr_path.isEmpty()) {
|
||||
QSize sz;
|
||||
QVector<QVector3D> data = loadFileHDR(hdr_path, &sz);
|
||||
loadHDR(data, sz);
|
||||
} else {
|
||||
destroy();
|
||||
bind();
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
f->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format_, 1, 1, 0, GL_RGB, GL_FLOAT, 0);
|
||||
}
|
||||
f->glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
|
||||
}
|
||||
changed_ = false;
|
||||
}
|
||||
60
src/core/core/glcubemap.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
QGL CubeTexture
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLCUBEMAP_H
|
||||
#define GLCUBEMAP_H
|
||||
|
||||
#include "chunkstream.h"
|
||||
#include "glshaders_types.h"
|
||||
|
||||
QVector<QVector3D> QGLENGINE_CORE_EXPORT loadFileHDR(const QString & path, QSize * size = 0);
|
||||
|
||||
class QGLENGINE_CORE_EXPORT CubeTexture {
|
||||
public:
|
||||
CubeTexture(QOpenGLExtraFunctions * f_, int _size, const GLenum & _format = GL_RGB16F);
|
||||
bool init();
|
||||
void destroy();
|
||||
void bind(int channel = 0);
|
||||
void release();
|
||||
void resize(int _size) {
|
||||
size = _size;
|
||||
changed_ = true;
|
||||
}
|
||||
void loadHDR(const QVector<QVector3D> & data, QSize sz);
|
||||
void setFileHDR(const QString & path);
|
||||
QString fileHDR() const { return hdr_path; }
|
||||
GLenum format() const { return format_; }
|
||||
void setFormat(GLenum f) {
|
||||
format_ = f;
|
||||
changed_ = true;
|
||||
}
|
||||
GLuint id() const { return id_; }
|
||||
bool isInit() const { return id_ != 0; }
|
||||
void load();
|
||||
|
||||
private:
|
||||
QOpenGLExtraFunctions * f;
|
||||
bool changed_;
|
||||
int size;
|
||||
GLenum format_;
|
||||
GLuint id_;
|
||||
QString hdr_path;
|
||||
};
|
||||
|
||||
|
||||
#endif // GLCUBEMAP_H
|
||||
321
src/core/core/glframebuffer.cpp
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
QGL Framebuffer
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glframebuffer.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QTime>
|
||||
|
||||
|
||||
Framebuffer::Framebuffer(QOpenGLExtraFunctions * f_, int colorAttachments_, bool withDepth, GLenum colorFormat_, GLenum _target)
|
||||
: f(f_)
|
||||
, pbo(GL_PIXEL_PACK_BUFFER, GL_STREAM_DRAW) {
|
||||
is_depth = withDepth;
|
||||
target_ = _target;
|
||||
color_formats.fill(colorFormat_, colorAttachments_);
|
||||
colors.fill(0, colorAttachments_);
|
||||
fbo = drbo = 0;
|
||||
tex_d = 0;
|
||||
wid = hei = 0;
|
||||
pbo_queried = 0;
|
||||
is_changed = false;
|
||||
}
|
||||
|
||||
|
||||
Framebuffer::Framebuffer(QOpenGLExtraFunctions * f_, QVector<GLenum> colors_, bool withDepth, GLenum _target)
|
||||
: f(f_)
|
||||
, pbo(GL_PIXEL_PACK_BUFFER, GL_STREAM_DRAW) {
|
||||
is_depth = withDepth;
|
||||
target_ = _target;
|
||||
color_formats = colors_;
|
||||
colors.fill(0, colors_.size());
|
||||
fbo = drbo = 0;
|
||||
tex_d = 0;
|
||||
wid = hei = 0;
|
||||
pbo_queried = 0;
|
||||
is_changed = false;
|
||||
}
|
||||
|
||||
|
||||
Framebuffer::~Framebuffer() {
|
||||
if (fbo > 0) 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 (fbo > 0) {
|
||||
if ((wid == width) && (hei == height) && !force) return;
|
||||
}
|
||||
wid = width;
|
||||
hei = height;
|
||||
if (fbo > 0) deleteGLFramebuffer(fbo);
|
||||
f->glGenFramebuffers(1, &fbo);
|
||||
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
// qDebug() << "resize" << f << wid << hei << 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->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target_, colors[i], 0);
|
||||
}
|
||||
if (is_depth) {
|
||||
deleteGLTexture(f, tex_d);
|
||||
if (drbo > 0) 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->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, target_, tex_d, 0);
|
||||
}
|
||||
f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
if (pbo.isInit()) {
|
||||
enablePixelBuffer();
|
||||
}
|
||||
is_changed = false;
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::reinit() {
|
||||
pbo.reinit();
|
||||
colors.fill(0);
|
||||
fbo = drbo = 0;
|
||||
tex_d = 0;
|
||||
pbo_queried = 0;
|
||||
is_changed = true;
|
||||
}
|
||||
|
||||
|
||||
QImage Framebuffer::grab() const {
|
||||
return QImage();
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::queryPoint(int index, QPoint p) {
|
||||
pbo_queried = 0;
|
||||
if (index < 0 || index >= colors.size()) return;
|
||||
if (!rect().contains(p) || !pbo.isInit()) return;
|
||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
|
||||
pbo.bind(f);
|
||||
f->glReadPixels(p.x(), height() - p.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
pbo_queried = 1;
|
||||
pbo.release(f);
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::queryPoints(int index, QRect rect_, GLenum pixel_format) {
|
||||
pbo_queried = 0;
|
||||
if (index < 0 || index >= colors.size()) return;
|
||||
rect_ &= rect();
|
||||
if (rect_.isEmpty() || !pbo.isInit()) return;
|
||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
|
||||
pbo.bind(f);
|
||||
f->glReadPixels(rect_.x(), height() - rect_.bottom(), rect_.width(), rect_.height(), GL_RGBA, pixel_format, 0);
|
||||
pbo_queried = rect_.width() * rect_.height();
|
||||
pbo.release(f);
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::queryImage(int index) {
|
||||
queryPoints(index, rect());
|
||||
}
|
||||
|
||||
|
||||
uint Framebuffer::getPoint() const {
|
||||
if (!pbo.isInit() || (pbo_queried == 0)) return 0;
|
||||
uint ret = 0;
|
||||
pbo.bind(f);
|
||||
void * map = pbo.map(f, GL_MAP_READ_BIT, sizeof(uint));
|
||||
if (map) memcpy(&ret, map, sizeof(uint));
|
||||
pbo.unmap(f);
|
||||
pbo.release(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QVector<uint> Framebuffer::getPointsByte() const {
|
||||
QVector<uint> ret;
|
||||
if (!pbo.isInit() || (pbo_queried == 0)) return ret;
|
||||
ret.resize(pbo_queried);
|
||||
pbo.bind(f);
|
||||
void * map = pbo.map(f, GL_MAP_READ_BIT, pbo_queried * sizeof(uint));
|
||||
if (map) memcpy(ret.data(), map, pbo_queried * sizeof(uint));
|
||||
pbo.unmap(f);
|
||||
pbo.release(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QVector<QVector4D> Framebuffer::getPointsFloat() const {
|
||||
QVector<QVector4D> ret;
|
||||
if (!pbo.isInit() || (pbo_queried == 0)) return ret;
|
||||
ret.resize(pbo_queried);
|
||||
pbo.bind(f);
|
||||
void * map = pbo.map(f, GL_MAP_READ_BIT, pbo_queried * sizeof(QVector4D));
|
||||
if (map) memcpy(ret.data(), map, pbo_queried * sizeof(QVector4D));
|
||||
pbo.unmap(f);
|
||||
pbo.release(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QImage Framebuffer::getImage() const {
|
||||
QImage ret;
|
||||
if (!pbo.isInit() || (pbo_queried == 0)) return ret;
|
||||
ret = QImage(size(), QImage::Format_RGBA8888);
|
||||
int bytes = width() * height() * 4;
|
||||
pbo.bind(f);
|
||||
void * map = pbo.map(f, GL_MAP_READ_BIT, bytes);
|
||||
if (map) memcpy(ret.bits(), map, bytes);
|
||||
pbo.unmap(f);
|
||||
pbo.release(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QVector<float> Framebuffer::grabF(int index) const {
|
||||
QVector<float> ret;
|
||||
if (index < 0 || index >= colors.size()) return ret;
|
||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
|
||||
ret.resize(wid * hei * 4);
|
||||
f->glReadPixels(0, 0, wid, hei, GL_RGBA, GL_FLOAT, ret.data());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::blit(int index_from, GLuint fb_to, int index_to, QRect from, QRect to, GLbitfield mask, GLenum filter) const {
|
||||
if (index_from < 0 || index_from >= colors.size()) return;
|
||||
GLenum e = GL_COLOR_ATTACHMENT0 + index_to;
|
||||
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_to);
|
||||
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index_from);
|
||||
f->glDrawBuffers(1, &e);
|
||||
f->glBlitFramebuffer(from.x(), from.y(), from.right(), from.bottom(), to.x(), to.y(), to.right(), to.bottom(), mask, filter);
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::bind() {
|
||||
if (is_changed) resize(wid, hei);
|
||||
if (fbo == 0) return;
|
||||
f->glGetIntegerv(GL_VIEWPORT, prev_view);
|
||||
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
setWriteBuffers();
|
||||
f->glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
f->glViewport(0, 0, wid, hei);
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::release() {
|
||||
is_changed = false;
|
||||
if (fbo == 0) return;
|
||||
f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
f->glViewport(prev_view[0], prev_view[1], prev_view[2], prev_view[3]);
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::setWriteBuffer(int index) {
|
||||
unsetWriteBuffers();
|
||||
GLenum e = GL_COLOR_ATTACHMENT0 + index;
|
||||
f->glDrawBuffers(1, &e);
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::setWriteBuffers(const int * indeces, int count) {
|
||||
unsetWriteBuffers();
|
||||
QVector<GLenum> buffers;
|
||||
for (int i = 0; i < count; ++i)
|
||||
buffers << GL_COLOR_ATTACHMENT0 + indeces[i];
|
||||
f->glDrawBuffers(buffers.size(), buffers.constData());
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::setWriteBuffers() {
|
||||
QVector<GLenum> buffers;
|
||||
for (int i = 0; i < colors.size(); ++i)
|
||||
buffers << GL_COLOR_ATTACHMENT0 + i;
|
||||
f->glDrawBuffers(buffers.size(), buffers.constData());
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::unsetWriteBuffers() {
|
||||
QVector<GLenum> buffers(colors.size(), GL_NONE);
|
||||
f->glDrawBuffers(buffers.size(), buffers.constData());
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::enablePixelBuffer() {
|
||||
pbo.init(f);
|
||||
pbo.bind(f);
|
||||
pbo.resize(f, width() * height() * 4 * 4);
|
||||
pbo.release(f);
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::setColorTextureFiltering(int index, GLenum filter) {
|
||||
bindColorTexture(index);
|
||||
f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, filter);
|
||||
f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, filter);
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::bindColorTexture(int index, int channel) {
|
||||
if (index < 0 || index >= colors.size()) return;
|
||||
f->glActiveTexture(GL_TEXTURE0 + channel);
|
||||
f->glBindTexture(GL_TEXTURE_2D, colors[index]);
|
||||
}
|
||||
|
||||
|
||||
void Framebuffer::bindColorTextures() {
|
||||
for (int i = colors.size() - 1; i >= 0; --i) {
|
||||
f->glActiveTexture(GL_TEXTURE0 + i);
|
||||
f->glBindTexture(GL_TEXTURE_2D, colors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
97
src/core/core/glframebuffer.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
QGL Framebuffer
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLFRAMEBUFFER_H
|
||||
#define GLFRAMEBUFFER_H
|
||||
|
||||
#include "glbuffer.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT Framebuffer {
|
||||
friend class FramebufferMipmap;
|
||||
|
||||
public:
|
||||
Framebuffer(QOpenGLExtraFunctions * f_,
|
||||
int colorAttachments = 1,
|
||||
bool withDepth = true,
|
||||
GLenum colorFormat = GL_RGBA8,
|
||||
GLenum _target = GL_TEXTURE_2D);
|
||||
Framebuffer(QOpenGLExtraFunctions * f_, QVector<GLenum> colors_, bool withDepth = true, GLenum _target = GL_TEXTURE_2D);
|
||||
virtual ~Framebuffer();
|
||||
|
||||
GLuint id() const { return fbo; }
|
||||
GLuint colorTexture(int index = 0) const { return colors[index]; }
|
||||
GLuint depthTexture() const { return tex_d; }
|
||||
GLenum target() const { return target_; }
|
||||
bool isInit() const { return fbo != 0; }
|
||||
int width() const { return wid; }
|
||||
int height() const { return hei; }
|
||||
QSize size() const { return QSize(wid, hei); }
|
||||
QRect rect() const { return QRect(0, 0, wid, hei); }
|
||||
QImage grab() const;
|
||||
QVector<float> grabF(int index) const;
|
||||
void queryPoint(int index, QPoint p);
|
||||
void queryPoints(int index, QRect rect, GLenum pixel_format = GL_UNSIGNED_BYTE);
|
||||
void queryImage(int index);
|
||||
uint getPoint() const;
|
||||
QVector<uint> getPointsByte() const;
|
||||
QVector<QVector4D> getPointsFloat() const;
|
||||
QImage getImage() const;
|
||||
int queriedPoints() const { return pbo_queried; }
|
||||
void blit(int index_from,
|
||||
GLuint fb_to,
|
||||
int index_to,
|
||||
QRect from,
|
||||
QRect to,
|
||||
GLbitfield mask = GL_COLOR_BUFFER_BIT,
|
||||
GLenum filter = GL_NEAREST) const;
|
||||
|
||||
void resize(int width, int height, bool force = false);
|
||||
void reinit();
|
||||
void bind();
|
||||
void release();
|
||||
void setReadBuffer(int index) { glReadBuffer(GL_COLOR_ATTACHMENT0 + index); }
|
||||
void setWriteBuffer(int index);
|
||||
void setWriteBuffers(const int * indeces, int count);
|
||||
void setWriteBuffers(const QVector<int> & indeces) { setWriteBuffers(indeces.constData(), indeces.size()); }
|
||||
void setWriteBuffers();
|
||||
void unsetWriteBuffers();
|
||||
void enablePixelBuffer();
|
||||
void setColorTextureFiltering(int index, GLenum filter);
|
||||
|
||||
void copyDepthFrom(GLuint tex) { ; }
|
||||
void bindColorTexture(int index, int channel = 0);
|
||||
void bindColorTextures();
|
||||
void bindDepthTexture(int channel);
|
||||
|
||||
private:
|
||||
void deleteGLRenderbuffer(GLuint & drbo);
|
||||
void deleteGLFramebuffer(GLuint & fbo);
|
||||
|
||||
bool is_depth, is_changed;
|
||||
int pbo_queried;
|
||||
QOpenGLExtraFunctions * f;
|
||||
mutable Buffer pbo;
|
||||
QVector<GLuint> colors;
|
||||
QVector<GLenum> color_formats;
|
||||
GLenum target_;
|
||||
GLuint fbo, drbo, tex_d;
|
||||
GLint prev_view[4], wid, hei;
|
||||
};
|
||||
|
||||
#endif // GLFRAMEBUFFER_H
|
||||
55
src/core/core/glframebuffer_mipmap.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
QGL FramebufferMipmap
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glframebuffer_mipmap.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QTime>
|
||||
|
||||
|
||||
FramebufferMipmap::FramebufferMipmap(const Framebuffer & fb, int index_from_, int levels): src_fb(fb) {
|
||||
index_from = index_from_;
|
||||
for (int i = 0; i < levels; ++i)
|
||||
fbo << new Framebuffer(fb.f, 1, false, fb.color_formats[index_from]);
|
||||
}
|
||||
|
||||
|
||||
FramebufferMipmap::~FramebufferMipmap() {}
|
||||
|
||||
|
||||
void FramebufferMipmap::resize() {
|
||||
QSize sz = src_fb.size();
|
||||
for (int i = 0; i < fbo.size(); ++i) {
|
||||
sz /= 2;
|
||||
fbo[i]->resize(sz.width(), sz.height());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FramebufferMipmap::create() {
|
||||
if (fbo.isEmpty()) return;
|
||||
src_fb.blit(index_from, fbo[0]->id(), 0, src_fb.rect(), fbo[0]->rect(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
for (int i = 0; i < fbo.size() - 1; ++i)
|
||||
fbo[i]->blit(0, fbo[i + 1]->id(), 0, fbo[i]->rect(), fbo[i + 1]->rect(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
}
|
||||
|
||||
|
||||
void FramebufferMipmap::reinit() {
|
||||
for (auto * f: fbo)
|
||||
f->reinit();
|
||||
}
|
||||
49
src/core/core/glframebuffer_mipmap.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
QGL FramebufferMipmap
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLFRAMEBUFFER_MIPMAP_H
|
||||
#define GLFRAMEBUFFER_MIPMAP_H
|
||||
|
||||
#include "glframebuffer.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT 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();
|
||||
void reinit();
|
||||
|
||||
private:
|
||||
int index_from;
|
||||
const Framebuffer & src_fb;
|
||||
QVector<Framebuffer *> fbo;
|
||||
};
|
||||
|
||||
#endif // GLFRAMEBUFFER_MIPMAP_H
|
||||
145
src/core/core/glframebuffereffectbase.cpp
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
QGL Framebuffer effect basic
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glframebuffereffectbase.h"
|
||||
|
||||
#include "renderer.h"
|
||||
|
||||
#include <QTime>
|
||||
|
||||
|
||||
FramebufferEffectBase::FramebufferEffectBase() {}
|
||||
|
||||
|
||||
FramebufferEffectBase::~FramebufferEffectBase() {
|
||||
/*if (fbo > 0) deleteGLFramebuffer(fbo);
|
||||
deleteGLRenderbuffer(drbo);
|
||||
for (int i = 0; i < colors.size(); ++i)
|
||||
deleteGLTexture(f, colors[i]);
|
||||
deleteGLTexture(f, tex_d);*/
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::resize(int width, int height, bool force) {
|
||||
/*if (fbo > 0) {
|
||||
if ((wid == width) && (hei == height) && !force) return;
|
||||
}
|
||||
wid = width;
|
||||
hei = height;
|
||||
if (fbo > 0) deleteGLFramebuffer(fbo);
|
||||
f->glGenFramebuffers(1, &fbo);
|
||||
f->glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
// qDebug() << "resize" << f << wid << hei << 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->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target_, colors[i], 0);
|
||||
}
|
||||
if (is_depth) {
|
||||
deleteGLTexture(f, tex_d);
|
||||
if (drbo > 0) 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->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, target_, tex_d, 0);
|
||||
}
|
||||
f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
if (pbo.isInit()) {
|
||||
enablePixelBuffer();
|
||||
}
|
||||
is_changed = false;*/
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::reinit() {
|
||||
/*pbo.reinit();
|
||||
colors.fill(0);
|
||||
fbo = drbo = 0;
|
||||
tex_d = 0;
|
||||
pbo_queried = 0;
|
||||
is_changed = true;*/
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::bindDeferredBuffer(int role, int channel) {
|
||||
r->fbo_ds.bindColorTexture(role, channel);
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::bindDepthBuffer(int channel) {
|
||||
r->fbo_ds.bindDepthTexture(channel);
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::bindPreviousOutput(int channel) {
|
||||
r->fbo_out.bindColorTexture(r->prev_write_plane, channel);
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::setOutputPlane(int channel) {
|
||||
r->fbo_out.setWriteBuffer(channel);
|
||||
r->prev_write_plane = r->cur_write_plane;
|
||||
r->cur_write_plane = channel;
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::drawScreen(QOpenGLShaderProgram * prog) {
|
||||
r->renderQuad(prog, r->quad, nullptr);
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::drawInternal() {
|
||||
if (!r) return;
|
||||
draw();
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::reloadShadersInternal() {
|
||||
if (!r) return;
|
||||
if (is_loaded) return;
|
||||
reloadShaders();
|
||||
is_loaded = true;
|
||||
}
|
||||
|
||||
|
||||
QVector<int> FramebufferEffectBase::getFreePlanes(int count) {
|
||||
return r->getFreePlanes(count);
|
||||
}
|
||||
|
||||
|
||||
int FramebufferEffectBase::previousOutputPlane() const {
|
||||
return r->prev_write_plane;
|
||||
}
|
||||
|
||||
|
||||
void FramebufferEffectBase::deleteShader(QOpenGLShaderProgram *& s) {
|
||||
if (!s) return;
|
||||
delete s;
|
||||
s = nullptr;
|
||||
}
|
||||
59
src/core/core/glframebuffereffectbase.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
QGL Framebuffer effect basic
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef glframebuffereffectbase_H
|
||||
#define glframebuffereffectbase_H
|
||||
|
||||
#include "glframebuffer.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT FramebufferEffectBase {
|
||||
friend class FramebufferMipmap;
|
||||
friend class Renderer;
|
||||
|
||||
public:
|
||||
FramebufferEffectBase();
|
||||
virtual ~FramebufferEffectBase();
|
||||
|
||||
virtual void draw() = 0;
|
||||
|
||||
protected:
|
||||
void resize(int width, int height, bool force = false);
|
||||
void reinit();
|
||||
|
||||
virtual int maxPlanesUsed() const { return 1; }
|
||||
virtual void reloadShaders() {}
|
||||
|
||||
void bindDeferredBuffer(int role, int channel);
|
||||
void bindDepthBuffer(int channel);
|
||||
void bindPreviousOutput(int channel);
|
||||
void setOutputPlane(int channel);
|
||||
void drawScreen(QOpenGLShaderProgram * prog);
|
||||
void drawInternal();
|
||||
void reloadShadersInternal();
|
||||
QVector<int> getFreePlanes(int count);
|
||||
int previousOutputPlane() const;
|
||||
static void deleteShader(QOpenGLShaderProgram *& s);
|
||||
|
||||
private:
|
||||
bool is_loaded = false;
|
||||
Renderer * r = nullptr;
|
||||
GLint wid, hei;
|
||||
};
|
||||
|
||||
#endif
|
||||
144
src/core/core/glmaterial.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
QGL Material
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gltexture_manager.h"
|
||||
#include "gltypes.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) {
|
||||
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();
|
||||
}
|
||||
129
src/core/core/glmaterial.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
QGL Material
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLMATERIAL_H
|
||||
#define GLMATERIAL_H
|
||||
|
||||
#include "chunkstream.h"
|
||||
#include "glshaders_types.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT 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 QGLENGINE_CORE_EXPORT 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;
|
||||
};
|
||||
|
||||
|
||||
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 << (cs.data());
|
||||
return s;
|
||||
}
|
||||
inline QDataStream & operator>>(QDataStream & s, Material *& m) {
|
||||
m = new Material();
|
||||
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
|
||||
433
src/core/core/glmesh.cpp
Normal file
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
QGL Mesh
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "glmesh.h"
|
||||
|
||||
#include "globject.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QTime>
|
||||
|
||||
using namespace QGLEngineShaders;
|
||||
|
||||
// static int _count = 0;
|
||||
|
||||
Mesh::Mesh(GLenum geom_type_)
|
||||
: geom_type(geom_type_)
|
||||
, buffer_geom(GL_ARRAY_BUFFER, GL_STATIC_DRAW)
|
||||
, buffer_ind(GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW) {
|
||||
hash_ = 0;
|
||||
changed = hash_changed = true;
|
||||
// qDebug() << "Mesh, now" << ++_count;
|
||||
}
|
||||
|
||||
|
||||
Mesh::~Mesh() {
|
||||
// qDebug() << "~Mesh, now" << --_count;
|
||||
}
|
||||
|
||||
|
||||
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::reinit() {
|
||||
buffer_geom.reinit();
|
||||
buffer_ind.reinit();
|
||||
QMapIterator<int, VertexObject *> it(vao_map);
|
||||
while (it.hasNext())
|
||||
it.next().value()->reinit();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
||||
void Mesh::destroy(QOpenGLExtraFunctions * f) {
|
||||
buffer_geom.destroy(f);
|
||||
buffer_ind.destroy(f);
|
||||
QList<VertexObject *> vaol = vao_map.values();
|
||||
foreach(VertexObject * vao, vaol)
|
||||
vao->destroy(f);
|
||||
qDeleteAll(vao_map);
|
||||
vao_map.clear();
|
||||
}
|
||||
|
||||
|
||||
void Mesh::calculateNormals() {
|
||||
normals_.resize(vertices_.size());
|
||||
QVector3D dv1, dv2, n;
|
||||
foreach(const Vector3i & t, triangles_) {
|
||||
QVector3D & v0(vertices_[t.p0]);
|
||||
QVector3D & v1(vertices_[t.p1]);
|
||||
QVector3D & v2(vertices_[t.p2]);
|
||||
dv1 = v1 - v0, dv2 = v2 - v0;
|
||||
n = QVector3D::crossProduct(dv1, dv2).normalized();
|
||||
normals_[t.p0] = n;
|
||||
normals_[t.p1] = n;
|
||||
normals_[t.p2] = n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Mesh::calculateTangents() {
|
||||
if (vertices_.isEmpty() || texcoords_.isEmpty()) return;
|
||||
if (texcoords_.size() != vertices_.size()) return;
|
||||
tangents_.resize(vertices_.size());
|
||||
bitangents_.resize(vertices_.size());
|
||||
// qDebug() << "calculateBinormals" << vcnt << tcnt << vertices_.size() << texcoords_.size() << "...";
|
||||
QVector3D dv1, dv2;
|
||||
QVector2D dt1, dt2;
|
||||
QVector3D tan, bitan;
|
||||
foreach(const Vector3i & t, triangles_) {
|
||||
QVector3D & v0(vertices_[t.p0]);
|
||||
QVector3D & v1(vertices_[t.p1]);
|
||||
QVector3D & v2(vertices_[t.p2]);
|
||||
QVector2D & t0(texcoords_[t.p0]);
|
||||
QVector2D & t1(texcoords_[t.p1]);
|
||||
QVector2D & t2(texcoords_[t.p2]);
|
||||
dv1 = v1 - v0, dv2 = v2 - v0;
|
||||
dt1 = t1 - t0, dt2 = t2 - t0;
|
||||
tan = (dv1 * dt2.y() - dv2 * dt1.y()).normalized();
|
||||
bitan = (dv2 * dt1.x() - dv1 * dt2.x()).normalized();
|
||||
tangents_[t.p0] = tan;
|
||||
tangents_[t.p1] = tan;
|
||||
tangents_[t.p2] = tan;
|
||||
bitangents_[t.p0] = bitan;
|
||||
bitangents_[t.p1] = bitan;
|
||||
bitangents_[t.p2] = bitan;
|
||||
// qDebug() << " t" << t << vi << ti << dv1.toQVector3D() << "...";
|
||||
}
|
||||
// qDebug() << "calculateBinormals" << vcnt << tcnt << tangents_.size();
|
||||
}
|
||||
|
||||
|
||||
VertexObject * Mesh::vaoByType(int type) {
|
||||
VertexObject *& vao(vao_map[type]);
|
||||
if (!vao) {
|
||||
vao = new VertexObject();
|
||||
}
|
||||
return vao;
|
||||
}
|
||||
|
||||
|
||||
bool Mesh::rebuffer(QOpenGLExtraFunctions * f) {
|
||||
changed = false;
|
||||
if (vertices_.isEmpty()) return true;
|
||||
if (normals_.isEmpty()) calculateNormals();
|
||||
calculateTangents();
|
||||
vert_count = qMin(vertices_.size(), normals_.size());
|
||||
vert_count = qMin(vert_count, tangents_.size());
|
||||
vert_count = qMin(vert_count, bitangents_.size());
|
||||
vert_count = qMin(vert_count, texcoords_.size());
|
||||
data_.resize(vert_count);
|
||||
for (int i = 0; i < vert_count; ++i) {
|
||||
Vertex & v(data_[i]);
|
||||
v.pos = vertices_[i];
|
||||
v.normal = normals_[i];
|
||||
v.tangent = tangents_[i];
|
||||
v.bitangent = bitangents_[i];
|
||||
v.tex = texcoords_[i];
|
||||
}
|
||||
int gsize = data_.size() * sizeof(Vertex);
|
||||
int tsize = triangles_.size() * sizeof(Vector3i);
|
||||
int lsize = lines_.size() * sizeof(Vector2i);
|
||||
|
||||
buffer_geom.bind(f);
|
||||
buffer_geom.resize(f, gsize);
|
||||
buffer_geom.load(f, data_.constData(), gsize);
|
||||
|
||||
buffer_ind.bind(f);
|
||||
if (geom_type == GL_TRIANGLES) {
|
||||
buffer_ind.resize(f, tsize);
|
||||
buffer_ind.load(f, triangles_.constData(), tsize);
|
||||
} else {
|
||||
buffer_ind.resize(f, lsize);
|
||||
buffer_ind.load(f, lines_.constData(), lsize);
|
||||
}
|
||||
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
|
||||
void Mesh::draw(QOpenGLExtraFunctions * f, int count, int type) {
|
||||
if (isEmpty()) return;
|
||||
if (!isInit()) init(f);
|
||||
if (changed) rebuffer(f);
|
||||
// qDebug() << "draw" << geom_type << vert_count << count;
|
||||
|
||||
VertexObject * vao = vaoByType(type);
|
||||
vao->bindBuffers(f, buffer_geom, buffer_ind);
|
||||
if (geom_type == GL_TRIANGLES)
|
||||
vao->draw(f, geom_type, triangles_.size() * 3, count);
|
||||
else
|
||||
vao->draw(f, geom_type, lines_.size() * 2, count);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::clear() {
|
||||
vertices_.clear();
|
||||
normals_.clear();
|
||||
tangents_.clear();
|
||||
bitangents_.clear();
|
||||
texcoords_.clear();
|
||||
triangles_.clear();
|
||||
lines_.clear();
|
||||
data_.clear();
|
||||
changed = hash_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void Mesh::loadObject(QOpenGLExtraFunctions * f, const Object & object, int type) {
|
||||
VertexObject * vao = vaoByType(type);
|
||||
vao->loadObject(f, object);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::loadObjects(QOpenGLExtraFunctions * f, const QVector<Object> & objects, int type) {
|
||||
VertexObject * vao = vaoByType(type);
|
||||
vao->loadObjects(f, objects);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels, int type) {
|
||||
VertexObject * vao = vaoByType(type);
|
||||
vao->loadSelections(f, sels);
|
||||
}
|
||||
|
||||
|
||||
uint Mesh::hash() const {
|
||||
if (hash_changed) {
|
||||
hash_changed = false;
|
||||
hash_ = qHashBits(vertices_.constData(), vertices_.size() * sizeof(QVector3D));
|
||||
hash_ ^= qHashBits(normals_.constData(), normals_.size() * sizeof(QVector3D));
|
||||
hash_ ^= qHashBits(texcoords_.constData(), texcoords_.size() * sizeof(QVector2D));
|
||||
hash_ ^= qHashBits(triangles_.constData(), triangles_.size() * sizeof(Vector3i));
|
||||
hash_ ^= qHashBits(lines_.constData(), lines_.size() * sizeof(Vector2i));
|
||||
}
|
||||
return hash_;
|
||||
}
|
||||
|
||||
|
||||
bool Mesh::isObjectsChanged(int type) const {
|
||||
return (const_cast<Mesh *>(this))->vaoByType(type)->isObjectsChanged();
|
||||
}
|
||||
|
||||
|
||||
bool Mesh::isSelectionChanged(int type) const {
|
||||
return (const_cast<Mesh *>(this))->vaoByType(type)->isSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
void Mesh::setObjectsChanged(int type, bool yes) {
|
||||
vaoByType(type)->setObjectsChanged(yes);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::setSelectionChanged(int type, bool yes) {
|
||||
vaoByType(type)->setSelectionChanged(yes);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::setAllObjectsChanged(bool yes) {
|
||||
QMapIterator<int, VertexObject *> it(vao_map);
|
||||
while (it.hasNext())
|
||||
it.next().value()->setObjectsChanged(yes);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::setAllSelectionChanged(bool yes) {
|
||||
QMapIterator<int, VertexObject *> it(vao_map);
|
||||
while (it.hasNext())
|
||||
it.next().value()->setSelectionChanged(yes);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::translatePoints(const QVector3D & dp) {
|
||||
QMatrix4x4 m;
|
||||
m.translate(dp);
|
||||
transformPoints(m);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::scalePoints(const QVector3D & dp) {
|
||||
QMatrix4x4 m;
|
||||
m.scale(dp);
|
||||
transformPoints(m);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::rotatePoints(const double & angle, const QVector3D & a) {
|
||||
QMatrix4x4 m;
|
||||
m.rotate(angle, a);
|
||||
transformPoints(m);
|
||||
}
|
||||
|
||||
|
||||
void Mesh::transformPoints(const QMatrix4x4 & mat) {
|
||||
if (vertices_.isEmpty()) return;
|
||||
int vcnt = vertices_.size(), ncnt = normals_.size();
|
||||
for (int i = 0; i < vcnt; ++i) {
|
||||
vertices_[i] = (mat * QVector4D(vertices_[i], 1)).toVector3D();
|
||||
if (i < ncnt) normals_[i] = (mat * QVector4D(normals_[i], 0)).toVector3D();
|
||||
}
|
||||
changed = hash_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void Mesh::flipNormals() {
|
||||
if (vertices_.isEmpty()) return;
|
||||
for (int i = 0; i < triangles_.size(); ++i)
|
||||
piSwap(triangles_[i].p1, triangles_[i].p2);
|
||||
for (int i = 0; i < lines_.size(); ++i)
|
||||
piSwap(lines_[i].p0, lines_[i].p1);
|
||||
int ncnt = normals_.size();
|
||||
for (int i = 0; i < ncnt; ++i)
|
||||
normals_[i] = -normals_[i];
|
||||
changed = hash_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void Mesh::append(const Mesh * m) {
|
||||
if (!m) return;
|
||||
if (m->isEmpty()) return;
|
||||
if (normals_.isEmpty()) calculateNormals();
|
||||
int vcnt = vertices_.size();
|
||||
vertices_.append(m->vertices_);
|
||||
normals_.append(m->normals_);
|
||||
texcoords_.append(m->texcoords_);
|
||||
QVector<Vector3i> tri = m->triangles_;
|
||||
for (int i = 0; i < tri.size(); ++i)
|
||||
tri[i] += vcnt;
|
||||
triangles_.append(tri);
|
||||
QVector<Vector2i> lin = m->lines_;
|
||||
for (int i = 0; i < lin.size(); ++i)
|
||||
lin[i] += vcnt;
|
||||
lines_.append(lin);
|
||||
}
|
||||
|
||||
|
||||
bool Mesh::saveToFile(const QString & filename) {
|
||||
if (filename.isEmpty()) return false;
|
||||
QFile f(filename);
|
||||
QByteArray ba;
|
||||
if (f.open(QFile::WriteOnly)) {
|
||||
QDataStream out(&ba, QFile::WriteOnly);
|
||||
out << vertices_ << normals_ << texcoords_ << triangles_ << lines_;
|
||||
ba = qCompress(ba);
|
||||
f.resize(0);
|
||||
f.write(ba);
|
||||
f.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Mesh::loadFromFile(const QString & filename) {
|
||||
if (filename.isEmpty()) return false;
|
||||
QFile f(filename);
|
||||
QByteArray ba;
|
||||
if (f.open(QFile::ReadOnly)) {
|
||||
ba = f.readAll();
|
||||
if (ba.isEmpty()) return false;
|
||||
ba = qUncompress(ba);
|
||||
QDataStream in(ba);
|
||||
in >> vertices_ >> normals_ >> texcoords_ >> triangles_ >> lines_;
|
||||
changed = hash_changed = true;
|
||||
f.close();
|
||||
return !isEmpty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Box3D Mesh::boundingBox() const {
|
||||
if (vertices_.isEmpty()) return Box3D();
|
||||
int vcnt = vertices_.size();
|
||||
// qDebug() << "calculateBinormals" << vcnt << tcnt << vertices_.size() << texcoords_.size() << "...";
|
||||
GLfloat mix, miy, miz, max, may, maz;
|
||||
QVector3D v0(vertices_[0]);
|
||||
mix = max = v0.x();
|
||||
miy = may = v0.y();
|
||||
miz = maz = v0.z();
|
||||
Box3D bound;
|
||||
for (int i = 1; i < vcnt; ++i) {
|
||||
const QVector3D & v(vertices_[i]);
|
||||
if (mix > v.x()) mix = v.x();
|
||||
if (max < v.x()) max = v.x();
|
||||
if (miy > v.y()) miy = v.y();
|
||||
if (may < v.y()) may = v.y();
|
||||
if (miz > v.z()) miz = v.z();
|
||||
if (maz < v.z()) maz = v.z();
|
||||
}
|
||||
bound.x = mix;
|
||||
bound.y = miy;
|
||||
bound.z = miz;
|
||||
bound.length = max - mix;
|
||||
bound.width = may - miy;
|
||||
bound.height = maz - miz;
|
||||
return bound;
|
||||
}
|
||||
|
||||
|
||||
QDataStream & operator<<(QDataStream & s, const Mesh * m) {
|
||||
ChunkStream cs;
|
||||
// qDebug() << "place VBO" << m.vertices_.size() << m.normals_.size() << m.texcoords_.size() << m.colors_.size() << "...";
|
||||
cs.add(1, m->vertices_).add(2, m->normals_).add(3, m->texcoords_).add(6, m->triangles_).add(7, m->lines_).add(10, int(m->geom_type));
|
||||
// qDebug() << "place VBO done" << cs.data().size() << "...";
|
||||
s << cs.data();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
QDataStream & operator>>(QDataStream & s, Mesh *& m) {
|
||||
m = new Mesh();
|
||||
ChunkStream cs(s);
|
||||
while (!cs.atEnd()) {
|
||||
switch (cs.read()) {
|
||||
case 1: cs.get(m->vertices_); break;
|
||||
case 2: cs.get(m->normals_); break;
|
||||
case 3: cs.get(m->texcoords_); break;
|
||||
case 6: cs.get(m->triangles_); break;
|
||||
case 7: cs.get(m->lines_); break;
|
||||
case 10: m->geom_type = cs.getData<int>(); break;
|
||||
}
|
||||
}
|
||||
m->changed = true;
|
||||
return s;
|
||||
}
|
||||
123
src/core/core/glmesh.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
QGL Mesh
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLMESH_H
|
||||
#define GLMESH_H
|
||||
|
||||
#include "glvertexobject.h"
|
||||
|
||||
#include <chunkstream.h>
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT Mesh {
|
||||
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();
|
||||
|
||||
void init(QOpenGLExtraFunctions * f);
|
||||
void destroy(QOpenGLExtraFunctions * f);
|
||||
bool rebuffer(QOpenGLExtraFunctions * f);
|
||||
void draw(QOpenGLExtraFunctions * f, int count, int type = 0);
|
||||
void reinit();
|
||||
void clear();
|
||||
void loadObject(QOpenGLExtraFunctions * f, const QGLEngineShaders::Object & object, int type = 0);
|
||||
void loadObjects(QOpenGLExtraFunctions * f, const QVector<QGLEngineShaders::Object> & objects, int type = 0);
|
||||
void loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels, int type = 0);
|
||||
|
||||
int verticesCount() const { return vertices_.size(); }
|
||||
int trianglesCount() const { return triangles_.size(); }
|
||||
int linesCount() const { return lines_.size(); }
|
||||
bool isInit() const { return buffer_geom.isInit(); }
|
||||
bool isEmpty() const { return vertices_.isEmpty(); }
|
||||
uint hash() const;
|
||||
|
||||
bool isObjectsChanged(int type = 0) const;
|
||||
bool isSelectionChanged(int type = 0) const;
|
||||
void setObjectsChanged(int type = 0, bool yes = true);
|
||||
void setSelectionChanged(int type = 0, bool yes = true);
|
||||
void setAllObjectsChanged(bool yes = true);
|
||||
void setAllSelectionChanged(bool yes = true);
|
||||
|
||||
QVector<QVector3D> & vertices() {
|
||||
changed = hash_changed = true;
|
||||
return vertices_;
|
||||
}
|
||||
QVector<QVector3D> & normals() {
|
||||
changed = hash_changed = true;
|
||||
return normals_;
|
||||
}
|
||||
QVector<QVector2D> & texcoords() {
|
||||
changed = hash_changed = true;
|
||||
return texcoords_;
|
||||
}
|
||||
QVector<Vector3i> & indicesTriangles() {
|
||||
changed = hash_changed = true;
|
||||
return triangles_;
|
||||
}
|
||||
QVector<Vector2i> & indicesLines() {
|
||||
changed = hash_changed = true;
|
||||
return lines_;
|
||||
}
|
||||
|
||||
void translatePoints(const QVector3D & dp);
|
||||
void translatePoints(const double & x, const double & y, const double & z) { translatePoints(QVector3D(x, y, z)); }
|
||||
void scalePoints(const QVector3D & dp);
|
||||
void scalePoints(const double & s) { scalePoints(QVector3D(s, s, s)); }
|
||||
void rotatePoints(const double & angle, const QVector3D & a);
|
||||
void rotatePoints(const double & angle, const double & x, const double & y, const double & z) {
|
||||
rotatePoints(angle, QVector3D(x, y, z));
|
||||
}
|
||||
void transformPoints(const QMatrix4x4 & mat);
|
||||
void flipNormals();
|
||||
void append(const Mesh * m);
|
||||
|
||||
bool saveToFile(const QString & filename);
|
||||
bool loadFromFile(const QString & filename);
|
||||
|
||||
Box3D boundingBox() const;
|
||||
|
||||
private:
|
||||
void calculateNormals();
|
||||
void calculateTangents();
|
||||
VertexObject * vaoByType(int type);
|
||||
|
||||
QVector<QVector3D> vertices_, normals_, tangents_, bitangents_;
|
||||
QVector<QVector2D> texcoords_;
|
||||
QVector<Vector3i> triangles_;
|
||||
QVector<Vector2i> lines_;
|
||||
|
||||
QVector<QGLEngineShaders::Vertex> data_;
|
||||
GLenum geom_type;
|
||||
Buffer buffer_geom, buffer_ind;
|
||||
QMap<int, VertexObject *> vao_map;
|
||||
mutable uint hash_;
|
||||
mutable bool hash_changed;
|
||||
int vert_count;
|
||||
bool changed;
|
||||
};
|
||||
|
||||
|
||||
QGLENGINE_CORE_EXPORT QDataStream & operator<<(QDataStream & s, const Mesh * m);
|
||||
QGLENGINE_CORE_EXPORT QDataStream & operator>>(QDataStream & s, Mesh *& m);
|
||||
|
||||
#endif // GLMESH_H
|
||||
437
src/core/core/glprimitives.cpp
Normal file
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
QGL Primitives
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glprimitives.h"
|
||||
|
||||
#include "glmesh.h"
|
||||
|
||||
|
||||
Mesh * Primitive::plane(float width, float length) {
|
||||
Mesh * ret = new Mesh();
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector3i> & i(ret->indicesTriangles());
|
||||
float hw = width / 2.f, hl = length / 2.f;
|
||||
for (int j = 0; j < 4; ++j)
|
||||
n << QVector3D(0., 0., 1.);
|
||||
t << QVector2D(0., 0.) << QVector2D(0., 1.) << QVector2D(1., 1.) << QVector2D(1., 0.);
|
||||
v << QVector3D(-hw, -hl, 0.) << QVector3D(-hw, hl, 0.) << QVector3D(hw, hl, 0.) << QVector3D(hw, -hl, 0.);
|
||||
i << Vector3i(0, 2, 1) << Vector3i(0, 3, 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::cube(float width, float length, float height) {
|
||||
Mesh * ret = new Mesh();
|
||||
QVector3D scale(width, length, height);
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector3i> & i(ret->indicesTriangles());
|
||||
float hs = 0.5f;
|
||||
int si = 0;
|
||||
QMatrix4x4 mat;
|
||||
|
||||
si = v.size();
|
||||
for (int j = 0; j < 4; ++j)
|
||||
n << QVector3D(0., -1., 0.);
|
||||
t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.);
|
||||
v << QVector3D(-hs, -hs, -hs) << QVector3D(hs, -hs, -hs) << QVector3D(hs, -hs, hs) << QVector3D(-hs, -hs, hs);
|
||||
i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3);
|
||||
|
||||
for (int r = 0; r < 3; ++r) {
|
||||
si = v.size();
|
||||
mat.rotate(90., 0., 0., 1.);
|
||||
QVector3D cn = mat.map(n[0]);
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
n << cn;
|
||||
v << mat.map(QVector4D(v[j])).toVector3D();
|
||||
}
|
||||
t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.);
|
||||
i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3);
|
||||
}
|
||||
|
||||
mat.setToIdentity();
|
||||
mat.rotate(90., 1., 0., 0.);
|
||||
for (int r = 0; r < 2; ++r) {
|
||||
si = v.size();
|
||||
mat.rotate(180., 1., 0., 0.);
|
||||
QVector3D cn = mat.map(n[0]);
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
n << cn;
|
||||
v << mat.map(QVector4D(v[j])).toVector3D();
|
||||
}
|
||||
t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.);
|
||||
i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3);
|
||||
}
|
||||
|
||||
for (int i = 0; i < v.size(); ++i)
|
||||
v[i] *= scale;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::ellipsoid(int segments_wl, int segments_h, float radius, float end_angle) {
|
||||
Mesh * ret = new Mesh();
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector3i> & ind(ret->indicesTriangles());
|
||||
int hseg = segments_h + 1, wlseg = segments_wl + 1;
|
||||
double crw, crl, a, ch, twl;
|
||||
double eang = deg2rad * end_angle;
|
||||
|
||||
QVector3D cp;
|
||||
for (int i = 0; i <= hseg; i++) {
|
||||
ch = -cos((double)i / hseg * M_PI);
|
||||
cp.setZ(ch * radius);
|
||||
twl = sqrt(1. - ch * ch);
|
||||
crw = twl * radius;
|
||||
crl = twl * radius;
|
||||
int cvcnt = wlseg * 2;
|
||||
for (int j = 0; j < cvcnt; j++) {
|
||||
a = (double)j / (cvcnt - 1) * eang;
|
||||
cp.setX(crl * cos(a));
|
||||
cp.setY(crw * sin(a));
|
||||
v << cp;
|
||||
t << QVector2D((double)j / (cvcnt - 1), ch / 2.f + 0.5f);
|
||||
n << cp.normalized();
|
||||
int si = v.size() - 1;
|
||||
if (j > 0 && i > 0) {
|
||||
ind << Vector3i(si - cvcnt - 1, si, si - 1);
|
||||
ind << Vector3i(si - cvcnt, si, si - cvcnt - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (end_angle < 360.) {
|
||||
Mesh * cap = Primitive::disc(segments_h + 1, radius, 180);
|
||||
cap->rotatePoints(90, 0, 1, 0);
|
||||
cap->rotatePoints(-90, 0, 0, 1);
|
||||
ret->append(cap);
|
||||
cap->flipNormals();
|
||||
cap->rotatePoints(end_angle, 0, 0, 1);
|
||||
ret->append(cap);
|
||||
delete cap;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::disc(int segments, float radius, float end_angle) {
|
||||
Mesh * ret = new Mesh();
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector3i> & ind(ret->indicesTriangles());
|
||||
|
||||
segments = qMax(segments + 1, 4);
|
||||
QVector3D cp;
|
||||
v << QVector3D();
|
||||
n << QVector3D(0, 0, 1);
|
||||
t << QVector2D(0.5f, 0.5f);
|
||||
end_angle *= deg2rad;
|
||||
for (int i = 0; i < segments; i++) {
|
||||
double a = (double)i / (segments - 1) * end_angle;
|
||||
cp.setX(radius * cos(a));
|
||||
cp.setY(radius * sin(a));
|
||||
v << cp;
|
||||
n << QVector3D(0, 0, 1);
|
||||
t << QVector2D(cp.x() / radius + 1, cp.y() / radius + 1);
|
||||
int si = v.size() - 1;
|
||||
if (i > 0) ind << Vector3i(si - 1, si, 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QVector3D coneNormal(double r, double height, double ang) {
|
||||
QVector3D norm;
|
||||
norm.setX(r * cos(ang));
|
||||
norm.setY(r * sin(ang));
|
||||
norm.setZ(0.);
|
||||
double rl = norm.length();
|
||||
double ca = atan2(rl, height);
|
||||
norm *= cos(ca);
|
||||
norm.setZ(norm.length() * tan(ca));
|
||||
return norm.normalized();
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::cone(int segments, float radius, float height) {
|
||||
Mesh * ret = new Mesh();
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector3i> & ind(ret->indicesTriangles());
|
||||
|
||||
int seg = qMax(segments + 1, 4);
|
||||
QVector3D cp;
|
||||
for (int i = 0; i < seg; i++) {
|
||||
double a = (double)i / (seg - 1) * M_2PI;
|
||||
cp.setX(radius * cos(a));
|
||||
cp.setY(radius * sin(a));
|
||||
if (i > 0) {
|
||||
v << QVector3D(0, 0, height);
|
||||
t << QVector2D((double)(i - 1) / (seg - 1), 1.f);
|
||||
double ta = ((double)i - 0.5) / (seg - 1) * M_2PI;
|
||||
n << coneNormal(radius, height, ta);
|
||||
}
|
||||
v << cp;
|
||||
t << QVector2D((double)i / (seg - 1), 0.f);
|
||||
n << coneNormal(radius, height, a);
|
||||
int si = v.size() - 1;
|
||||
if (i > 0) ind << Vector3i(si - 1, si - 2, si);
|
||||
}
|
||||
|
||||
Mesh * cap = Primitive::disc(segments, radius);
|
||||
cap->flipNormals();
|
||||
ret->append(cap);
|
||||
delete cap;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::cylinder(int segments, float radius, float height, float end_angle) {
|
||||
Mesh * ret = new Mesh();
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector3i> & ind(ret->indicesTriangles());
|
||||
|
||||
int seg = qMax(segments + 1, 4);
|
||||
QVector3D cp, norm;
|
||||
double eang = deg2rad * end_angle;
|
||||
|
||||
for (int i = 0; i < seg; i++) {
|
||||
double a = (double)i / (seg - 1) * eang;
|
||||
cp.setX(radius * cos(a));
|
||||
cp.setY(radius * sin(a));
|
||||
cp.setZ(0.);
|
||||
norm = cp.normalized();
|
||||
v << cp;
|
||||
cp.setZ(height);
|
||||
v << cp;
|
||||
t << QVector2D((double)i / (seg - 1), 0.f);
|
||||
t << QVector2D((double)i / (seg - 1), 1.f);
|
||||
n << norm;
|
||||
n << norm;
|
||||
int si = v.size() - 1;
|
||||
if (i > 0) {
|
||||
ind << Vector3i(si - 2, si - 1, si);
|
||||
ind << Vector3i(si - 1, si - 2, si - 3);
|
||||
}
|
||||
}
|
||||
|
||||
Mesh * cap = Primitive::disc(segments, radius, end_angle);
|
||||
cap->flipNormals();
|
||||
ret->append(cap);
|
||||
cap->translatePoints(QVector3D(0., 0., height));
|
||||
cap->flipNormals();
|
||||
ret->append(cap);
|
||||
delete cap;
|
||||
|
||||
if (end_angle < 360.) {
|
||||
Mesh * cap = Primitive::plane(radius, height);
|
||||
cap->rotatePoints(90, 1, 0, 0);
|
||||
cap->translatePoints(radius / 2, 0, height / 2);
|
||||
ret->append(cap);
|
||||
cap->flipNormals();
|
||||
cap->rotatePoints(end_angle, 0, 0, 1);
|
||||
ret->append(cap);
|
||||
delete cap;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::arrow(int segments, float thick, float angle) {
|
||||
double cone_r = 1.5 * thick;
|
||||
double cone_h = 2. * cone_r / tan(angle * deg2rad);
|
||||
Mesh * ret = new Mesh();
|
||||
Mesh * m = Primitive::cylinder(segments, thick / 2., 1. - cone_h);
|
||||
ret->append(m);
|
||||
delete m;
|
||||
m = Primitive::cone(segments, cone_r, cone_h);
|
||||
m->translatePoints(QVector3D(0., 0., 1. - cone_h));
|
||||
ret->append(m);
|
||||
delete m;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::torus(int segments_main, int segments_second, float radius_main, float radius_second, float end_angle) {
|
||||
Mesh * ret = new Mesh();
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector3i> & ind(ret->indicesTriangles());
|
||||
|
||||
QVector<QVector3D> cv, cn;
|
||||
QVector<QVector2D> ct;
|
||||
segments_second = qMax(segments_second + 1, 4);
|
||||
for (int i = 0; i < segments_second; i++) {
|
||||
double x = (double)i / (segments_second - 1);
|
||||
double a = x * M_2PI;
|
||||
cv << QVector3D(radius_second * cos(a), 0., radius_second * sin(a));
|
||||
cn << cv.back().normalized();
|
||||
ct << QVector2D(0., x);
|
||||
cv.back() += QVector3D(radius_main, 0., 0.);
|
||||
}
|
||||
|
||||
segments_main = qMax(segments_main + 1, 4);
|
||||
int ccnt = cv.size(), pcnt = 0;
|
||||
for (int i = 0; i < segments_main; i++) {
|
||||
double x = (double)i / (segments_main - 1);
|
||||
QMatrix4x4 rm;
|
||||
rm.rotate(x * end_angle, 0., 0., 1.);
|
||||
for (int j = 0; j < ccnt; j++) {
|
||||
ct[j].setX(x);
|
||||
v << rm.map(cv[j]);
|
||||
n << rm.map(cn[j]);
|
||||
}
|
||||
t.append(ct);
|
||||
if (i > 0) {
|
||||
for (int j = 0; j < ccnt - 1; j++) {
|
||||
ind << Vector3i(pcnt + j, pcnt + j + 1, pcnt + j - ccnt);
|
||||
ind << Vector3i(pcnt + j + 1, pcnt + j + 1 - ccnt, pcnt + j - ccnt);
|
||||
}
|
||||
}
|
||||
pcnt = v.size();
|
||||
}
|
||||
if (end_angle < 360.) {
|
||||
Mesh * cap = Primitive::disc(segments_second - 1, radius_second);
|
||||
cap->rotatePoints(90, 1, 0, 0);
|
||||
cap->translatePoints(radius_main, 0, 0);
|
||||
ret->append(cap);
|
||||
cap->flipNormals();
|
||||
cap->rotatePoints(end_angle, 0, 0, 1);
|
||||
ret->append(cap);
|
||||
delete cap;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::cubeFrame(float width, float length, float height) {
|
||||
Mesh * ret = new Mesh(GL_LINES);
|
||||
QVector3D scale(width, length, height);
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector2i> & i(ret->indicesLines());
|
||||
float hs = 0.5f;
|
||||
v << QVector3D(-hs, -hs, -hs) << QVector3D(-hs, hs, -hs) << QVector3D(hs, hs, -hs) << QVector3D(hs, -hs, -hs);
|
||||
v << QVector3D(-hs, -hs, hs) << QVector3D(-hs, hs, hs) << QVector3D(hs, hs, hs) << QVector3D(hs, -hs, hs);
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
v[j] *= scale;
|
||||
t << QVector2D(0, 0);
|
||||
n << QVector3D(0, 0, 1);
|
||||
}
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
i << Vector2i(j, (j + 1) % 4);
|
||||
i << Vector2i(j, j + 4);
|
||||
i << Vector2i(j + 4, (j + 1) % 4 + 4);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::ellipsoidFrame(int segments_wl, int segments_h, float radius) {
|
||||
Mesh * ret = new Mesh(GL_LINES);
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector2i> & ind(ret->indicesLines());
|
||||
int hseg = segments_h + 1, wlseg = segments_wl + 1;
|
||||
double crw, crl, a, ch, twl;
|
||||
|
||||
QVector3D cp;
|
||||
for (int i = 0; i <= hseg; i++) {
|
||||
ch = -cos((double)i / hseg * M_PI);
|
||||
cp.setZ(ch * radius);
|
||||
twl = sqrt(1. - ch * ch);
|
||||
crw = twl * radius;
|
||||
crl = twl * radius;
|
||||
int cvcnt = wlseg * 2;
|
||||
for (int j = 0; j < cvcnt; j++) {
|
||||
a = (double)j / (cvcnt - 1) * M_2PI;
|
||||
cp.setX(crl * cos(a));
|
||||
cp.setY(crw * sin(a));
|
||||
v << cp;
|
||||
t << QVector2D((double)j / (cvcnt - 1), ch / 2.f + 0.5f);
|
||||
n << cp.normalized();
|
||||
int si = v.size() - 1;
|
||||
if (j > 0 && i > 0) {
|
||||
ind << Vector2i(si, si - 1);
|
||||
ind << Vector2i(si - cvcnt, si);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::coneFrame(int segments, float radius, float height) {
|
||||
Mesh * ret = new Mesh(GL_LINES);
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector2i> & ind(ret->indicesLines());
|
||||
|
||||
int seg = qMax(segments + 1, 4);
|
||||
QVector3D cp;
|
||||
for (int i = 0; i < seg; i++) {
|
||||
double a = (double)i / (seg - 1) * M_2PI;
|
||||
cp.setX(radius * cos(a));
|
||||
cp.setY(radius * sin(a));
|
||||
if (i > 0) {
|
||||
v << QVector3D(0, 0, height);
|
||||
t << QVector2D((double)(i - 1) / (seg - 1), 1.f);
|
||||
double ta = ((double)i - 0.5) / (seg - 1) * M_2PI;
|
||||
n << coneNormal(radius, height, ta);
|
||||
}
|
||||
v << cp;
|
||||
t << QVector2D((double)i / (seg - 1), 0.f);
|
||||
n << coneNormal(radius, height, a);
|
||||
int si = v.size() - 1;
|
||||
if (i > 0) {
|
||||
ind << Vector2i(si - 1, si);
|
||||
ind << Vector2i(si - 2, si);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Mesh * Primitive::lineFrame(QVector3D p0, QVector3D p1) {
|
||||
Mesh * ret = new Mesh(GL_LINES);
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
QVector<Vector2i> & ind(ret->indicesLines());
|
||||
v << p0 << p1;
|
||||
n << QVector3D(0, 0, 1) << QVector3D(0, 0, 1);
|
||||
t << QVector2D(0, 0) << QVector2D(1, 0);
|
||||
ind << Vector2i(0, 1);
|
||||
return ret;
|
||||
}
|
||||
56
src/core/core/glprimitives.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
QGL Primitives
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLPRIMITIVE_CUBE_H
|
||||
#define GLPRIMITIVE_CUBE_H
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
|
||||
namespace Primitive {
|
||||
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * plane(float width = 1., float length = 1.);
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * cube(float width = 1., float length = 1., float height = 1.);
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * ellipsoid(int segments_wl, int segments_h, float radius = 1., float end_angle = 360.);
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * disc(int segments, float radius = 1., float end_angle = 360.);
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * cone(int segments, float radius = 1., float height = 1.);
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * cylinder(int segments, float radius = 1., float height = 1., float end_angle = 360.);
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * arrow(int segments = 16, float thick = 0.04, float angle = 30.); // length = 1
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh *
|
||||
torus(int segments_main = 30, int segments_second = 16, float radius_main = 2.5, float radius_second = 0.5, float end_angle = 360.);
|
||||
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * lineFrame(QVector3D p0, QVector3D p1);
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * cubeFrame(float width = 1., float length = 1., float height = 1.);
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * ellipsoidFrame(int segments_wl, int segments_h, float radius = 1.);
|
||||
|
||||
QGLENGINE_CORE_EXPORT Mesh * coneFrame(int segments, float radius = 1., float height = 1.);
|
||||
|
||||
} // namespace Primitive
|
||||
|
||||
#endif // GLPRIMITIVE_CUBE_H
|
||||
146
src/core/core/glshaders.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
QGLEngineShaders
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glshaders.h"
|
||||
|
||||
#include "glshaders_headers.h"
|
||||
#include "gltypes.h"
|
||||
#include "qglview.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 = QOpenGLShader::ShaderType();
|
||||
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 = QOpenGLShader::ShaderType();
|
||||
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;
|
||||
}
|
||||
33
src/core/core/glshaders.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
QGLEngineShaders
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLSHADERS_H
|
||||
#define GLSHADERS_H
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
namespace QGLEngineShaders {
|
||||
|
||||
QGLENGINE_CORE_EXPORT bool
|
||||
loadShadersMulti(QOpenGLShaderProgram *& prog, const QString & file, bool add_qgl = true, const QStringList & defines = QStringList());
|
||||
QGLENGINE_CORE_EXPORT bool
|
||||
loadShaders(QOpenGLShaderProgram *& prog, const QStringList & files, bool add_qgl = true, const QStringList & defines = QStringList());
|
||||
|
||||
} // namespace QGLEngineShaders
|
||||
|
||||
#endif // GLSHADERS_H
|
||||
117
src/core/core/glshaders_headers.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
QGLEngineShaders
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLSHADERS_HEADERS_H
|
||||
#define GLSHADERS_HEADERS_H
|
||||
|
||||
namespace QGLEngineShaders {
|
||||
|
||||
const int max_materials = 128;
|
||||
const int max_lights = 256;
|
||||
|
||||
const char qgl_common_head[] = "#version 400 core\n"
|
||||
"";
|
||||
|
||||
const char qgl_vertex_head[] = "layout(location = 1 ) in vec3 qgl_Vertex ;\n"
|
||||
"layout(location = 2 ) in vec3 qgl_Normal ;\n"
|
||||
"layout(location = 3 ) in vec3 qgl_Tangent ;\n"
|
||||
"layout(location = 4 ) in vec3 qgl_Bitangent ;\n"
|
||||
"layout(location = 5 ) in vec2 qgl_Texture ;\n"
|
||||
"layout(location = 6 ) in uint qgl_Material ;\n"
|
||||
"layout(location = 7 ) in uint qgl_ObjectSelected;\n"
|
||||
"layout(location = 8 ) in uint qgl_ObjectID ;\n"
|
||||
"layout(location = 9 ) in vec4 qgl_ObjectColor ;\n"
|
||||
"layout(location = 10) in mat4 qgl_ModelMatrix ;\n"
|
||||
"out vec2 qgl_FragTexture;\n"
|
||||
"flat out uint qgl_MaterialIndex;\n"
|
||||
"uniform mat4 qgl_ViewMatrix;\n"
|
||||
"uniform mat4 qgl_ViewProjMatrix;\n"
|
||||
"mat3 qgl_getNormalMatrix() {return inverse(mat3(qgl_ViewMatrix * qgl_ModelMatrix));}\n"
|
||||
"mat3 qgl_getTangentMatrix() {return mat3(qgl_ViewMatrix * qgl_ModelMatrix);}\n"
|
||||
"vec4 qgl_ftransform() {return qgl_ViewProjMatrix * (qgl_ModelMatrix * vec4(qgl_Vertex, 1));}\n"
|
||||
"";
|
||||
|
||||
const char qgl_fragment_head[] =
|
||||
"in vec2 qgl_FragTexture;\n"
|
||||
"flat in uint qgl_MaterialIndex;\n"
|
||||
"out vec4 qgl_FragData[gl_MaxDrawBuffers];\n"
|
||||
"uniform vec2 qgl_ViewSize;\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_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"
|
||||
"};\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"
|
||||
"";
|
||||
|
||||
} // namespace QGLEngineShaders
|
||||
|
||||
#endif // GLSHADERS_HEADERS_H
|
||||
100
src/core/core/glshaders_types.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
QGLEngineShaders
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glshaders_types.h"
|
||||
|
||||
|
||||
QGLEngineShaders::QGLMap::QGLMap() {
|
||||
offset = 0.;
|
||||
amount = 1.;
|
||||
scale = QVector2D(1., 1.);
|
||||
array_index = map_index = 0;
|
||||
}
|
||||
|
||||
|
||||
QGLEngineShaders::QGLMaterial::QGLMaterial() {
|
||||
color_diffuse = QVector4D(1., 1., 1., 0.);
|
||||
color_emission = QVector4D(0., 0., 0., 0.);
|
||||
transparency = 0.;
|
||||
reflectivity = 0.;
|
||||
iof = 0.;
|
||||
dispersion = 0.;
|
||||
map[mtNormal].map_index = emrBlue;
|
||||
map[mtRoughness].amount = 0.75;
|
||||
map[mtMetalness].amount = 0.25;
|
||||
}
|
||||
|
||||
|
||||
void QGLEngineShaders::prepareDrawGeom(QOpenGLExtraFunctions * f) {
|
||||
// qDebug() << "prepareDrawGeom";
|
||||
|
||||
f->glEnableVertexAttribArray(pos_loc);
|
||||
f->glEnableVertexAttribArray(normal_loc);
|
||||
f->glEnableVertexAttribArray(tangent_loc);
|
||||
f->glEnableVertexAttribArray(bitangent_loc);
|
||||
f->glEnableVertexAttribArray(tex_loc);
|
||||
|
||||
int size = sizeof(Vertex);
|
||||
f->glVertexAttribPointer(pos_loc, 3, GL_FLOAT, GL_FALSE, size, (const void *)pos_offset);
|
||||
f->glVertexAttribPointer(normal_loc, 3, GL_FLOAT, GL_FALSE, size, (const void *)normal_offset);
|
||||
f->glVertexAttribPointer(tangent_loc, 3, GL_FLOAT, GL_FALSE, size, (const void *)tangent_offset);
|
||||
f->glVertexAttribPointer(bitangent_loc, 3, GL_FLOAT, GL_FALSE, size, (const void *)bitangent_offset);
|
||||
f->glVertexAttribPointer(tex_loc, 2, GL_FLOAT, GL_FALSE, size, (const void *)tex_offset);
|
||||
}
|
||||
|
||||
|
||||
void QGLEngineShaders::prepareDrawObj(QOpenGLExtraFunctions * f) {
|
||||
// qDebug() << "prepareDrawObj";
|
||||
|
||||
f->glEnableVertexAttribArray(material_loc);
|
||||
f->glEnableVertexAttribArray(object_id_loc);
|
||||
f->glEnableVertexAttribArray(color_loc);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
f->glEnableVertexAttribArray(modelmatrix_loc + i);
|
||||
}
|
||||
|
||||
GLsizei size = sizeof(Object);
|
||||
f->glVertexAttribIPointer(material_loc, 1, GL_UNSIGNED_INT, size, (const void *)material_offset);
|
||||
f->glVertexAttribIPointer(object_id_loc, 1, GL_UNSIGNED_INT, size, (const void *)object_id_offset);
|
||||
f->glVertexAttribPointer(color_loc, 4, GL_FLOAT, GL_FALSE, size, (const void *)color_offset);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
f->glVertexAttribPointer(modelmatrix_loc + i,
|
||||
4,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
size,
|
||||
(const void *)(modelmatrix_offset + sizeof(QVector4D) * i));
|
||||
}
|
||||
|
||||
f->glVertexAttribDivisor(material_loc, 1);
|
||||
f->glVertexAttribDivisor(object_id_loc, 1);
|
||||
f->glVertexAttribDivisor(color_loc, 1);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
f->glVertexAttribDivisor(modelmatrix_loc + i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QGLEngineShaders::prepareDrawSel(QOpenGLExtraFunctions * f) {
|
||||
// qDebug() << "prepareDrawObj";
|
||||
|
||||
f->glEnableVertexAttribArray(is_selected_loc);
|
||||
GLsizei size = 1;
|
||||
f->glVertexAttribIPointer(is_selected_loc, 1, GL_UNSIGNED_BYTE, size, (const void *)is_selected_offset);
|
||||
f->glVertexAttribDivisor(is_selected_loc, 1);
|
||||
}
|
||||
142
src/core/core/glshaders_types.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
QGLEngineShaders
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLSHADERS_TYPES_H
|
||||
#define GLSHADERS_TYPES_H
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
namespace QGLEngineShaders {
|
||||
|
||||
|
||||
/// VBO
|
||||
|
||||
// geometry
|
||||
const GLsizei pos_offset = 0;
|
||||
const GLsizei normal_offset = sizeof(QVector3D) + pos_offset;
|
||||
const GLsizei tangent_offset = sizeof(QVector3D) + normal_offset;
|
||||
const GLsizei bitangent_offset = sizeof(QVector3D) + tangent_offset;
|
||||
const GLsizei tex_offset = sizeof(QVector3D) + bitangent_offset;
|
||||
|
||||
// object
|
||||
const GLsizei material_offset = 0;
|
||||
const GLsizei object_id_offset = sizeof(GLuint) + material_offset;
|
||||
const GLsizei color_offset = sizeof(GLuint) + object_id_offset;
|
||||
const GLsizei modelmatrix_offset = sizeof(QVector4D) + color_offset;
|
||||
|
||||
const GLsizei is_selected_offset = 0;
|
||||
|
||||
const GLuint pos_loc = 1; // qgl_Vertex
|
||||
const GLuint normal_loc = 2; // qgl_Normal
|
||||
const GLuint tangent_loc = 3; // qgl_Tangent
|
||||
const GLuint bitangent_loc = 4; // qgl_Bitangent
|
||||
const GLuint tex_loc = 5; // qgl_Texture
|
||||
|
||||
const GLuint material_loc = 6; // qgl_Material
|
||||
const GLuint object_id_loc = 8; // qgl_ObjectID
|
||||
const GLuint color_loc = 9; // qgl_ObjectColor
|
||||
const GLuint modelmatrix_loc = 10; // qgl_ModelViewProjectionMatrix
|
||||
|
||||
const GLuint is_selected_loc = 7; // qgl_ObjectSelected
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Vertex {
|
||||
QVector3D pos;
|
||||
QVector3D normal;
|
||||
QVector3D tangent;
|
||||
QVector3D bitangent;
|
||||
QVector2D tex;
|
||||
};
|
||||
struct Object {
|
||||
Object() {
|
||||
material = object_id = 0;
|
||||
color = QVector4D(1, 1, 1, 1);
|
||||
QMatrix4x4().copyDataTo(modelmatrix);
|
||||
}
|
||||
GLuint material;
|
||||
GLuint object_id;
|
||||
QVector4D color;
|
||||
GLfloat modelmatrix[16];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
/// UBO
|
||||
|
||||
enum BindingPoints {
|
||||
bpMaterials,
|
||||
bpLightParameters,
|
||||
bpLightPositions,
|
||||
};
|
||||
|
||||
enum MapType {
|
||||
mtDiffuse = 0,
|
||||
mtNormal = 1,
|
||||
mtMetalness = 2,
|
||||
mtRoughness = 3,
|
||||
mtEmission = 4,
|
||||
mtRelief = 5,
|
||||
};
|
||||
enum TextureArrayRole {
|
||||
tarEmpty = 0,
|
||||
tarMaps = 1,
|
||||
};
|
||||
enum EmptyMapRole {
|
||||
emrWhite = 0,
|
||||
emrBlue = 1,
|
||||
};
|
||||
#define QGL_MAPS_COUNT 6
|
||||
#pragma pack(push, 1)
|
||||
struct QGLMap {
|
||||
QGLMap();
|
||||
GLfloat offset;
|
||||
GLfloat amount;
|
||||
QVector2D scale;
|
||||
GLuint array_index;
|
||||
GLuint map_index;
|
||||
GLfloat __res_2[2];
|
||||
};
|
||||
struct QGLMaterial {
|
||||
QGLMaterial();
|
||||
QVector4D color_diffuse;
|
||||
QVector4D color_emission;
|
||||
GLfloat transparency;
|
||||
GLfloat reflectivity;
|
||||
GLfloat iof;
|
||||
GLfloat dispersion;
|
||||
QGLMap map[QGL_MAPS_COUNT];
|
||||
};
|
||||
struct QGLLightParameter {
|
||||
QVector4D color;
|
||||
QVector4D decay_intensity; // [^0, ^1, ^2, intensity]
|
||||
QVector4D angles; // [start, cos(start), end, cos(end)]
|
||||
};
|
||||
struct QGLLightPosition {
|
||||
QVector4D position;
|
||||
QVector4D direction;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
void prepareDrawGeom(QOpenGLExtraFunctions * f);
|
||||
void prepareDrawObj(QOpenGLExtraFunctions * f);
|
||||
void prepareDrawSel(QOpenGLExtraFunctions * f);
|
||||
|
||||
|
||||
} // namespace QGLEngineShaders
|
||||
|
||||
#endif // GLSHADERS_TYPES_H
|
||||
101
src/core/core/gltexturearray.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
QGL Texture2DArray
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "gltexturearray.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
|
||||
|
||||
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::reinit() {
|
||||
texture_ = 0;
|
||||
layers_ = 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_);
|
||||
}
|
||||
54
src/core/core/gltexturearray.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
QGL Texture2DArray
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLTEXTUREARRAY_H
|
||||
#define GLTEXTUREARRAY_H
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT Texture2DArray {
|
||||
public:
|
||||
Texture2DArray(bool filter);
|
||||
~Texture2DArray();
|
||||
|
||||
void init(QOpenGLExtraFunctions * f);
|
||||
void destroy(QOpenGLExtraFunctions * f);
|
||||
void reinit();
|
||||
|
||||
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
|
||||
455
src/core/core/gltransform.cpp
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
QGL Transform
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "gltransform.h"
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
inline void composeQMatrix4x4(const QVector3D & position, const QVector3D & orientation, const QVector3D & scale, QMatrix4x4 & m) {
|
||||
const QMatrix3x3 rot3x3(Transform::toRotationMatrix(orientation));
|
||||
|
||||
// set up final matrix with scale, rotation and translation
|
||||
m(0, 0) = scale.x() * rot3x3(0, 0);
|
||||
m(0, 1) = scale.y() * rot3x3(0, 1);
|
||||
m(0, 2) = scale.z() * rot3x3(0, 2);
|
||||
m(0, 3) = position.x();
|
||||
m(1, 0) = scale.x() * rot3x3(1, 0);
|
||||
m(1, 1) = scale.y() * rot3x3(1, 1);
|
||||
m(1, 2) = scale.z() * rot3x3(1, 2);
|
||||
m(1, 3) = position.y();
|
||||
m(2, 0) = scale.x() * rot3x3(2, 0);
|
||||
m(2, 1) = scale.y() * rot3x3(2, 1);
|
||||
m(2, 2) = scale.z() * rot3x3(2, 2);
|
||||
m(2, 3) = position.z();
|
||||
// no projection term
|
||||
m(3, 0) = 0.0f;
|
||||
m(3, 1) = 0.0f;
|
||||
m(3, 2) = 0.0f;
|
||||
m(3, 3) = 1.0f;
|
||||
}
|
||||
|
||||
inline void decomposeQMatrix3x3(const QMatrix3x3 & m, QMatrix3x3 & Q, QVector3D & D, QVector3D & U) {
|
||||
// Factor M = QR = QDU where Q is orthogonal, D is diagonal,
|
||||
// and U is upper triangular with ones on its diagonal.
|
||||
// Algorithm uses Gram-Schmidt orthogonalization (the QR algorithm).
|
||||
//
|
||||
// If M = [ m0 | m1 | m2 ] and Q = [ q0 | q1 | q2 ], then
|
||||
// q0 = m0/|m0|
|
||||
// q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0|
|
||||
// q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1|
|
||||
//
|
||||
// where |V| indicates length of vector V and A*B indicates dot
|
||||
// product of vectors A and B. The matrix R has entries
|
||||
//
|
||||
// r00 = q0*m0 r01 = q0*m1 r02 = q0*m2
|
||||
// r10 = 0 r11 = q1*m1 r12 = q1*m2
|
||||
// r20 = 0 r21 = 0 r22 = q2*m2
|
||||
//
|
||||
// so D = diag(r00,r11,r22) and U has entries u01 = r01/r00,
|
||||
// u02 = r02/r00, and u12 = r12/r11.
|
||||
|
||||
// Q = rotation
|
||||
// D = scaling
|
||||
// U = shear
|
||||
|
||||
// D stores the three diagonal entries r00, r11, r22
|
||||
// U stores the entries U[0] = u01, U[1] = u02, U[2] = u12
|
||||
|
||||
// build orthogonal matrix Q
|
||||
float invLen = 1.0f / std::sqrt(m(0, 0) * m(0, 0) + m(1, 0) * m(1, 0) + m(2, 0) * m(2, 0));
|
||||
Q(0, 0) = m(0, 0) * invLen;
|
||||
Q(1, 0) = m(1, 0) * invLen;
|
||||
Q(2, 0) = m(2, 0) * invLen;
|
||||
|
||||
float dot = Q(0, 0) * m(0, 1) + Q(1, 0) * m(1, 1) + Q(2, 0) * m(2, 1);
|
||||
Q(0, 1) = m(0, 1) - dot * Q(0, 0);
|
||||
Q(1, 1) = m(1, 1) - dot * Q(1, 0);
|
||||
Q(2, 1) = m(2, 1) - dot * Q(2, 0);
|
||||
invLen = 1.0f / std::sqrt(Q(0, 1) * Q(0, 1) + Q(1, 1) * Q(1, 1) + Q(2, 1) * Q(2, 1));
|
||||
Q(0, 1) *= invLen;
|
||||
Q(1, 1) *= invLen;
|
||||
Q(2, 1) *= invLen;
|
||||
|
||||
dot = Q(0, 0) * m(0, 2) + Q(1, 0) * m(1, 2) + Q(2, 0) * m(2, 2);
|
||||
Q(0, 2) = m(0, 2) - dot * Q(0, 0);
|
||||
Q(1, 2) = m(1, 2) - dot * Q(1, 0);
|
||||
Q(2, 2) = m(2, 2) - dot * Q(2, 0);
|
||||
dot = Q(0, 1) * m(0, 2) + Q(1, 1) * m(1, 2) + Q(2, 1) * m(2, 2);
|
||||
Q(0, 2) -= dot * Q(0, 1);
|
||||
Q(1, 2) -= dot * Q(1, 1);
|
||||
Q(2, 2) -= dot * Q(2, 1);
|
||||
invLen = 1.0f / std::sqrt(Q(0, 2) * Q(0, 2) + Q(1, 2) * Q(1, 2) + Q(2, 2) * Q(2, 2));
|
||||
Q(0, 2) *= invLen;
|
||||
Q(1, 2) *= invLen;
|
||||
Q(2, 2) *= invLen;
|
||||
|
||||
// guarantee that orthogonal matrix has determinant 1 (no reflections)
|
||||
const float det = Q(0, 0) * Q(1, 1) * Q(2, 2) + Q(0, 1) * Q(1, 2) * Q(2, 0) + Q(0, 2) * Q(1, 0) * Q(2, 1) -
|
||||
Q(0, 2) * Q(1, 1) * Q(2, 0) - Q(0, 1) * Q(1, 0) * Q(2, 2) - Q(0, 0) * Q(1, 2) * Q(2, 1);
|
||||
if (det < 0.0f) Q *= -1.0f;
|
||||
|
||||
// build "right" matrix R
|
||||
QMatrix3x3 R(Qt::Uninitialized);
|
||||
R(0, 0) = Q(0, 0) * m(0, 0) + Q(1, 0) * m(1, 0) + Q(2, 0) * m(2, 0);
|
||||
R(0, 1) = Q(0, 0) * m(0, 1) + Q(1, 0) * m(1, 1) + Q(2, 0) * m(2, 1);
|
||||
R(1, 1) = Q(0, 1) * m(0, 1) + Q(1, 1) * m(1, 1) + Q(2, 1) * m(2, 1);
|
||||
R(0, 2) = Q(0, 0) * m(0, 2) + Q(1, 0) * m(1, 2) + Q(2, 0) * m(2, 2);
|
||||
R(1, 2) = Q(0, 1) * m(0, 2) + Q(1, 1) * m(1, 2) + Q(2, 1) * m(2, 2);
|
||||
R(2, 2) = Q(0, 2) * m(0, 2) + Q(1, 2) * m(1, 2) + Q(2, 2) * m(2, 2);
|
||||
|
||||
// the scaling component
|
||||
D[0] = R(0, 0);
|
||||
D[1] = R(1, 1);
|
||||
D[2] = R(2, 2);
|
||||
|
||||
// the shear component
|
||||
U[0] = R(0, 1) / D[0];
|
||||
U[1] = R(0, 2) / D[0];
|
||||
U[2] = R(1, 2) / D[1];
|
||||
}
|
||||
|
||||
inline bool hasScale(const QMatrix4x4 & m) {
|
||||
// If the columns are orthonormal and form a right-handed system, then there is no scale
|
||||
float t(m.determinant());
|
||||
if (!qFuzzyIsNull(t - 1.0f)) return true;
|
||||
t = m(0, 0) * m(0, 0) + m(1, 0) * m(1, 0) + m(2, 0) * m(2, 0);
|
||||
if (!qFuzzyIsNull(t - 1.0f)) return true;
|
||||
t = m(0, 1) * m(0, 1) + m(1, 1) * m(1, 1) + m(2, 1) * m(2, 1);
|
||||
if (!qFuzzyIsNull(t - 1.0f)) return true;
|
||||
t = m(0, 2) * m(0, 2) + m(1, 2) * m(1, 2) + m(2, 2) * m(2, 2);
|
||||
if (!qFuzzyIsNull(t - 1.0f)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void decomposeQMatrix4x4(const QMatrix4x4 & m, QVector3D & position, QVector3D & orientation, QVector3D & scale) {
|
||||
Q_ASSERT(m.isAffine());
|
||||
|
||||
const QMatrix3x3 m3x3(m.toGenericMatrix<3, 3>());
|
||||
|
||||
QMatrix3x3 rot3x3(Qt::Uninitialized);
|
||||
if (hasScale(m)) {
|
||||
decomposeQMatrix3x3(m3x3, rot3x3, scale, position);
|
||||
} else {
|
||||
// we know there is no scaling part; no need for QDU decomposition
|
||||
scale = QVector3D(1.0f, 1.0f, 1.0f);
|
||||
rot3x3 = m3x3;
|
||||
}
|
||||
orientation = Transform::fromRotationMatrix(rot3x3);
|
||||
position = QVector3D(m(0, 3), m(1, 3), m(2, 3));
|
||||
}
|
||||
|
||||
|
||||
Transform::Transform(): m_scale(1.0f, 1.0f, 1.0f), m_translation(), m_eulerRotationAngles(), m_matrixDirty(false) {}
|
||||
|
||||
|
||||
Transform & Transform::operator=(const Transform & t) {
|
||||
m_scale = t.m_scale;
|
||||
m_translation = t.m_translation;
|
||||
m_eulerRotationAngles = t.m_eulerRotationAngles;
|
||||
m_matrixDirty = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void Transform::setMatrix(const QMatrix4x4 & m) {
|
||||
if (m != matrix()) {
|
||||
m_matrix = m;
|
||||
m_matrixDirty = false;
|
||||
QVector3D s;
|
||||
QVector3D t;
|
||||
QVector3D r;
|
||||
decomposeQMatrix4x4(m, t, r, s);
|
||||
m_scale = s;
|
||||
m_translation = t;
|
||||
m_eulerRotationAngles = r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Transform::setRotationX(float r) {
|
||||
if (m_eulerRotationAngles.x() == r) return;
|
||||
m_eulerRotationAngles.setX(r);
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
|
||||
|
||||
void Transform::setRotationY(float r) {
|
||||
if (m_eulerRotationAngles.y() == r) return;
|
||||
m_eulerRotationAngles.setY(r);
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
|
||||
|
||||
void Transform::setRotationZ(float r) {
|
||||
if (m_eulerRotationAngles.z() == r) return;
|
||||
m_eulerRotationAngles.setZ(r);
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
|
||||
|
||||
QMatrix4x4 Transform::matrix() const {
|
||||
buildMatrix();
|
||||
return m_matrix;
|
||||
}
|
||||
|
||||
|
||||
QMatrix4x4 Transform::matrixRotate() const {
|
||||
buildMatrix();
|
||||
return m_matrixR;
|
||||
}
|
||||
|
||||
|
||||
QMatrix4x4 Transform::matrixScale() const {
|
||||
buildMatrix();
|
||||
return m_matrixS;
|
||||
}
|
||||
|
||||
|
||||
QMatrix4x4 Transform::matrixRotateScale() const {
|
||||
buildMatrix();
|
||||
return m_matrixWT;
|
||||
}
|
||||
|
||||
|
||||
QVector3D Transform::direction() const {
|
||||
return matrixRotate().mapVector(QVector3D(0, 0, -1)).normalized();
|
||||
}
|
||||
|
||||
|
||||
void Transform::buildMatrix() const {
|
||||
if (m_matrixDirty) {
|
||||
composeQMatrix4x4(m_translation, m_eulerRotationAngles, m_scale, m_matrix);
|
||||
composeQMatrix4x4(QVector3D(), m_eulerRotationAngles, m_scale, m_matrixWT);
|
||||
composeQMatrix4x4(QVector3D(), m_eulerRotationAngles, QVector3D(1, 1, 1), m_matrixR);
|
||||
composeQMatrix4x4(QVector3D(), QVector3D(), m_scale, m_matrixS);
|
||||
m_matrixDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float Transform::rotationX() const {
|
||||
return m_eulerRotationAngles.x();
|
||||
}
|
||||
|
||||
|
||||
float Transform::rotationY() const {
|
||||
return m_eulerRotationAngles.y();
|
||||
}
|
||||
|
||||
|
||||
float Transform::rotationZ() const {
|
||||
return m_eulerRotationAngles.z();
|
||||
}
|
||||
|
||||
|
||||
void Transform::setScale(const QVector3D & s) {
|
||||
if (s != m_scale) {
|
||||
m_scale = s;
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Transform::setScaleX(float s) {
|
||||
if (s != m_scale.x()) {
|
||||
m_scale.setX(s);
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Transform::setScaleY(float s) {
|
||||
if (s != m_scale.y()) {
|
||||
m_scale.setY(s);
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Transform::setScaleZ(float s) {
|
||||
if (s != m_scale.z()) {
|
||||
m_scale.setZ(s);
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QVector3D Transform::scale3D() const {
|
||||
return m_scale;
|
||||
}
|
||||
|
||||
|
||||
float Transform::scale() const {
|
||||
return m_scale.x();
|
||||
}
|
||||
|
||||
|
||||
void Transform::setRotation(const QVector3D & r) {
|
||||
if (r != m_eulerRotationAngles) {
|
||||
m_eulerRotationAngles = r;
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QVector3D Transform::rotation() const {
|
||||
return m_eulerRotationAngles;
|
||||
}
|
||||
|
||||
|
||||
void Transform::setTranslation(const QVector3D & t) {
|
||||
if (t != m_translation) {
|
||||
m_translation = t;
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Transform::setTranslationX(float t) {
|
||||
if (t != m_translation.x()) {
|
||||
m_translation.setX(t);
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Transform::setTranslationY(float t) {
|
||||
if (t != m_translation.y()) {
|
||||
m_translation.setY(t);
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Transform::setTranslationZ(float t) {
|
||||
if (t != m_translation.z()) {
|
||||
m_translation.setZ(t);
|
||||
m_matrixDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QVector3D Transform::translation() const {
|
||||
return m_translation;
|
||||
}
|
||||
|
||||
|
||||
QQuaternion Transform::fromAxisAndAngle(const QVector3D & axis, float angle) {
|
||||
return QQuaternion::fromAxisAndAngle(axis, angle);
|
||||
}
|
||||
|
||||
|
||||
QQuaternion Transform::fromAxisAndAngle(float x, float y, float z, float angle) {
|
||||
return QQuaternion::fromAxisAndAngle(x, y, z, angle);
|
||||
}
|
||||
|
||||
|
||||
QQuaternion Transform::fromAxesAndAngles(const QVector3D & axis1, float angle1, const QVector3D & axis2, float angle2) {
|
||||
const QQuaternion q1 = QQuaternion::fromAxisAndAngle(axis1, angle1);
|
||||
const QQuaternion q2 = QQuaternion::fromAxisAndAngle(axis2, angle2);
|
||||
return q2 * q1;
|
||||
}
|
||||
|
||||
|
||||
QQuaternion Transform::fromAxesAndAngles(const QVector3D & axis1,
|
||||
float angle1,
|
||||
const QVector3D & axis2,
|
||||
float angle2,
|
||||
const QVector3D & axis3,
|
||||
float angle3) {
|
||||
const QQuaternion q1 = QQuaternion::fromAxisAndAngle(axis1, angle1);
|
||||
const QQuaternion q2 = QQuaternion::fromAxisAndAngle(axis2, angle2);
|
||||
const QQuaternion q3 = QQuaternion::fromAxisAndAngle(axis3, angle3);
|
||||
return q3 * q2 * q1;
|
||||
}
|
||||
|
||||
|
||||
QQuaternion Transform::fromAxes(const QVector3D & xAxis, const QVector3D & yAxis, const QVector3D & zAxis) {
|
||||
return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
|
||||
}
|
||||
|
||||
|
||||
QVector3D Transform::fromDirection(QVector3D d, float pitch) {
|
||||
QVector3D ret;
|
||||
d.normalize();
|
||||
ret[0] = M_PI - acos(d.z());
|
||||
ret[1] = pitch * deg2rad;
|
||||
ret[2] = -atan2(d.x(), d.y());
|
||||
normalizeAngleRad(ret[0]);
|
||||
normalizeAngleRad(ret[2]);
|
||||
return ret * rad2deg;
|
||||
}
|
||||
|
||||
|
||||
QVector3D Transform::fromRotationMatrix(const QMatrix3x3 & m) {
|
||||
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);
|
||||
}
|
||||
110
src/core/core/gltransform.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
QGL Transform
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLTRANSFORM_H
|
||||
#define GLTRANSFORM_H
|
||||
|
||||
#include "qglengine_core_export.h"
|
||||
|
||||
#include <QMatrix4x4>
|
||||
#include <QQuaternion>
|
||||
#include <QVector3D>
|
||||
#include <chunkstream.h>
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT 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) {
|
||||
s << v.matrix();
|
||||
return s;
|
||||
}
|
||||
inline QDataStream & operator>>(QDataStream & s, Transform & v) {
|
||||
QMatrix4x4 m;
|
||||
s >> m;
|
||||
v.setMatrix(m);
|
||||
v.m_matrixDirty = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // QT3DCORE_QTRANSFORM_H
|
||||
388
src/core/core/gltypes.cpp
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
QGLView Types
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glcamera.h"
|
||||
#include "gltexture_manager.h"
|
||||
#include "qglview.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
|
||||
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) {
|
||||
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;
|
||||
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) {
|
||||
if (tex == 0) {
|
||||
f->glGenTextures(1, &tex);
|
||||
f->glBindTexture(target, tex);
|
||||
}
|
||||
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);
|
||||
// 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);
|
||||
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;
|
||||
ret(2, 3) = (e - 2.f) * 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_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());
|
||||
if (depth)
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
else
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
|
||||
Box3D::Box3D(const QVector<QVector3D> & points) {
|
||||
x = y = z = width = length = height = angle_z = angle_xy = angle_roll = 0.f;
|
||||
if (points.isEmpty()) return;
|
||||
float ix, iy, iz, ax, ay, az;
|
||||
ix = ax = points[0].x();
|
||||
iy = ay = points[0].y();
|
||||
iz = az = points[0].z();
|
||||
for (int i = 1; i < points.size(); ++i) {
|
||||
ix = qMin<float>(ix, points[i].x());
|
||||
ax = qMax<float>(ax, points[i].x());
|
||||
iy = qMin<float>(iy, points[i].y());
|
||||
ay = qMax<float>(ay, points[i].y());
|
||||
iz = qMin<float>(iz, points[i].z());
|
||||
az = qMax<float>(az, points[i].z());
|
||||
}
|
||||
x = ix;
|
||||
y = iy;
|
||||
z = iz;
|
||||
length = ax - ix;
|
||||
width = ay - iy;
|
||||
height = az - iz;
|
||||
}
|
||||
|
||||
|
||||
QVector<QVector3D> Box3D::corners() const {
|
||||
QVector<QVector3D> ret;
|
||||
ret << QVector3D(x, y, z) << QVector3D(x, y + width, z) << QVector3D(x, y, z + height) << QVector3D(x, y + width, z + height)
|
||||
<< QVector3D(x + length, y, z) << QVector3D(x + length, y + width, z) << QVector3D(x + length, y, z + height)
|
||||
<< QVector3D(x + length, y + width, z + height);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Box3D & Box3D::operator|=(const Box3D & o) {
|
||||
if (o.isEmpty()) return *this;
|
||||
if (isEmpty())
|
||||
*this = o;
|
||||
else {
|
||||
GLfloat mx = x + length, my = y + width, mz = z + height;
|
||||
GLfloat omx = o.x + o.length, omy = o.y + o.width, omz = o.z + o.height;
|
||||
x = qMin(x, o.x);
|
||||
y = qMin(y, o.y);
|
||||
z = qMin(z, o.z);
|
||||
mx = qMax(mx, omx);
|
||||
my = qMax(my, omy);
|
||||
mz = qMax(mz, omz);
|
||||
length = mx - x;
|
||||
width = my - y;
|
||||
height = mz - z;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
QVector3D vectorFromString(const QString & str) {
|
||||
QTextStream s(const_cast<QString *>(&str), QIODevice::ReadOnly);
|
||||
QVector3D ret;
|
||||
float f(0.f);
|
||||
s >> f;
|
||||
ret.setX(f);
|
||||
s >> f;
|
||||
ret.setY(f);
|
||||
s >> f;
|
||||
ret.setZ(f);
|
||||
return ret;
|
||||
}
|
||||
498
src/core/core/gltypes.h
Normal file
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
QGLView Types
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLTYPES_H
|
||||
#define GLTYPES_H
|
||||
|
||||
#if WIN32 || WIN64 || _WIN32 || _WIN64 || __WIN32__ || __WIN64__
|
||||
# define WINDOWS
|
||||
#endif
|
||||
#if __QNX__ || __QNXNTO__
|
||||
# define QNX
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
# define MAC
|
||||
#endif
|
||||
#ifndef WINDOWS
|
||||
# ifndef QNX
|
||||
# ifndef MAC
|
||||
# define LINUX
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#if __GNUC__
|
||||
# define CC_GCC
|
||||
#elif _MSC_VER
|
||||
# define CC_VC
|
||||
#endif
|
||||
|
||||
|
||||
#include <QObject>
|
||||
// #ifndef WINDOWS
|
||||
// # ifdef MAC
|
||||
// # include <OpenGL/gl.h>
|
||||
// # include <OpenGL/glu.h>
|
||||
// # include <GLUT/glut.h>
|
||||
// # else
|
||||
// # include <GL/gl.h>
|
||||
// # include <GL/glext.h>
|
||||
// # include <GL/glu.h>
|
||||
// # endif
|
||||
// #endif
|
||||
#include <QColor>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QImage>
|
||||
#include <QMatrix4x4>
|
||||
#include <QMutex>
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QOpenGLShader>
|
||||
#include <QOpenGLShaderProgram>
|
||||
#include <QVector2D>
|
||||
#include <QVector3D>
|
||||
#include <cmath>
|
||||
#include <float.h>
|
||||
#include <qopenglext.h>
|
||||
#ifndef QNX
|
||||
# include <cmath>
|
||||
# include <complex>
|
||||
#else
|
||||
# include <complex.h>
|
||||
# include <math.h>
|
||||
#endif
|
||||
#include "qglengine_core_export.h"
|
||||
#include "qglengine_version.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
|
||||
// #ifdef WINDOWS
|
||||
// # define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
|
||||
// # define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
|
||||
// #endif
|
||||
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
#ifndef M_2PI
|
||||
# define M_2PI 6.28318530717958647692
|
||||
#endif
|
||||
#ifndef M_PI_3
|
||||
# define M_PI_3 1.04719755119659774615
|
||||
#endif
|
||||
|
||||
#ifndef GL_RGBA16F
|
||||
# define GL_RGBA16F GL_RGBA16F_ARB
|
||||
#endif
|
||||
|
||||
using std::complex;
|
||||
|
||||
#ifndef PIP_VERSION
|
||||
typedef long long llong;
|
||||
typedef unsigned char uchar;
|
||||
typedef unsigned short int ushort;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned long long ullong;
|
||||
typedef long double ldouble;
|
||||
|
||||
const float deg2rad = atanf(1.f) / 45.f;
|
||||
const float rad2deg = 45.f / atanf(1.f);
|
||||
|
||||
# ifdef WINDOWS
|
||||
inline int random() {
|
||||
return rand();
|
||||
}
|
||||
# endif
|
||||
#else
|
||||
# define random randomi
|
||||
#endif
|
||||
|
||||
#ifdef CC_VC
|
||||
inline float round(const float & v) {
|
||||
return floor(v + 0.5);
|
||||
}
|
||||
#endif
|
||||
inline float randomu() {
|
||||
return float(random()) / RAND_MAX;
|
||||
}
|
||||
|
||||
inline const QSizeF operator*(const QSizeF & f, const QSizeF & s) {
|
||||
return QSizeF(f.width() * s.width(), f.height() * s.height());
|
||||
}
|
||||
#ifndef PIP_VERSION
|
||||
template<typename T>
|
||||
inline void piSwap(T & f, T & s) {
|
||||
T t(f);
|
||||
f = s;
|
||||
s = t;
|
||||
}
|
||||
template<typename Type>
|
||||
inline Type piMin(const Type & f, const Type & s) {
|
||||
return (f > s) ? s : f;
|
||||
}
|
||||
template<typename Type>
|
||||
inline Type piMin(const Type & f, const Type & s, const Type & t) {
|
||||
return (f < s && f < t) ? f : ((s < t) ? s : t);
|
||||
}
|
||||
template<typename Type>
|
||||
inline Type piMax(const Type & f, const Type & s) {
|
||||
return (f < s) ? s : f;
|
||||
}
|
||||
template<typename Type>
|
||||
inline Type piMax(const Type & f, const Type & s, const Type & t) {
|
||||
return (f > s && f > t) ? f : ((s > t) ? s : t);
|
||||
}
|
||||
template<typename Type>
|
||||
inline Type piClamp(const Type & v, const Type & min, const Type & max) {
|
||||
return (v > max ? max : (v < min ? min : v));
|
||||
}
|
||||
inline ushort letobe_s(ushort v) {
|
||||
return (v << 8) | (v >> 8);
|
||||
}
|
||||
inline uint letobe_i(const uint & v) {
|
||||
return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | ((v << 24) & 0xFF000000);
|
||||
}
|
||||
#endif
|
||||
// return [-1, 1]
|
||||
inline float urand(const float & scale = 1.) {
|
||||
return ((float)rand() / RAND_MAX - .5f) * (scale + scale);
|
||||
}
|
||||
// return [0, 1]
|
||||
inline float uprand(const float & scale = 1.) {
|
||||
return ((float)rand() / RAND_MAX) * scale;
|
||||
}
|
||||
QGLENGINE_CORE_EXPORT QString readCharsUntilNull(QDataStream & s);
|
||||
QGLENGINE_CORE_EXPORT QString findFile(const QString & file, const QStringList & pathes);
|
||||
inline QColor operator*(const QColor & c, float v) {
|
||||
return QColor(piClamp<int>(c.red() * v, 0, 255),
|
||||
piClamp<int>(c.green() * v, 0, 255),
|
||||
piClamp<int>(c.blue() * v, 0, 255),
|
||||
piClamp<int>(c.alpha() * v, 0, 255));
|
||||
}
|
||||
inline QColor operator/(const QColor & c, float v) {
|
||||
return QColor(piClamp<int>(c.red() / v, 0, 255),
|
||||
piClamp<int>(c.green() / v, 0, 255),
|
||||
piClamp<int>(c.blue() / v, 0, 255),
|
||||
piClamp<int>(c.alpha() / v, 0, 255));
|
||||
}
|
||||
|
||||
|
||||
inline void qglColor(const QColor & c) {
|
||||
glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF());
|
||||
}
|
||||
inline void glClearError() {
|
||||
int c = 100;
|
||||
while (glGetError() != GL_NO_ERROR && --c > 0)
|
||||
glGetError();
|
||||
}
|
||||
inline void glSetCapEnabled(GLenum cap, bool on = true) {
|
||||
if (on)
|
||||
glEnable(cap);
|
||||
else
|
||||
glDisable(cap);
|
||||
}
|
||||
inline void glSetPolygonMode(GLenum mode) {
|
||||
glPolygonMode(GL_FRONT_AND_BACK, mode);
|
||||
}
|
||||
inline void deleteGLTexture(QOpenGLExtraFunctions * f, GLuint & tex) {
|
||||
if (tex != 0) f->glDeleteTextures(1, &tex);
|
||||
tex = 0;
|
||||
}
|
||||
QGLENGINE_CORE_EXPORT void glEnableDepth();
|
||||
QGLENGINE_CORE_EXPORT void glDisableDepth();
|
||||
QGLENGINE_CORE_EXPORT void glClearFramebuffer(const QColor & color = Qt::black, bool depth = true);
|
||||
QGLENGINE_CORE_EXPORT 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);
|
||||
QGLENGINE_CORE_EXPORT void createGLTexture(QOpenGLExtraFunctions * f,
|
||||
GLuint & tex,
|
||||
int width,
|
||||
int height,
|
||||
const GLenum & format = GL_RGBA,
|
||||
const GLenum & target = GL_TEXTURE_2D);
|
||||
QGLENGINE_CORE_EXPORT void createGLTexture(QOpenGLExtraFunctions * f,
|
||||
GLuint & tex,
|
||||
const QImage & image,
|
||||
const GLenum & format = GL_RGBA,
|
||||
const GLenum & target = GL_TEXTURE_2D);
|
||||
QGLENGINE_CORE_EXPORT QMatrix4x4 glMatrixPerspective(float angle, float aspect, float near_);
|
||||
QGLENGINE_CORE_EXPORT QImage rotateQImageLeft(const QImage & im);
|
||||
QGLENGINE_CORE_EXPORT 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 FramebufferEffectBase;
|
||||
class VertexObject;
|
||||
class Mesh;
|
||||
class Scene;
|
||||
class RendererBase;
|
||||
class Renderer;
|
||||
class RendererMaterial;
|
||||
class RendererService;
|
||||
class RendererSelection;
|
||||
|
||||
enum RenderPass {
|
||||
rpSolid,
|
||||
rpTransparent,
|
||||
};
|
||||
|
||||
typedef QList<ObjectBase *> ObjectBaseList;
|
||||
|
||||
struct QGLENGINE_CORE_EXPORT Box3D {
|
||||
GLfloat x;
|
||||
GLfloat y;
|
||||
GLfloat z;
|
||||
GLfloat width;
|
||||
GLfloat length;
|
||||
GLfloat height;
|
||||
GLfloat angle_z;
|
||||
GLfloat angle_xy;
|
||||
GLfloat angle_roll;
|
||||
Box3D() { x = y = z = width = length = height = angle_z = angle_xy = angle_roll = 0.f; }
|
||||
Box3D(const QVector3D & center, GLfloat hwid, GLfloat hlen, GLfloat hhei) {
|
||||
x = center.x() - hwid;
|
||||
y = center.y() - hlen;
|
||||
z = center.z() - hhei;
|
||||
width = 2 * hwid;
|
||||
length = 2 * hlen;
|
||||
height = 2 * hhei;
|
||||
angle_z = angle_xy = angle_roll = 0.f;
|
||||
}
|
||||
Box3D(const QVector<QVector3D> & points);
|
||||
bool isEmpty() const { return (qAbs(width) < 1E-6f) && (qAbs(length) < 1E-6f) && (qAbs(height) < 1E-6f); }
|
||||
QVector3D randomPoint() const { return QVector3D(uprand(length) + x, uprand(width) + y, uprand(height) + z); }
|
||||
QVector3D pos() const { return QVector3D(x, y, z); }
|
||||
QVector3D size() const { return QVector3D(length, width, height); }
|
||||
QVector3D center() const { return QVector3D(length / 2.f + x, width / 2.f + y, height / 2.f + z); }
|
||||
QVector3D angles() const { return QVector3D(angle_xy, angle_roll, angle_z); }
|
||||
QVector<QVector3D> corners() const;
|
||||
void setPos(const QVector3D & p) {
|
||||
x = p.x();
|
||||
y = p.y();
|
||||
z = p.z();
|
||||
}
|
||||
void setAngles(const QVector3D & a) {
|
||||
angle_xy = a.x();
|
||||
angle_roll = a.y();
|
||||
angle_z = a.z();
|
||||
}
|
||||
void setSize(const QVector3D & s) {
|
||||
length = s.x();
|
||||
width = s.y();
|
||||
height = s.z();
|
||||
}
|
||||
Box3D & moveTo(const QVector3D & v) {
|
||||
x = v.x();
|
||||
y = v.y();
|
||||
z = v.z();
|
||||
return *this;
|
||||
}
|
||||
Box3D & move(const QVector3D & v) {
|
||||
x += v.x();
|
||||
y += v.y();
|
||||
z += v.z();
|
||||
return *this;
|
||||
}
|
||||
Box3D movedTo(const QVector3D & v) const {
|
||||
Box3D t(*this);
|
||||
t.x = v.x();
|
||||
t.y = v.y();
|
||||
t.z = v.z();
|
||||
return t;
|
||||
}
|
||||
Box3D moved(const QVector3D & v) const {
|
||||
Box3D t(*this);
|
||||
t.x += v.x();
|
||||
t.y += v.y();
|
||||
t.z += v.z();
|
||||
return t;
|
||||
}
|
||||
Box3D & operator|=(const Box3D & o);
|
||||
};
|
||||
|
||||
inline QDebug operator<<(QDebug d, const Box3D & v) {
|
||||
d << "Box3D {start (" << v.x << "," << v.y << "," << v.z << "), size (" << v.length << "," << v.width << "," << v.height << ")}";
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct QGLENGINE_CORE_EXPORT 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 QGLENGINE_CORE_EXPORT 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;
|
||||
}
|
||||
|
||||
|
||||
QGLENGINE_CORE_EXPORT QVector3D vectorFromString(const QString & str);
|
||||
QGLENGINE_CORE_EXPORT 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 normalizeAngleDeg360(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);
|
||||
}
|
||||
QGLENGINE_CORE_EXPORT QVector3D orthToVector(const QVector3D & v, const float & scale = 1.);
|
||||
QGLENGINE_CORE_EXPORT QVector3D rotateVector(const QVector3D & v, const QVector3D & a);
|
||||
QGLENGINE_CORE_EXPORT void setVectorLength(QVector3D & v, const float & l);
|
||||
QGLENGINE_CORE_EXPORT 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
|
||||
126
src/core/core/glvertexobject.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
QGL VertexObject
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "glvertexobject.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
|
||||
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::reinit() {
|
||||
vao_ = 0;
|
||||
buffer_obj.reinit();
|
||||
buffer_sel.reinit();
|
||||
buffers_binded = false;
|
||||
objects_changed = selected_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void VertexObject::bind(QOpenGLExtraFunctions * f) {
|
||||
// qDebug() << "bind" << target_ << buffer_;
|
||||
f->glBindVertexArray(vao_);
|
||||
}
|
||||
|
||||
|
||||
void VertexObject::release(QOpenGLExtraFunctions * f) {
|
||||
f->glBindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
void VertexObject::bindBuffers(QOpenGLExtraFunctions * f, Buffer & geom, Buffer & elem, bool force) {
|
||||
if (!force && buffers_binded) return;
|
||||
buffers_binded = true;
|
||||
|
||||
init(f);
|
||||
bind(f);
|
||||
|
||||
geom.bind(f);
|
||||
prepareDrawGeom(f);
|
||||
|
||||
elem.bind(f);
|
||||
|
||||
buffer_obj.bind(f);
|
||||
prepareDrawObj(f);
|
||||
|
||||
buffer_sel.bind(f);
|
||||
prepareDrawSel(f);
|
||||
|
||||
release(f);
|
||||
}
|
||||
|
||||
|
||||
void VertexObject::loadObject(QOpenGLExtraFunctions * f, const Object & object) {
|
||||
loadBuffer(f, buffer_obj, &object, sizeof(Object));
|
||||
}
|
||||
|
||||
|
||||
void VertexObject::loadObjects(QOpenGLExtraFunctions * f, const QVector<Object> & objects) {
|
||||
loadBuffer(f, buffer_obj, objects.constData(), objects.size() * sizeof(Object));
|
||||
}
|
||||
|
||||
|
||||
void VertexObject::loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels) {
|
||||
loadBuffer(f, buffer_sel, sels.constData(), sels.size());
|
||||
}
|
||||
|
||||
|
||||
void VertexObject::draw(QOpenGLExtraFunctions * f, GLenum geom_type, int vert_cout, int obj_count) {
|
||||
bind(f);
|
||||
f->glDrawElementsInstanced(geom_type, vert_cout, GL_UNSIGNED_INT, 0, obj_count);
|
||||
release(f);
|
||||
}
|
||||
|
||||
|
||||
void VertexObject::loadBuffer(QOpenGLExtraFunctions * f, Buffer & buf, const void * data, int size) {
|
||||
buf.init(f);
|
||||
if (!data) return;
|
||||
buf.bind(f);
|
||||
buf.resize(f, size);
|
||||
buf.load(f, data, size);
|
||||
}
|
||||
62
src/core/core/glvertexobject.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
QGL VertexObject
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLVERTEXOBJECT_H
|
||||
#define GLVERTEXOBJECT_H
|
||||
|
||||
#include "glbuffer.h"
|
||||
#include "glshaders_types.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT VertexObject {
|
||||
public:
|
||||
VertexObject();
|
||||
~VertexObject();
|
||||
|
||||
void init(QOpenGLExtraFunctions * f);
|
||||
void destroy(QOpenGLExtraFunctions * f);
|
||||
void reinit();
|
||||
|
||||
void bind(QOpenGLExtraFunctions * f);
|
||||
void release(QOpenGLExtraFunctions * f);
|
||||
|
||||
void bindBuffers(QOpenGLExtraFunctions * f, Buffer & geom, Buffer & elem, bool force = false);
|
||||
void loadObject(QOpenGLExtraFunctions * f, const QGLEngineShaders::Object & object);
|
||||
void loadObjects(QOpenGLExtraFunctions * f, const QVector<QGLEngineShaders::Object> & objects);
|
||||
void loadSelections(QOpenGLExtraFunctions * f, const QVector<uchar> & sels);
|
||||
|
||||
void draw(QOpenGLExtraFunctions * f, GLenum geom_type, int vert_cout, int obj_count);
|
||||
|
||||
GLuint ID() const { return vao_; }
|
||||
bool isInit() const { return vao_ != 0; }
|
||||
|
||||
bool isObjectsChanged() const { return objects_changed; }
|
||||
bool isSelectionChanged() const { return selected_changed; }
|
||||
void setObjectsChanged(bool yes = true) { objects_changed = yes; }
|
||||
void setSelectionChanged(bool yes = true) { selected_changed = yes; }
|
||||
|
||||
private:
|
||||
void loadBuffer(QOpenGLExtraFunctions * f, Buffer & buf, const void * data, int size);
|
||||
|
||||
GLuint vao_;
|
||||
Buffer buffer_obj, buffer_sel;
|
||||
bool buffers_binded, objects_changed, selected_changed;
|
||||
};
|
||||
|
||||
|
||||
#endif // GLVERTEXOBJECT_H
|
||||
122
src/core/core/hdr.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
QGL HDR
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hdr_p.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <qmath.h>
|
||||
|
||||
#define RGBE_DATA_RED 2
|
||||
#define RGBE_DATA_GREEN 1
|
||||
#define RGBE_DATA_BLUE 0
|
||||
/* number of floats per pixel */
|
||||
#define RGBE_DATA_SIZE 3
|
||||
|
||||
|
||||
void rgbe2float(float * red, float * green, float * blue, uchar rgbe[4]) {
|
||||
float f;
|
||||
if (rgbe[3]) {
|
||||
f = static_cast<float>(ldexp(1.0, rgbe[3] - (int)(128 + 8)));
|
||||
*red = rgbe[0] * f;
|
||||
*green = rgbe[1] * f;
|
||||
*blue = rgbe[2] * f;
|
||||
} else
|
||||
*red = *green = *blue = 0.0;
|
||||
}
|
||||
|
||||
|
||||
/* simple read routine. will not correctly handle run length encoding */
|
||||
bool RGBE_ReadPixels(QDataStream * fp, float * data, int numpixels) {
|
||||
uchar rgbe[4];
|
||||
while (numpixels-- > 0) {
|
||||
if (fp->readRawData((char *)rgbe, sizeof(rgbe)) < 1) return false;
|
||||
rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
|
||||
data += RGBE_DATA_SIZE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool RGBE_ReadPixels_RLE(QDataStream * fp, float * data, int scanline_width, int num_scanlines) {
|
||||
uchar rgbe[4], *ptr, *ptr_end;
|
||||
int i, count;
|
||||
uchar buf[2];
|
||||
QByteArray scanline_buffer;
|
||||
|
||||
if ((scanline_width < 8) || (scanline_width > 0x7fff)) /* run length encoding is not allowed so read flat*/
|
||||
return RGBE_ReadPixels(fp, data, scanline_width * num_scanlines);
|
||||
scanline_buffer.resize(4 * scanline_width);
|
||||
/* read in each successive scanline */
|
||||
while (num_scanlines > 0) {
|
||||
if (fp->readRawData((char *)rgbe, sizeof(rgbe)) < 1) {
|
||||
return false;
|
||||
}
|
||||
if ((rgbe[0] != 2) || (rgbe[1] != 2) || (rgbe[2] & 0x80)) {
|
||||
/* this file is not run length encoded */
|
||||
rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
|
||||
data += RGBE_DATA_SIZE;
|
||||
return RGBE_ReadPixels(fp, data, scanline_width * num_scanlines - 1);
|
||||
}
|
||||
if ((((int)rgbe[2]) << 8 | rgbe[3]) != scanline_width) {
|
||||
return false;
|
||||
}
|
||||
ptr = (uchar *)scanline_buffer.data();
|
||||
/* read each of the four channels for the scanline into the buffer */
|
||||
for (i = 0; i < 4; i++) {
|
||||
ptr_end = (uchar *)scanline_buffer.data() + ((i + 1) * scanline_width);
|
||||
while (ptr < ptr_end) {
|
||||
if (fp->readRawData((char *)buf, sizeof(buf[0]) * 2) < 1) {
|
||||
return false;
|
||||
}
|
||||
if (buf[0] > 128) {
|
||||
/* a run of the same value */
|
||||
count = buf[0] - 128;
|
||||
if ((count == 0) || (count > ptr_end - ptr)) {
|
||||
return false;
|
||||
}
|
||||
while (count-- > 0)
|
||||
*ptr++ = buf[1];
|
||||
} else {
|
||||
/* a non-run */
|
||||
count = buf[0];
|
||||
if ((count == 0) || (count > ptr_end - ptr)) {
|
||||
return false;
|
||||
}
|
||||
*ptr++ = buf[1];
|
||||
if (--count > 0) {
|
||||
if (fp->readRawData((char *)ptr, sizeof(*ptr) * count) < 1) {
|
||||
return false;
|
||||
}
|
||||
ptr += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* now convert data from buffer into floats */
|
||||
for (i = 0; i < scanline_width; i++) {
|
||||
rgbe[0] = scanline_buffer[i];
|
||||
rgbe[1] = scanline_buffer[i + scanline_width];
|
||||
rgbe[2] = scanline_buffer[i + 2 * scanline_width];
|
||||
rgbe[3] = scanline_buffer[i + 3 * scanline_width];
|
||||
rgbe2float(&data[RGBE_DATA_RED], &data[RGBE_DATA_GREEN], &data[RGBE_DATA_BLUE], rgbe);
|
||||
data += RGBE_DATA_SIZE;
|
||||
}
|
||||
num_scanlines--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
26
src/core/core/hdr_p.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
QGL HDR
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HDR_P_H
|
||||
#define HDR_P_H
|
||||
|
||||
#include <QDataStream>
|
||||
|
||||
bool RGBE_ReadPixels_RLE(QDataStream * fp, float * data, int scanline_width, int num_scanlines);
|
||||
|
||||
#endif // HDR_P_H
|
||||
278
src/core/formats/loader_assimp.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
QGL Loader Assimp
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "loader_assimp.h"
|
||||
|
||||
#include "glmaterial.h"
|
||||
#include "glmesh.h"
|
||||
#include "globject.h"
|
||||
#include "glscene.h"
|
||||
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/material.h>
|
||||
#include <assimp/mesh.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/scene.h>
|
||||
|
||||
QString fromAiString(const aiString & s) {
|
||||
return QString::fromLocal8Bit(s.C_Str());
|
||||
}
|
||||
QColor fromAiColor(const aiColor4D & c) {
|
||||
return QColor::fromRgbF(piClamp(c.r, 0.f, 1.f), piClamp(c.g, 0.f, 1.f), piClamp(c.b, 0.f, 1.f));
|
||||
}
|
||||
QVector3D fromAiVector3D(const aiVector3D & v) {
|
||||
return QVector3D(v.x, v.y, v.z);
|
||||
}
|
||||
Vector3i fromAiFace(const aiFace & v) {
|
||||
return Vector3i(v.mIndices[0], v.mIndices[1], v.mIndices[2]);
|
||||
}
|
||||
QMatrix4x4 fromAiMatrix4D(const aiMatrix4x4 & v) {
|
||||
return QMatrix4x4(v.a1, v.a2, v.a3, v.a4, v.b1, v.b2, v.b3, v.b4, v.c1, v.c2, v.c3, v.c4, v.d1, v.d2, v.d3, v.d4);
|
||||
}
|
||||
bool isAiMeshTriangles(const aiMesh * m) {
|
||||
return (m->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) == aiPrimitiveType_TRIANGLE;
|
||||
}
|
||||
|
||||
|
||||
Mesh * assimpMesh(const aiMesh * m) {
|
||||
if (!m) return 0;
|
||||
if (!isAiMeshTriangles(m)) return 0;
|
||||
Mesh * ret = new Mesh();
|
||||
int vcnt = m->mNumVertices, tcnt = m->mNumFaces;
|
||||
|
||||
QVector<QVector3D> & v(ret->vertices());
|
||||
v.resize(vcnt);
|
||||
QVector<QVector2D> & t(ret->texcoords());
|
||||
t.resize(vcnt);
|
||||
QVector<Vector3i> & ind(ret->indicesTriangles());
|
||||
|
||||
for (int i = 0; i < vcnt; ++i)
|
||||
v[i] = fromAiVector3D(m->mVertices[i]);
|
||||
|
||||
if (m->HasNormals()) {
|
||||
QVector<QVector3D> & n(ret->normals());
|
||||
n.resize(vcnt);
|
||||
for (int i = 0; i < vcnt; ++i)
|
||||
n[i] = fromAiVector3D(m->mNormals[i]);
|
||||
}
|
||||
|
||||
if (m->HasTextureCoords(0)) {
|
||||
for (int i = 0; i < vcnt; ++i)
|
||||
t[i] = fromAiVector3D(m->mTextureCoords[0][i]).toVector2D();
|
||||
}
|
||||
|
||||
if (m->HasFaces()) {
|
||||
ind.resize(tcnt);
|
||||
for (int i = 0; i < tcnt; ++i)
|
||||
ind[i] = fromAiFace(m->mFaces[i]);
|
||||
} else {
|
||||
tcnt = vcnt / 3;
|
||||
ind.resize(tcnt);
|
||||
int si = 0;
|
||||
for (int i = 0; i < tcnt; ++i) {
|
||||
si = i + i + i;
|
||||
ind[i] = Vector3i(si, si + 1, si + 2);
|
||||
}
|
||||
}
|
||||
|
||||
// qDebug() << "add mesh" << v.size() << ret->normals().size() << ret->texcoords().size() << ret->indices().size();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QColor aiMatColor(const aiMaterial * m, const char * key, uint s0, uint s1, const QColor & def = Qt::white) {
|
||||
aiColor4D col;
|
||||
aiReturn r = m->Get(key, s0, s1, col);
|
||||
// qDebug() << key << r << col.r << col.g << col.b;
|
||||
if (r != aiReturn_SUCCESS) return def;
|
||||
return fromAiColor(col);
|
||||
}
|
||||
float aiMatFloat(const aiMaterial * m, const char * key, uint s0, uint s1, float def = 0.f) {
|
||||
float ret;
|
||||
aiReturn r = m->Get(key, s0, s1, ret);
|
||||
if (r != aiReturn_SUCCESS) return def;
|
||||
return ret;
|
||||
}
|
||||
QString aiMatString(const aiMaterial * m, const char * key, uint s0, uint s1) {
|
||||
aiString p;
|
||||
aiReturn r = const_cast<aiMaterial *>(m)->Get(key, s0, s1, p);
|
||||
// qDebug() << "GetTexture" << key << s0 << r << fromAiString(p);
|
||||
if (r != aiReturn_SUCCESS) return QString();
|
||||
return fromAiString(p);
|
||||
}
|
||||
Material * assimpMaterial(const aiMaterial * m) {
|
||||
if (!m) return 0;
|
||||
Material * ret = new Material();
|
||||
/// WARNING: no function GetName() in aiMaterial in stable release
|
||||
// ret->name = fromAiString(const_cast<aiMaterial*>(m)->GetName());
|
||||
aiString name;
|
||||
const_cast<aiMaterial *>(m)->Get(AI_MATKEY_NAME, name);
|
||||
ret->name = fromAiString(name);
|
||||
// qDebug() << "mat" << ret->name;
|
||||
// for (int i = 0; i < m->mNumProperties; ++i) {
|
||||
// qDebug()<< fromAiString(m->mProperties[i]->mKey);// << "=" << aiMatFloat(m, m->mProperties[i]->mKey.C_Str(), 0, 0);
|
||||
// }
|
||||
ret->color_diffuse = aiMatColor(m, AI_MATKEY_COLOR_DIFFUSE);
|
||||
ret->color_emission = aiMatColor(m, AI_MATKEY_COLOR_EMISSIVE);
|
||||
float shine = aiMatFloat(m, AI_MATKEY_SHININESS, -1.f);
|
||||
if (shine >= 0) {
|
||||
ret->map_roughness.color_amount = 0.8f - (shine / 100.f * 0.6f);
|
||||
// qDebug() << "shine" << shine;
|
||||
}
|
||||
ret->map_diffuse.bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_DIFFUSE(0));
|
||||
ret->map_normal.bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_NORMALS(0));
|
||||
// ret->map_metalness.bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_SPECULAR(0));
|
||||
ret->map_roughness.bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_SHININESS(0));
|
||||
ret->map_emission.bitmap_path = aiMatString(m, AI_MATKEY_TEXTURE_EMISSIVE(0));
|
||||
ret->transparency = 1.f - aiMatFloat(m, AI_MATKEY_OPACITY, 1.f);
|
||||
ret->detectMaps();
|
||||
ret->setTypes();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Light * assimpLight(const aiLight * l) {
|
||||
if (!l) return 0;
|
||||
if (l->mType != aiLightSource_POINT && l->mType != aiLightSource_SPOT) return 0;
|
||||
Light * ret = new Light();
|
||||
ret->setName(fromAiString(l->mName));
|
||||
ret->setPos(fromAiVector3D(l->mPosition));
|
||||
ret->setDirection(fromAiVector3D(l->mDirection));
|
||||
ret->decay_const = l->mAttenuationConstant;
|
||||
ret->decay_linear = l->mAttenuationLinear;
|
||||
ret->decay_quadratic = l->mAttenuationQuadratic;
|
||||
ret->angle_start = l->mAngleInnerCone * rad2deg;
|
||||
ret->angle_end = l->mAngleOuterCone * rad2deg;
|
||||
if (l->mType == aiLightSource_SPOT) ret->light_type = Light::Cone;
|
||||
QVector3D col(l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b);
|
||||
ret->intensity = col.length();
|
||||
col /= ret->intensity;
|
||||
ret->setColor(QColor::fromRgbF(col[0], col[1], col[2]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
ObjectBaseList assimpObject(const aiNode * n,
|
||||
const QVector<Mesh *> & meshes,
|
||||
aiMesh ** ai_meshes,
|
||||
const QVector<Material *> & materials,
|
||||
const QMap<QString, Light *> & light_by_name,
|
||||
QVector<Light *> & out_lights) {
|
||||
if (!n) return ObjectBaseList();
|
||||
ObjectBaseList ret;
|
||||
ObjectBase * obj = 0;
|
||||
QString name = fromAiString(n->mName);
|
||||
Light * light = light_by_name.value(name, 0);
|
||||
if (light) {
|
||||
obj = light->clone();
|
||||
out_lights << (Light *)obj;
|
||||
} else
|
||||
obj = new ObjectBase();
|
||||
obj->setName(name);
|
||||
obj->setMatrix(fromAiMatrix4D(n->mTransformation));
|
||||
ret << obj;
|
||||
// qDebug() << "add object" << ret << ret->name();
|
||||
if (!light) {
|
||||
// qDebug() << name << "has" << n->mNumMeshes << "meshes";
|
||||
for (uint i = 0; i < n->mNumMeshes; ++i) {
|
||||
int mi = n->mMeshes[i];
|
||||
if (meshes[mi]) {
|
||||
if (obj->mesh()) {
|
||||
obj = obj->clone(false);
|
||||
ret << obj;
|
||||
}
|
||||
obj->setMesh(meshes[mi]);
|
||||
int mati = ai_meshes[mi]->mMaterialIndex;
|
||||
if (mati >= 0 || mati < materials.size()) obj->setMaterial(materials[mati]);
|
||||
// ret->setMesh(meshes[mi]);
|
||||
// qDebug() << "set mesh" << mi << ret->mesh();
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < n->mNumChildren; ++i) {
|
||||
ObjectBaseList cl = assimpObject(n->mChildren[i], meshes, ai_meshes, materials, light_by_name, out_lights);
|
||||
foreach(ObjectBase * c, cl)
|
||||
obj->addChild(c);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Scene * loadScene(const QString & filepath) {
|
||||
if (filepath.isEmpty()) return 0;
|
||||
qDebug() << "[Loader Assimp] Import" << filepath << "...";
|
||||
Assimp::Importer importer;
|
||||
const aiScene * ais =
|
||||
importer.ReadFile(filepath.toUtf8(),
|
||||
aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_GenUVCoords | aiProcess_TransformUVCoords);
|
||||
if (!ais) {
|
||||
qDebug() << "[Loader Assimp] Error: \"" + QString(importer.GetErrorString()) + "\"";
|
||||
return 0;
|
||||
}
|
||||
qDebug() << "[Loader Assimp] Imported" << ais->mNumMeshes << "meshes";
|
||||
QVector<Mesh *> meshes;
|
||||
for (uint i = 0; i < ais->mNumMeshes; ++i)
|
||||
meshes << assimpMesh(ais->mMeshes[i]);
|
||||
QVector<Material *> materials;
|
||||
for (uint i = 0; i < ais->mNumMaterials; ++i)
|
||||
materials << assimpMaterial(ais->mMaterials[i]);
|
||||
QVector<Light *> lights;
|
||||
for (uint i = 0; i < ais->mNumLights; ++i)
|
||||
lights << assimpLight(ais->mLights[i]);
|
||||
QMap<QString, Light *> light_by_name;
|
||||
foreach(Light * l, lights)
|
||||
if (l) light_by_name[l->name()] = l;
|
||||
|
||||
QVector<Light *> out_lights;
|
||||
ObjectBaseList rootl = assimpObject(ais->mRootNode, meshes, ais->mMeshes, materials, light_by_name, out_lights);
|
||||
if (rootl.isEmpty()) return 0;
|
||||
ObjectBase * root = rootl[0];
|
||||
root->transferTransformToChildren(true);
|
||||
|
||||
ObjectBaseList rcl = root->children(true);
|
||||
foreach(ObjectBase * c, rcl) {
|
||||
foreach(Light * l, out_lights) {
|
||||
if (c->name() == (l->name() + ".Target")) {
|
||||
l->setDistance((l->worldPos() - c->worldPos()).length());
|
||||
delete c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
root->cleanTree();
|
||||
|
||||
Scene * scene = new Scene();
|
||||
scene->setName(root->name());
|
||||
foreach(ObjectBase * o, root->children())
|
||||
scene->addObject(o);
|
||||
lights.removeAll(nullptr);
|
||||
qDeleteAll(lights);
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
|
||||
QStringList supportedFormats() {
|
||||
Assimp::Importer importer;
|
||||
aiString ret;
|
||||
importer.GetExtensionList(ret);
|
||||
return fromAiString(ret).toLower().split(";");
|
||||
}
|
||||
27
src/core/formats/loader_assimp.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
QGL Loader Assimp
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LOADER_ASSIMP_H
|
||||
#define LOADER_ASSIMP_H
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
QGLENGINE_CORE_EXPORT Scene * loadScene(const QString & filepath);
|
||||
QGLENGINE_CORE_EXPORT QStringList supportedFormats();
|
||||
|
||||
#endif // LOADER_ASSIMP_H
|
||||
68
src/core/formats/loader_qgl.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
QGL Loader QGL
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "loader_qgl.h"
|
||||
|
||||
#include "glscene.h"
|
||||
|
||||
#include <chunkstream.h>
|
||||
|
||||
|
||||
Scene * loadFromQGLFile(const QString & filepath) {
|
||||
QFile f(filepath);
|
||||
if (!f.exists()) {
|
||||
qDebug() << "[Loader QGL] Error: can`t open \"" + filepath + "\"";
|
||||
return 0;
|
||||
}
|
||||
f.open(QIODevice::ReadOnly);
|
||||
QDataStream s(&f);
|
||||
s.setVersion(QDataStream::Qt_5_0);
|
||||
char sign[4];
|
||||
s.readRawData(sign, 4);
|
||||
if ((sign[0] != 'Q') || (sign[1] != 'G') || (sign[2] != 'L') || (sign[3] != 'E')) {
|
||||
qDebug() << "[Loader QGL] Error: \"" + filepath + "\" is not valid QGLEngine file!";
|
||||
return 0;
|
||||
}
|
||||
ushort version = 0x1;
|
||||
f.peek((char *)&version, 2);
|
||||
if (version != 1) {
|
||||
qDebug() << "[Loader QGL] Error: \"" + filepath + "\" unsupported version!";
|
||||
return 0;
|
||||
}
|
||||
Scene * ret = 0;
|
||||
s.skipRawData(2);
|
||||
s >> ret;
|
||||
// root->buildTransform();
|
||||
qDebug() << "[Loader QGL] Loaded" << ret->objectsCount(true) << "objects from" << filepath;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool saveToQGLFile(const QString & filepath, const Scene * scene) {
|
||||
QFile f(filepath);
|
||||
if (!f.open(QIODevice::ReadWrite)) return false;
|
||||
f.resize(0);
|
||||
QDataStream s(&f);
|
||||
s.setVersion(QDataStream::Qt_5_0);
|
||||
char sign[4] = {'Q', 'G', 'L', 'E'};
|
||||
ushort version = 0x1;
|
||||
s.writeRawData(sign, 4);
|
||||
s.writeRawData((char *)&version, 2);
|
||||
s << scene;
|
||||
return true;
|
||||
}
|
||||
29
src/core/formats/loader_qgl.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
QGL Loader QGL
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LOADER_QGL_H
|
||||
#define LOADER_QGL_H
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
QGLENGINE_CORE_EXPORT Scene * loadFromQGLFile(const QString & filepath);
|
||||
QGLENGINE_CORE_EXPORT bool saveToQGLFile(const QString & filepath, const Scene * scene);
|
||||
|
||||
#endif // LOADER_QGL_H
|
||||
221
src/core/glwidget.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
QGL GLWidget
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glwidget.h"
|
||||
|
||||
#include "qglview.h"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
|
||||
GLWidget::GLWidget(QWidget * parent): QWidget(parent) {
|
||||
view_ = new QGLView();
|
||||
// view_->setFlags(windowFlags() | Qt::FramelessWindowHint);
|
||||
container = QWidget::createWindowContainer(view_, this);
|
||||
lay = new QVBoxLayout(this);
|
||||
lay->addWidget(container);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
lay->setSpacing(0);
|
||||
setMouseTracking(true);
|
||||
setWindowIcon(QIcon("://icons/qglview.png"));
|
||||
connect(view_, &QGLView::doubleClick, this, &GLWidget::viewDoubleClicked);
|
||||
}
|
||||
|
||||
|
||||
qreal GLWidget::lineWidth() const {
|
||||
return view_->lineWidth();
|
||||
}
|
||||
|
||||
|
||||
qreal GLWidget::FOV() const {
|
||||
return view_->FOV();
|
||||
}
|
||||
|
||||
|
||||
qreal GLWidget::depthStart() const {
|
||||
return view_->depthStart();
|
||||
}
|
||||
|
||||
|
||||
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::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::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);
|
||||
}
|
||||
}
|
||||
100
src/core/glwidget.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
QGL GLWidget
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLWIDGET_H
|
||||
#define GLWIDGET_H
|
||||
|
||||
#include "qglengine_core_export.h"
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
class QGLView;
|
||||
class ObjectBase;
|
||||
class Scene;
|
||||
|
||||
class QGLENGINE_CORE_EXPORT GLWidget: public QWidget {
|
||||
Q_OBJECT
|
||||
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(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 = 0.0);
|
||||
void setLineWidth(const qreal & arg);
|
||||
void setFOV(const qreal & arg);
|
||||
void setDepthStart(const qreal & 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;
|
||||
};
|
||||
|
||||
#endif // GLWIDGET_H
|
||||
308
src/core/mouse_controller.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
QGL MouseController
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "mouse_controller.h"
|
||||
|
||||
#include "glmesh.h"
|
||||
#include "qglview.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QKeyEvent>
|
||||
#include <qad_types.h>
|
||||
|
||||
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]->setAimSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e->button() == Qt::RightButton) {
|
||||
if (view->renderer_.edit_mode && !view->scene()->selectedObjects().isEmpty())
|
||||
view->popupMenu(
|
||||
#if QT_VERSION_MAJOR <= 5
|
||||
((QMouseEvent *)e)->globalPos()
|
||||
#else
|
||||
((QMouseEvent *)e)->globalPosition().toPoint()
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
canSelect_ = e->buttons() == 0;
|
||||
emit view->glMouseReleaseEvent(e);
|
||||
}
|
||||
|
||||
|
||||
void MouseController::mousePressEvent(QMouseEvent * e) {
|
||||
downPos = e->pos();
|
||||
if (cur_action != RendererService::haNoAction && e->buttons() == Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
if (selecting_) {
|
||||
selecting_ = false;
|
||||
view->renderer_.mouse_rect = QRect();
|
||||
return;
|
||||
}
|
||||
if (!QRect(QPoint(), view->size()).contains(e->pos())) return;
|
||||
lastPos = e->pos();
|
||||
downPos = e->pos();
|
||||
emit view->glMousePressEvent(e);
|
||||
}
|
||||
|
||||
|
||||
void MouseController::mouseMoveEvent(QMouseEvent * e) {
|
||||
QPoint cpos = e->pos();
|
||||
if (cur_action != RendererService::haNoAction && (e->buttons() == Qt::LeftButton)) {
|
||||
RendererService & rs(view->renderer_.rend_service);
|
||||
ObjectBaseList objects = view->scene()->selectedObjects(true);
|
||||
QVector<int> axis;
|
||||
switch (cur_action) {
|
||||
case RendererService::haMove:
|
||||
if (cur_handle.testFlag(RendererService::hmMoveX)) axis << 0;
|
||||
if (cur_handle.testFlag(RendererService::hmMoveY)) axis << 1;
|
||||
if (cur_handle.testFlag(RendererService::hmMoveZ)) axis << 2;
|
||||
break;
|
||||
case RendererService::haRotate:
|
||||
if (cur_handle.testFlag(RendererService::hmRotateX)) axis << 0;
|
||||
if (cur_handle.testFlag(RendererService::hmRotateY)) axis << 1;
|
||||
if (cur_handle.testFlag(RendererService::hmRotateZ)) axis << 2;
|
||||
break;
|
||||
case RendererService::haScale:
|
||||
if (cur_handle.testFlag(RendererService::hmScaleX)) axis << 0;
|
||||
if (cur_handle.testFlag(RendererService::hmScaleY)) axis << 1;
|
||||
if (cur_handle.testFlag(RendererService::hmScaleZ)) axis << 2;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
QVector<QVector3D> scales;
|
||||
foreach(int a, axis) {
|
||||
QVector3D axe_vector;
|
||||
axe_vector[a] = 1.;
|
||||
QMatrix4x4 axis_mat = view->camera()->fullViewMatrix() * rs.axis_mat;
|
||||
QVector3D center_screen = axis_mat.map(rs.selection_center);
|
||||
QVector3D axe_screen = (axis_mat.map(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->isAimSelected()) {
|
||||
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_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_) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
float dx = e->x() - lastPos.x();
|
||||
float dy = e->y() - lastPos.y();
|
||||
#else
|
||||
float dx = e->position().toPoint().x() - lastPos.x();
|
||||
float dy = e->position().toPoint().y() - lastPos.y();
|
||||
#endif
|
||||
if (e->buttons().testFlag(QT_MID_BUTTON)) {
|
||||
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 = QFlags<RendererService::HandleMesh>();
|
||||
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 = QFlags<RendererService::HandleMesh>();
|
||||
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();
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
int dx = e->x() - lastPos.x();
|
||||
int dy = e->y() - lastPos.y();
|
||||
#else
|
||||
int dx = e->position().toPoint().x() - lastPos.x();
|
||||
int dy = e->position().toPoint().y() - lastPos.y();
|
||||
#endif
|
||||
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_) {
|
||||
double ang =
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||
e->delta();
|
||||
#else
|
||||
e->angleDelta().y();
|
||||
#endif
|
||||
if (ang > 0) view->camera()->flyCloser(0.1f);
|
||||
if (ang < 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_MID_BUTTON)) emit view->doubleClick();
|
||||
}
|
||||
87
src/core/mouse_controller.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
QGL MouseController
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MOUSE_CONTROLLER_H
|
||||
#define MOUSE_CONTROLLER_H
|
||||
|
||||
#include "glcamera.h"
|
||||
#include "glprimitives.h"
|
||||
#include "renderer_service.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QTime>
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT MouseController: public QObject {
|
||||
friend class QGLView;
|
||||
friend class RendererSelection;
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MouseController(QGLView * view_);
|
||||
virtual ~MouseController();
|
||||
|
||||
bool isGrabMouseEnabled() const { return grabMouse_; }
|
||||
bool isMouseRotateEnabled() const { return mouseRotate_; }
|
||||
bool isMouseSelectionEnabled() const { return mouseSelect_; }
|
||||
bool isCameraOrbit() const { return cameraOrbit_; }
|
||||
|
||||
Qt::MouseButton selectionButton() const { return sel_button; }
|
||||
Qt::KeyboardModifier selectionModifier() const { return sel_mod; }
|
||||
|
||||
void setSelectionButton(Qt::MouseButton v) { sel_button = v; }
|
||||
void setSelectionModifier(Qt::KeyboardModifier v) { sel_mod = v; }
|
||||
|
||||
protected:
|
||||
void resize();
|
||||
void mousePressEvent(QMouseEvent * e);
|
||||
void mouseMoveEvent(QMouseEvent * e);
|
||||
void mouseReleaseEvent(QMouseEvent * e);
|
||||
void wheelEvent(QWheelEvent * e);
|
||||
void leaveEvent(QEvent *);
|
||||
void mouseDoubleClickEvent(QMouseEvent * e);
|
||||
|
||||
private:
|
||||
QGLView * view;
|
||||
QPoint lastPos, downPos;
|
||||
QSet<int> keys_;
|
||||
QVector<ObjectBase *> hov_objects, hov_aims;
|
||||
Qt::MouseButton sel_button;
|
||||
Qt::KeyboardModifier sel_mod;
|
||||
RendererService::HandleAction cur_action;
|
||||
QFlags<RendererService::HandleMesh> cur_handle;
|
||||
float app_scale;
|
||||
bool grabMouse_, mouse_first, mouseRotate_, mouseSelect_, customMouseMove_, canSelect_;
|
||||
bool cameraOrbit_, selecting_, mouse_sec;
|
||||
|
||||
private slots:
|
||||
|
||||
public slots:
|
||||
void setGrabMouseEnabled(const bool & arg) {
|
||||
grabMouse_ = arg;
|
||||
mouse_first = true;
|
||||
}
|
||||
void setMouseRotateEnabled(const bool & arg) { mouseRotate_ = arg; }
|
||||
void setMouseSelectionEnabled(const bool & arg) { mouseSelect_ = arg; }
|
||||
void setCustomMouseMove(const bool & arg) { customMouseMove_ = arg; }
|
||||
void setCameraOrbit(const bool & arg) { cameraOrbit_ = arg; }
|
||||
|
||||
signals:
|
||||
};
|
||||
|
||||
#endif // QGLVIEW_H
|
||||
103
src/core/openglwindow.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "openglwindow.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLPaintDevice>
|
||||
#include <QPainter>
|
||||
#include <qopenglext.h>
|
||||
|
||||
|
||||
OpenGLWindow::OpenGLWindow(QWindow * parent): QWindow(parent) {
|
||||
setFlags(flags() | Qt::FramelessWindowHint);
|
||||
setSurfaceType(QWindow::OpenGLSurface);
|
||||
QSurfaceFormat format = QSurfaceFormat::defaultFormat();
|
||||
#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);
|
||||
setFormat(format);
|
||||
}
|
||||
|
||||
|
||||
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::setVSync(bool on) {
|
||||
QSurfaceFormat f = requestedFormat();
|
||||
if (on) {
|
||||
if (f.swapInterval() != 1) {
|
||||
f.setSwapInterval(1);
|
||||
setFormat(f);
|
||||
format_change = true;
|
||||
}
|
||||
} else {
|
||||
if (f.swapInterval() != 0) {
|
||||
f.setSwapInterval(0);
|
||||
setFormat(f);
|
||||
format_change = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool OpenGLWindow::getVSync() const {
|
||||
return (requestedFormat().swapInterval() == 1);
|
||||
}
|
||||
|
||||
|
||||
// void OpenGLWindow::setSamples(int samples) {
|
||||
// QSurfaceFormat f = requestedFormat();
|
||||
// if (f.samples() != samples) {
|
||||
// f.setSamples(samples);
|
||||
// setFormat(f);
|
||||
// format_change = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// int OpenGLWindow::getSamples() const {
|
||||
// return requestedFormat().samples();
|
||||
// }
|
||||
|
||||
|
||||
void OpenGLWindow::renderNow() {
|
||||
if (!isExposed()) return;
|
||||
bool needsInitialize = false;
|
||||
if (!m_context || format_change) {
|
||||
if (m_context) delete m_context;
|
||||
m_context = new QOpenGLContext(this);
|
||||
m_context->setFormat(requestedFormat());
|
||||
m_context->create();
|
||||
needsInitialize = true;
|
||||
format_change = false;
|
||||
}
|
||||
m_context->makeCurrent(this);
|
||||
if (needsInitialize) {
|
||||
initializeOpenGLFunctions();
|
||||
initialize();
|
||||
}
|
||||
render();
|
||||
m_context->swapBuffers(this);
|
||||
frame_cnt++;
|
||||
}
|
||||
42
src/core/openglwindow.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "qglengine_core_export.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <QWindow>
|
||||
|
||||
class QPainter;
|
||||
class QOpenGLContext;
|
||||
class QOpenGLPaintDevice;
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT OpenGLWindow
|
||||
: public QWindow
|
||||
, public QOpenGLExtraFunctions {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OpenGLWindow(QWindow * parent = nullptr);
|
||||
virtual ~OpenGLWindow() {}
|
||||
|
||||
virtual void render() {}
|
||||
virtual void initialize() {}
|
||||
|
||||
QOpenGLContext * context() { return m_context; }
|
||||
void setVSync(bool on);
|
||||
bool getVSync() const;
|
||||
// void setSamples(int samples);
|
||||
// int getSamples() const;
|
||||
int getFrameCounter() const { return frame_cnt; }
|
||||
|
||||
public slots:
|
||||
void renderLater();
|
||||
void renderNow();
|
||||
|
||||
protected:
|
||||
bool event(QEvent * event) override;
|
||||
void exposeEvent(QExposeEvent * event) override;
|
||||
|
||||
private:
|
||||
QOpenGLContext * m_context = nullptr;
|
||||
bool format_change = false;
|
||||
int frame_cnt = 0;
|
||||
};
|
||||
46
src/core/qglengine_core.qrc
Normal file
@@ -0,0 +1,46 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>coeffs_brdf.png</file>
|
||||
<file>../icons/add-type-camera.png</file>
|
||||
<file>../icons/add-type-geo.png</file>
|
||||
<file>../icons/add-type-light.png</file>
|
||||
<file>../icons/add-type-empty.png</file>
|
||||
<file>../icons/collapse.png</file>
|
||||
<file>../icons/expand.png</file>
|
||||
<file>../icons/edit-rename.png</file>
|
||||
<file>../icons/alpha.png</file>
|
||||
<file>../icons/application-exit.png</file>
|
||||
<file>../icons/configure.png</file>
|
||||
<file>../icons/dialog-close.png</file>
|
||||
<file>../icons/document-edit.png</file>
|
||||
<file>../icons/document-import.png</file>
|
||||
<file>../icons/document-new.png</file>
|
||||
<file>../icons/document-open.png</file>
|
||||
<file>../icons/document-save.png</file>
|
||||
<file>../icons/document-save-all.png</file>
|
||||
<file>../icons/edit-clear.png</file>
|
||||
<file>../icons/edit-clear-locationbar-rtl.png</file>
|
||||
<file>../icons/edit-copy.png</file>
|
||||
<file>../icons/edit-delete.png</file>
|
||||
<file>../icons/edit-find.png</file>
|
||||
<file>../icons/edit-paste.png</file>
|
||||
<file>../icons/go-jump.png</file>
|
||||
<file>../icons/go-top.png</file>
|
||||
<file>../icons/layer-visible-on.png</file>
|
||||
<file>../icons/layer-visible-off.png</file>
|
||||
<file>../icons/light-+.png</file>
|
||||
<file>../icons/list-add.png</file>
|
||||
<file>../icons/object-flip-horizontal.png</file>
|
||||
<file>../icons/object-flip-vertical.png</file>
|
||||
<file>../icons/picker.png</file>
|
||||
<file>../icons/qglview.png</file>
|
||||
<file>../icons/transform-move.png</file>
|
||||
<file>../icons/transform-rotate.png</file>
|
||||
<file>../icons/transform-scale.png</file>
|
||||
<file>../icons/type-camera.png</file>
|
||||
<file>../icons/type-geo.png</file>
|
||||
<file>../icons/type-light.png</file>
|
||||
<file>../icons/type-empty.png</file>
|
||||
<file>../icons/view-refresh.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
284
src/core/qglview.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qglview.h"
|
||||
|
||||
#include "glmesh.h"
|
||||
#include "gltexture_manager.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QKeyEvent>
|
||||
#include <QOpenGLTexture>
|
||||
#include <chunkstream.h>
|
||||
#include <qad_types.h>
|
||||
|
||||
using namespace QGLEngineShaders;
|
||||
|
||||
|
||||
QGLView::QGLView(): OpenGLWindow(), renderer_(this), mouse(this) {
|
||||
setIcon(QIcon(":/icons/qglview.png"));
|
||||
is_init = false;
|
||||
timer = 0;
|
||||
hoverHaloColor_ = QColor(195, 140, 255);
|
||||
selectionHaloColor_ = QColor(175, 255, 140);
|
||||
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_ = shaders_bind = false;
|
||||
rmode = ObjectBase::Fill;
|
||||
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());
|
||||
|
||||
Mesh * m = Primitive::cube(10, 10, 10);
|
||||
m->flipNormals();
|
||||
ObjectBase * o = new ObjectBase(m);
|
||||
o->setColor(Qt::cyan);
|
||||
scene()->addObject(o);
|
||||
delete m;
|
||||
}
|
||||
|
||||
|
||||
QGLView::~QGLView() {
|
||||
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<Light *> QGLView::selectedLights() const {
|
||||
QList<Light *> ret;
|
||||
ObjectBaseList sol = scene_->selectedObjects();
|
||||
foreach(ObjectBase * o, sol)
|
||||
if (o->type() == ObjectBase::glLight) ret << (Light *)o;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QList<Camera *> QGLView::selectedCameras() const {
|
||||
QList<Camera *> ret;
|
||||
ObjectBaseList sol = scene_->selectedObjects();
|
||||
for (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();
|
||||
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();
|
||||
fps_cnt++;
|
||||
if (fps_tm < 1000.) return;
|
||||
time.restart();
|
||||
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());
|
||||
scene_->reinitAll();
|
||||
is_init = true;
|
||||
prev_size = QSize();
|
||||
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 = default_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;
|
||||
setDefaultCamera();
|
||||
Camera * c = default_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<int, QVariant> f;
|
||||
QDataStream ds(ba);
|
||||
ds >> f;
|
||||
features_ = f;
|
||||
}
|
||||
290
src/core/qglview.h
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef QGLVIEW_H
|
||||
#define QGLVIEW_H
|
||||
|
||||
#include "glcamera.h"
|
||||
#include "glframebuffer.h"
|
||||
#include "glprimitives.h"
|
||||
#include "glscene.h"
|
||||
#include "mouse_controller.h"
|
||||
#include "openglwindow.h"
|
||||
#include "qglengine_core_export.h"
|
||||
#include "renderer.h"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QMenu>
|
||||
|
||||
class QGLENGINE_CORE_EXPORT QGLView: public OpenGLWindow {
|
||||
friend class RendererSelection;
|
||||
Q_OBJECT
|
||||
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 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 = 0.);
|
||||
|
||||
float lineWidth() const { return lineWidth_; }
|
||||
float FOV() const { return camera()->FOV(); }
|
||||
float depthStart() const { return camera()->depthStart(); }
|
||||
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 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<Light *> selectedLights() const;
|
||||
QList<Camera *> 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; }
|
||||
bool isDefaultCamera() const { return 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<QAction *> al) {
|
||||
context_menu.clear();
|
||||
context_menu.addActions(al);
|
||||
}
|
||||
void popupMenu(const QPoint & pos, QAction * at = nullptr) { context_menu.popup(pos, at); }
|
||||
void setGrabImage(bool on) { renderer_.setGrabImage(on); }
|
||||
bool isGrabImage() const { return renderer_.isGrabImage(); }
|
||||
QImage getImage() const { return renderer_.getImage(); }
|
||||
|
||||
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<int> keys_;
|
||||
QColor fogColor_, hoverHaloColor_, selectionHaloColor_;
|
||||
QElapsedTimer time;
|
||||
GLint max_anisotropic, max_texture_chanels;
|
||||
ObjectBase::RenderMode rmode;
|
||||
QHash<int, QVariant> features_;
|
||||
QSize prev_size;
|
||||
float lineWidth_;
|
||||
float fps_, fps_tm, fogDensity_, fogDecay_;
|
||||
float hoverHaloFill_, selectionHaloFill_, m_motionBlurFactor;
|
||||
int timer, fps_cnt, sh_id_loc;
|
||||
bool fogEnabled_, lightEnabled_;
|
||||
bool shaders_supported, shaders_bind;
|
||||
bool hoverHalo_, selectionHalo_;
|
||||
bool is_init;
|
||||
|
||||
private slots:
|
||||
void __destroyed();
|
||||
void __objectDeleted(ObjectBase * o);
|
||||
|
||||
public slots:
|
||||
void setLineWidth(const float & arg) { lineWidth_ = arg; }
|
||||
void setFOV(const float & arg) { camera()->setFOV(arg); }
|
||||
void setDepthStart(const float & arg) { camera()->setDepthStart(arg); }
|
||||
void setGamma(const float & arg) { renderer_.gamma_ = arg; }
|
||||
void setAutoExposure(bool arg) { renderer_.tone_proc.enabled = 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
|
||||
209
src/core/render/gltexture_manager.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
QGL TextureManager
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gltexture_manager.h"
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
QStringList TextureManager::search_pathes(".");
|
||||
|
||||
|
||||
QVector3D colorVector(QRgb c) {
|
||||
return QVector3D(((uchar *)(&c))[0] / 255.f, ((uchar *)(&c))[1] / 255.f, ((uchar *)(&c))[2] / 255.f);
|
||||
}
|
||||
|
||||
|
||||
QString TextureManager::findFile(const QString & path) {
|
||||
return ::findFile(path, search_pathes);
|
||||
}
|
||||
|
||||
|
||||
GLuint TextureManager::loadTexture(const QString & path, bool ownership, bool bump) {
|
||||
QString p = findFile(path);
|
||||
if (p.isEmpty()) return 0;
|
||||
int tid = textureID(p, bump);
|
||||
if (tid > 0) {
|
||||
// qDebug() << "[TextureManager] Found" << path << "as" << tid;
|
||||
return tid;
|
||||
}
|
||||
QImage image(p);
|
||||
if (bump) convertToNormal(image);
|
||||
// qDebug() << p << image.width() << image.height() << image.format() << bump;
|
||||
GLuint tid_ = tid;
|
||||
createGLTexture(f, tid_, image);
|
||||
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);
|
||||
if (tid == 0) {
|
||||
qDebug() << "[TextureManager] Can`t load image";
|
||||
return tid;
|
||||
}
|
||||
// qDebug() << "[TextureManager] Loaded image as" << tid;
|
||||
return tid;
|
||||
}
|
||||
|
||||
|
||||
QImage TextureManager::loadTextureImage(const QString & path, bool bump) {
|
||||
QString p = findFile(path);
|
||||
if (p.isEmpty()) return QImage();
|
||||
QImage ret = tex_im[bump ? 1 : 0].value(p);
|
||||
if (!ret.isNull()) return ret;
|
||||
ret = QImage(p);
|
||||
if (bump) convertToNormal(ret);
|
||||
tex_im[bump ? 1 : 0].insert(p, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::reloadTexture(GLuint tid, const QString & path) {
|
||||
QString p = findFile(path);
|
||||
if (p.isEmpty() || (tid == 0)) return;
|
||||
QImage image(p);
|
||||
createGLTexture(f, tid, image);
|
||||
if (tid == 0) {
|
||||
qDebug() << "[TextureManager] Can`t load" << p;
|
||||
return;
|
||||
}
|
||||
qDebug() << "[TextureManager] Reloaded" << p << "as" << tid;
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::reloadTexture(GLuint tid, const QImage & im) {
|
||||
if (im.isNull() || (tid == 0)) return;
|
||||
QImage image(im);
|
||||
createGLTexture(f, tid, image);
|
||||
qDebug() << "[TextureManager] Reloaded" << tid;
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::convertToNormal(QImage & im) {
|
||||
if (im.isNull()) return;
|
||||
QImage sim = im.convertToFormat(QImage::Format_ARGB32);
|
||||
float sum[3] = {0., 0., 0.};
|
||||
llong a = 0;
|
||||
const uchar * sd = sim.constBits();
|
||||
for (int i = 0; i < sim.height(); i++) {
|
||||
for (int j = 0; j < sim.width(); j++) {
|
||||
sum[2] += sd[a] / 255.f - 0.5f;
|
||||
++a;
|
||||
sum[1] += sd[a] / 255.f - 0.5f;
|
||||
++a;
|
||||
sum[0] += sd[a] / 255.f - 0.5f;
|
||||
++a;
|
||||
++a;
|
||||
}
|
||||
}
|
||||
float wh = sim.width() * sim.height();
|
||||
sum[0] /= wh;
|
||||
sum[1] /= wh;
|
||||
sum[2] /= wh;
|
||||
// qDebug() << sum[0] << sum[1] << sum[2];
|
||||
if ((qAbs(sum[0]) <= 0.05f) && (qAbs(sum[1]) <= 0.05f) && (sum[2] >= 0.25f)) /// already normal
|
||||
return;
|
||||
// qDebug() << "convert to normal";
|
||||
QImage dim = QImage(sim.width(), sim.height(), QImage::Format_ARGB32);
|
||||
int tx, ty, w = sim.width(), h = sim.height();
|
||||
a = 0;
|
||||
uchar * dd = dim.bits();
|
||||
for (int i = 0; i < sim.height(); i++) {
|
||||
for (int j = 0; j < sim.width(); j++) {
|
||||
tx = j - 1;
|
||||
tx = tx < 0 ? w + tx : tx % w;
|
||||
ty = i - 1;
|
||||
ty = ty < 0 ? h + ty : ty % h;
|
||||
QVector3D p[3], res;
|
||||
p[0] = colorVector(sim.pixel(j, i));
|
||||
p[1] = colorVector(sim.pixel(j, ty));
|
||||
p[2] = colorVector(sim.pixel(tx, i));
|
||||
res.setY(piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f));
|
||||
res.setX(piClamp(0.5f - (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f));
|
||||
tx = (j + 1) % w;
|
||||
ty = (i + 1) % h;
|
||||
p[1] = colorVector(sim.pixel(j, ty));
|
||||
p[2] = colorVector(sim.pixel(tx, i));
|
||||
res.setY(piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f));
|
||||
res.setX(piClamp(0.5f - (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f));
|
||||
res.setZ(1.f);
|
||||
dd[a] = res.z() * 255;
|
||||
++a;
|
||||
dd[a] = res.y() * 255;
|
||||
++a;
|
||||
dd[a] = res.x() * 255;
|
||||
++a;
|
||||
dd[a] = 255;
|
||||
++a;
|
||||
}
|
||||
}
|
||||
im = dim;
|
||||
// im.save("_normal.png");
|
||||
}
|
||||
|
||||
|
||||
bool TextureManager::loadTextures() {
|
||||
QFileInfoList fil;
|
||||
foreach(const QString & i, tex_pathes)
|
||||
loadTexture(i, true);
|
||||
tex_pathes.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::deleteTextures() {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
QList<GLuint> texs = tex_ids[i].values();
|
||||
qDebug() << "[TextureManager] Delete" << texs.size() << "textures";
|
||||
if (!texs.isEmpty()) f->glDeleteTextures(texs.size(), &texs[0]);
|
||||
tex_ids[i].clear();
|
||||
tex_im[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::deleteTexture(const QString & name) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (tex_ids[i].contains(name)) {
|
||||
GLuint id = tex_ids[i][name];
|
||||
f->glDeleteTextures(1, &id);
|
||||
tex_ids[i].remove(name);
|
||||
tex_im[i].remove(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TextureManager::clearImageCache() {
|
||||
tex_im[0].clear();
|
||||
tex_im[1].clear();
|
||||
}
|
||||
67
src/core/render/gltexture_manager.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
QGL TextureManager
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLTEXTUREMANAGER_H
|
||||
#define GLTEXTUREMANAGER_H
|
||||
|
||||
#include "qglengine_core_export.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QImage>
|
||||
#include <QMap>
|
||||
#include <QOpenGLExtraFunctions>
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT TextureManager {
|
||||
public:
|
||||
TextureManager(QOpenGLExtraFunctions * f_): f(f_) {}
|
||||
virtual ~TextureManager() {}
|
||||
|
||||
static void addSearchPath(const QString & path) {
|
||||
if (!search_pathes.contains(path)) search_pathes << path;
|
||||
}
|
||||
static void clearSearchPathes() { search_pathes.clear(); }
|
||||
static QStringList searchPathes() { return search_pathes; }
|
||||
static QString findFile(const QString & path);
|
||||
GLuint loadTexture(const QString & path, bool ownership = true, bool bump = false);
|
||||
GLuint loadTexture(const QImage & image, bool ownership = true, bool bump = false);
|
||||
QImage loadTextureImage(const QString & path, bool bump = false);
|
||||
void reloadTexture(GLuint tid, const QString & path);
|
||||
void reloadTexture(GLuint tid, const QImage & image);
|
||||
int textureID(const QString & path, bool bump = false) { return tex_ids[bump ? 1 : 0][path]; }
|
||||
QImage textureImage(const QString & path, bool bump = false) { return tex_im[bump ? 1 : 0][path]; }
|
||||
void addTexture(const QString & path) { tex_pathes << path; }
|
||||
bool loadTextures();
|
||||
void deleteTextures();
|
||||
void deleteTexture(const QString & name);
|
||||
void clearImageCache();
|
||||
|
||||
protected:
|
||||
static void convertToNormal(QImage & im);
|
||||
|
||||
static QStringList search_pathes;
|
||||
|
||||
QMap<QString, GLuint> tex_ids[2];
|
||||
QMap<QString, QImage> tex_im[2];
|
||||
QStringList tex_pathes;
|
||||
QOpenGLExtraFunctions * f;
|
||||
};
|
||||
|
||||
|
||||
#endif // GLTEXTUREMANAGER_H
|
||||
450
src/core/render/renderer.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "renderer.h"
|
||||
|
||||
#include "glmesh.h"
|
||||
#include "glshaders.h"
|
||||
#include "gltexture_manager.h"
|
||||
#include "qglview.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <qad_types.h>
|
||||
|
||||
|
||||
using namespace QGLEngineShaders;
|
||||
|
||||
|
||||
Renderer::Renderer(QGLView * view_)
|
||||
: RendererBase(view_)
|
||||
, fbo_ds(view_, QVector<GLenum>() << GL_RGBA16F << GL_RGBA32F << GL_RGBA16F << GL_RGBA16F << GL_RGBA16F << GL_RGBA32F << 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";
|
||||
|
||||
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) {
|
||||
fbo_ds.reinit();
|
||||
fbo_out.reinit();
|
||||
quad->reinit();
|
||||
buffer_materials.reinit();
|
||||
buffer_lights.reinit();
|
||||
buffer_lights_pos.reinit();
|
||||
textures_maps.reinit();
|
||||
textures_empty.reinit();
|
||||
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();
|
||||
markReloadTextures();
|
||||
tex_env.init();
|
||||
if (is_grabbing) fbo_out.enablePixelBuffer();
|
||||
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<ShaderRole, QString> it(shader_files);
|
||||
qDeleteAll(shaders.values());
|
||||
shaders.clear();
|
||||
if (shader_fxaa) delete shader_fxaa;
|
||||
shader_fxaa = nullptr;
|
||||
if (tone_proc.shader_sum) delete tone_proc.shader_sum;
|
||||
tone_proc.shader_sum = nullptr;
|
||||
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);
|
||||
for (auto * e: fb_effects) {
|
||||
e->reloadShaders();
|
||||
e->is_loaded = true;
|
||||
}
|
||||
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_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);
|
||||
}
|
||||
|
||||
|
||||
QVector<int> Renderer::getFreePlanes(int count) {
|
||||
prev_write_plane = cur_write_plane;
|
||||
QVector<int> ret;
|
||||
bool output_done = false;
|
||||
const int total_count = 4;
|
||||
for (int i = 0; i < total_count; ++i) {
|
||||
int plane = obrGeneral0 + i;
|
||||
if (prev_write_plane == plane) continue;
|
||||
if (!output_done) {
|
||||
fbo_out.setWriteBuffer(plane);
|
||||
cur_write_plane = plane;
|
||||
output_done = true;
|
||||
continue;
|
||||
}
|
||||
ret << plane;
|
||||
if (ret.size() == count) break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
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<Mesh *, ObjectBaseList> 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);
|
||||
typedef QPair<Renderer::ShaderRole, Light::Type> PassPair;
|
||||
QVector<PassPair> 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", (int)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) {
|
||||
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.blit(dbrNormalZ, fbo_ds.id(), dbrNormalZSolid, fbo_ds.rect(), fbo_ds.rect());
|
||||
fbo_ds.blit(dbrSpecularReflect, fbo_ds.id(), dbrSpecularReflectSolid, fbo_ds.rect(), fbo_ds.rect());
|
||||
fbo_ds.release();
|
||||
|
||||
/// lighting passes
|
||||
renderLight(obrSolidOmni, scene.geometries_used[rpSolid].isEmpty());
|
||||
|
||||
/// transparent geometry pass
|
||||
fbo_ds.bind();
|
||||
glEnableDepth();
|
||||
fbo_ds.setWriteBuffers({0, 1, 2, 3, 4});
|
||||
glClearFramebuffer(Qt::black, false);
|
||||
fbo_ds.setWriteBuffers();
|
||||
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_out.bindColorTexture(obrSolidOmni, 1);
|
||||
fbo_out.bindColorTexture(obrSolidSpot, 2);
|
||||
fbo_out.bindColorTexture(obrTransparentOmni, 3);
|
||||
fbo_out.bindColorTexture(obrTransparentSpot, 4);
|
||||
fbo_out.setWriteBuffer(obrGeneral0);
|
||||
renderQuad(prog, quad);
|
||||
}
|
||||
|
||||
cur_write_plane = obrGeneral0;
|
||||
|
||||
/// tonemapping
|
||||
tone_proc.process();
|
||||
auto free = getFreePlanes(0);
|
||||
if (bindShader(srTonemapPass, &prog)) {
|
||||
fbo_out.bind();
|
||||
prog->setUniformValue("gamma", gamma_);
|
||||
prog->setUniformValue("frame_max", tone_proc.frameMax());
|
||||
fbo_out.bindColorTexture(prev_write_plane, 0);
|
||||
renderQuad(prog, quad);
|
||||
} else {
|
||||
fbo_out.blit(prev_write_plane, fbo_out.id(), cur_write_plane, fbo_out.rect(), fbo_out.rect());
|
||||
}
|
||||
|
||||
/// FXAA
|
||||
if (view->isFeatureEnabled(QGLView::qglFXAA)) {
|
||||
prog = shader_fxaa;
|
||||
if (bindShader(prog)) {
|
||||
auto free = getFreePlanes(0);
|
||||
setUniformCamera(prog, 0, true, fbo_out.size());
|
||||
fbo_out.bindColorTexture(prev_write_plane);
|
||||
renderQuad(prog, quad, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// custom effects
|
||||
for (auto * e: fb_effects) {
|
||||
e->reloadShadersInternal();
|
||||
e->drawInternal();
|
||||
}
|
||||
|
||||
fbo_out.release();
|
||||
|
||||
/// apply hovers and selection frame
|
||||
if (edit_mode) {
|
||||
rend_selection.drawSelection(fbo_out, cur_write_plane);
|
||||
rend_service.renderService();
|
||||
} else {
|
||||
fbo_out.blit(cur_write_plane, 0, 0, fbo_out.rect(), QRect(QPoint(), view->size()));
|
||||
}
|
||||
if (is_grabbing) {
|
||||
fbo_out.queryImage(0);
|
||||
last_img = fbo_out.getImage().mirrored();
|
||||
// qDebug() << last_img.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Renderer::setCameraLightMode(int m) {
|
||||
camera_light_mode = m;
|
||||
view->scene()->setLightsChanged();
|
||||
}
|
||||
|
||||
void Renderer::setGrabImage(bool on) {
|
||||
is_grabbing = on;
|
||||
// fbo_out.enablePixelBuffer();
|
||||
}
|
||||
|
||||
|
||||
void Renderer::addFramebufferEffect(FramebufferEffectBase * e) {
|
||||
e->r = this;
|
||||
fb_effects << e;
|
||||
}
|
||||
150
src/core/render/renderer.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RENDERER_H
|
||||
#define RENDERER_H
|
||||
|
||||
#include "glcubemap.h"
|
||||
#include "glframebuffereffectbase.h"
|
||||
#include "renderer_base.h"
|
||||
#include "renderer_material.h"
|
||||
#include "renderer_selection.h"
|
||||
#include "renderer_service.h"
|
||||
#include "tonemapping_proc.h"
|
||||
|
||||
#include <QQueue>
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT Renderer: public RendererBase {
|
||||
friend class QGLView;
|
||||
friend class MouseController;
|
||||
friend class RendererMaterial;
|
||||
friend class RendererService;
|
||||
friend class RendererSelection;
|
||||
friend class FramebufferEffectBase;
|
||||
friend class TonemappingProc;
|
||||
enum ShaderRole {
|
||||
// Selection
|
||||
srSelectionFill,
|
||||
srSelectionHalo,
|
||||
srSelectionApply,
|
||||
srSelectionFrame,
|
||||
|
||||
// Service
|
||||
srServiceFill,
|
||||
srServiceFrame,
|
||||
srServiceLine,
|
||||
|
||||
// Deferred shading
|
||||
srGeometryPass,
|
||||
srLightOmniPass,
|
||||
srLightSpotPass,
|
||||
srFinalPass,
|
||||
srTonemapPass,
|
||||
};
|
||||
enum OutBufferRole {
|
||||
obrSolidOmni,
|
||||
obrSolidSpot,
|
||||
obrTransparentOmni,
|
||||
obrTransparentSpot,
|
||||
|
||||
obrGeneral0,
|
||||
obrGeneral1,
|
||||
obrGeneral2,
|
||||
obrGeneral3,
|
||||
|
||||
obrBuffersCount,
|
||||
};
|
||||
|
||||
public:
|
||||
Renderer(QGLView * view_);
|
||||
virtual ~Renderer();
|
||||
|
||||
enum DeferredBufferRole {
|
||||
dbrDiffuse,
|
||||
dbrNormalZ,
|
||||
dbrSpecularReflect,
|
||||
dbrEmissionRough,
|
||||
dbrSpeedBitangXY,
|
||||
|
||||
dbrNormalZSolid,
|
||||
dbrSpecularReflectSolid,
|
||||
|
||||
dbrBuffersCount,
|
||||
};
|
||||
|
||||
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; }
|
||||
void setGrabImage(bool on);
|
||||
bool isGrabImage() const { return is_grabbing; }
|
||||
QImage getImage() const { return last_img; }
|
||||
void addFramebufferEffect(FramebufferEffectBase * e);
|
||||
void clearFramebufferEffects() { fb_effects.clear(); }
|
||||
|
||||
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();
|
||||
|
||||
QVector<int> getFreePlanes(int count);
|
||||
|
||||
private:
|
||||
float gamma_ = 1.f;
|
||||
int camera_light_mode, cur_write_plane = 0, prev_write_plane = 0;
|
||||
bool edit_mode, need_init_shaders, need_render_sum;
|
||||
Framebuffer fbo_ds, fbo_out;
|
||||
QMap<ShaderRole, QString> shader_files;
|
||||
QMap<ShaderRole, QStringList> shader_defines;
|
||||
QMap<ShaderRole, QOpenGLShaderProgram *> shaders;
|
||||
QOpenGLShaderProgram * shader_fxaa = nullptr;
|
||||
|
||||
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<QVector3D> hcontent;
|
||||
QMap<int, QList<Light *>> cur_lights;
|
||||
QVector<FramebufferEffectBase *> fb_effects;
|
||||
QImage last_img;
|
||||
bool is_grabbing = false;
|
||||
};
|
||||
|
||||
#endif // RENDERER_H
|
||||
422
src/core/render/renderer_base.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "renderer_base.h"
|
||||
|
||||
#include "glmesh.h"
|
||||
#include "glshaders_headers.h"
|
||||
#include "gltexture_manager.h"
|
||||
#include "qglview.h"
|
||||
#include "renderer.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
|
||||
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_ViewSize", QVector2D(w, 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] = QVector4D(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<uchar> & 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<uchar> & 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<Map *> maps[2];
|
||||
QMap<QString, int> 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<QString, int> 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->_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";
|
||||
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<int, QList<Light *>> & lights) {
|
||||
lights_start.clear();
|
||||
lights_start[Light::Omni] = 0;
|
||||
QMapIterator<int, QList<Light *>> 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;
|
||||
}
|
||||
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);
|
||||
pos = m * pos;
|
||||
dir = (m * QVector4D(QVector3D(0, 0, -1), 0)).normalized();
|
||||
so.position = pos;
|
||||
so.direction = dir;
|
||||
}
|
||||
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<QVector2D> data(size * size);
|
||||
int ind = -1;
|
||||
for (int x = 0; x < size; ++x) {
|
||||
for (int y = 0; y < size; ++y) {
|
||||
QColor p = im.pixelColor(x, y);
|
||||
data[++ind] = QVector2D(p.redF(), p.greenF());
|
||||
}
|
||||
}
|
||||
createCoeffTexture(tex_coeff[0], data.constData(), size, 2);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
67
src/core/render/renderer_base.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RENDERER_BASE_H
|
||||
#define RENDERER_BASE_H
|
||||
|
||||
#include "glbuffer.h"
|
||||
#include "glshaders_types.h"
|
||||
#include "gltexturearray.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT 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<uchar> & buffer, const ObjectBaseList & ol);
|
||||
void fillSelectionsBuffer(QVector<uchar> & buffer, bool yes, int size);
|
||||
void reloadMaterials(Scene & scene);
|
||||
void reloadLightsParameters(const QMap<int, QList<Light *>> & 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<QGLEngineShaders::Object> cur_objects_;
|
||||
QVector<QGLEngineShaders::QGLMaterial> cur_materials_;
|
||||
QVector<QGLEngineShaders::QGLLightParameter> cur_lights_params_;
|
||||
QVector<QGLEngineShaders::QGLLightPosition> 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<int, int> lights_start;
|
||||
QList<Light *> current_lights;
|
||||
};
|
||||
|
||||
#endif // RENDERER_BASE_H
|
||||
133
src/core/render/renderer_material.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "renderer_material.h"
|
||||
|
||||
#include "glmesh.h"
|
||||
#include "gltexture_manager.h"
|
||||
#include "qglview.h"
|
||||
#include "renderer.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <qad_types.h>
|
||||
|
||||
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) {
|
||||
fbo_mat_thumb.reinit();
|
||||
mat_sphere->reinit();
|
||||
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<int, QList<Light *>> mat_l;
|
||||
mat_l[Light::Omni] << mat_light;
|
||||
r->reloadLightsParameters(mat_l);
|
||||
r->reloadLightsPositions(mat_camera);
|
||||
glClearFramebuffer(Qt::black, 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);
|
||||
}
|
||||
}
|
||||
53
src/core/render/renderer_material.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RENDERER_MATERIAL_H
|
||||
#define RENDERER_MATERIAL_H
|
||||
|
||||
#include "glframebuffer.h"
|
||||
|
||||
#include <QQueue>
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT 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<Material *, QImage> mat_thumbnails;
|
||||
Material * last_thumb_material;
|
||||
QQueue<Material *> mat_thumb_queue;
|
||||
};
|
||||
|
||||
#endif // RENDERER_MATERIAL_H
|
||||
215
src/core/render/renderer_selection.cpp
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "renderer_selection.h"
|
||||
|
||||
#include "glmesh.h"
|
||||
#include "qglview.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <qad_types.h>
|
||||
|
||||
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) {
|
||||
fbo_selection.reinit();
|
||||
sel_frame->reinit();
|
||||
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_);
|
||||
}
|
||||
|
||||
|
||||
void RendererSelection::generateObjectsID(Scene & scene) {
|
||||
ids.clear();
|
||||
aim_ids.clear();
|
||||
QList<int> passes = scene.geometries_used.keys();
|
||||
foreach(int p, passes) {
|
||||
QMapIterator<Mesh *, ObjectBaseList> 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<int, QList<Light *>> 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<uint> points = fbo_selection.getPointsByte();
|
||||
QSet<uint> 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();
|
||||
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<int>() << sbrHovered << sbrSelected);
|
||||
if (!view->isHoverHaloEnabled() && !view->isSelectionHaloEnabled())
|
||||
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->isHoverHaloEnabled() && (id_hover > 0) ? 1.f : 0.f);
|
||||
prog->setUniformValue("has_selection", view->isSelectionHaloEnabled() ? 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
66
src/core/render/renderer_selection.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RENDERER_SELECTION_H
|
||||
#define RENDERER_SELECTION_H
|
||||
|
||||
#include "renderer_service.h"
|
||||
|
||||
#include <QHash>
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT 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<uchar> cur_selections_;
|
||||
QHash<uint, ObjectBase *> ids;
|
||||
QHash<uint, ObjectBase *> aim_ids;
|
||||
uint id_hover;
|
||||
};
|
||||
|
||||
#endif // RENDERER_selection_H
|
||||
528
src/core/render/renderer_service.cpp
Normal file
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "renderer_service.h"
|
||||
|
||||
#include "glmesh.h"
|
||||
#include "qglview.h"
|
||||
#include "renderer.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <qad_types.h>
|
||||
|
||||
using namespace QGLEngineShaders;
|
||||
|
||||
|
||||
RendererService::RendererService(Renderer * r_): r(r_) {
|
||||
line_width = 1;
|
||||
current_action = haNoAction;
|
||||
current_handle = QFlags<HandleMesh>();
|
||||
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) {
|
||||
box_mesh->reinit();
|
||||
box_mesh_f->reinit();
|
||||
omni_mesh->reinit();
|
||||
omni_mesh_f->reinit();
|
||||
cone_mesh->reinit();
|
||||
cone_mesh_f->reinit();
|
||||
camera_mesh->reinit();
|
||||
camera_mesh_f->reinit();
|
||||
line_spot_f->reinit();
|
||||
line_camera_f->reinit();
|
||||
axis_mesh->reinit();
|
||||
handle_move_mesh->reinit();
|
||||
handle_ms_2_mesh->reinit();
|
||||
handle_rotate_mesh->reinit();
|
||||
handle_scale_mesh->reinit();
|
||||
handle_scale_3_mesh->reinit();
|
||||
axis_mesh->reinit();
|
||||
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<Light *> 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<QVector3D> & lv(line_mesh->vertices());
|
||||
QVector<QVector3D> & ln(line_mesh->normals());
|
||||
QVector<QVector2D> & 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<QMatrix4x4> & mats,
|
||||
const QVector<QVector4D> & 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]->isAimSelected()) 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<uchar> 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<QMatrix4x4> mv;
|
||||
mv.resize(1);
|
||||
QVector<QVector4D> 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<Object> & 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)) {
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
112
src/core/render/renderer_service.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef RENDERER_SERVICE_H
|
||||
#define RENDERER_SERVICE_H
|
||||
|
||||
#include "glframebuffer.h"
|
||||
#include "glshaders_types.h"
|
||||
|
||||
#include <QQueue>
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT 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<QMatrix4x4> & mats,
|
||||
const QVector<QVector4D> & 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<QGLEngineShaders::Object> & 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<QMatrix4x4> mat_xyz, mat_ms2;
|
||||
QVector<QVector4D> color_xyz, color_ms2;
|
||||
QVector<QGLEngineShaders::Object> cur_objects, cur_aims;
|
||||
QGLEngineShaders::Object line_object;
|
||||
Camera * axis_camera;
|
||||
QSize axis_viewport;
|
||||
HandleAction current_action;
|
||||
QFlags<HandleMesh> current_handle;
|
||||
int line_width;
|
||||
double size_vp_scale, size_full_scale;
|
||||
};
|
||||
|
||||
#endif // RENDERER_SERVICE_H
|
||||
187
src/core/render/tonemapping_proc.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "tonemapping_proc.h"
|
||||
|
||||
#include "qglview.h"
|
||||
|
||||
#include <QOpenGLExtraFunctions>
|
||||
#include <qad_types.h>
|
||||
|
||||
using namespace QGLEngineShaders;
|
||||
|
||||
|
||||
TonemappingProc::TonemappingProc(Renderer * rend)
|
||||
: QThread()
|
||||
, r(rend)
|
||||
, fbo_1x1(r->view, 1, false, GL_RGB32F)
|
||||
, fbomm(r->fbo_out, Renderer::obrGeneral1, 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() {
|
||||
buffer_vbo.reinit();
|
||||
fbo_1x1.reinit();
|
||||
fbomm.reinit();
|
||||
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<Vector2i> _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();
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
void TonemappingProc::run() {
|
||||
while (!exit_) {
|
||||
if (!enabled) {
|
||||
msleep(100);
|
||||
continue;
|
||||
}
|
||||
mutex.lock();
|
||||
if (last_data.isEmpty()) {
|
||||
mutex.unlock();
|
||||
msleep(10);
|
||||
continue;
|
||||
}
|
||||
QVector<QVector4D> 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<QVector4D> & 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<QVector4D> data = fbo.getPointsFloat();
|
||||
mutex.lock();
|
||||
last_data = data;
|
||||
mutex.unlock();
|
||||
}
|
||||
fbomm.create();
|
||||
fbo.queryPoints(0, fbo.rect(), GL_FLOAT);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
float TonemappingProc::frameMax() {
|
||||
if (!enabled) return 1.f;
|
||||
return cur_max;
|
||||
}
|
||||
64
src/core/render/tonemapping_proc.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TONEMAPPING_PROC_H
|
||||
#define TONEMAPPING_PROC_H
|
||||
|
||||
#include "glframebuffer_mipmap.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT 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) override;
|
||||
void run() override;
|
||||
float calcHistogram(const QVector<QVector4D> & data);
|
||||
|
||||
private:
|
||||
Renderer * r;
|
||||
QMutex mutex;
|
||||
QVector<QVector4D> last_data;
|
||||
QVector<float> 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
|
||||
91
src/core/scene/glcamera.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
QGL Camera
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gltypes.h"
|
||||
#include "qglview.h"
|
||||
|
||||
|
||||
Camera::Camera() {
|
||||
type_ = glCamera;
|
||||
fov_ = 60.;
|
||||
roll_ = 0.;
|
||||
depth_start = 0.1f;
|
||||
mirror_x = mirror_y = false;
|
||||
}
|
||||
|
||||
|
||||
QMatrix4x4 Camera::offsetMatrix() const {
|
||||
QMatrix4x4 ret;
|
||||
ret.translate(parent_ ? -offset_ : -aim());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
o->is_init = false;
|
||||
o->name_ = name_;
|
||||
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();
|
||||
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);
|
||||
}
|
||||
65
src/core/scene/glcamera.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
QGL Camera
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLCAMERA_H
|
||||
#define GLCAMERA_H
|
||||
|
||||
#include "globject.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT Camera: public AimedObject {
|
||||
friend QGLENGINE_CORE_EXPORT QDataStream & operator<<(QDataStream & s, const ObjectBase * p);
|
||||
friend QGLENGINE_CORE_EXPORT 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
|
||||
803
src/core/scene/globject.cpp
Normal file
@@ -0,0 +1,803 @@
|
||||
/*
|
||||
QGL ObjectBase & Light
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "globject.h"
|
||||
|
||||
#include "glcamera.h"
|
||||
#include "glmesh.h"
|
||||
#include "glscene.h"
|
||||
|
||||
#include <chunkstream.h>
|
||||
// static int _count = 0;
|
||||
|
||||
ObjectBase::ObjectBase(Mesh * geom, Material * mat) {
|
||||
type_ = glMesh;
|
||||
render_mode = View;
|
||||
prev_pass = rpSolid;
|
||||
parent_ = nullptr;
|
||||
color_ = Qt::white;
|
||||
is_root = is_init = selected_ = false;
|
||||
visible_ = accept_fog = accept_light = cast_shadow = rec_shadow = select_ = true;
|
||||
line_width = -1.;
|
||||
id_ = 0;
|
||||
blend_src = GL_SRC_ALPHA;
|
||||
blend_dest = GL_ONE_MINUS_SRC_ALPHA;
|
||||
type_ = glMesh;
|
||||
raw_matrix = selected_aim = false;
|
||||
mat_.setToIdentity();
|
||||
scene_ = nullptr;
|
||||
mesh_ = geom;
|
||||
material_ = mat;
|
||||
// qDebug() << "ObjectBase, now" << ++_count;
|
||||
}
|
||||
|
||||
|
||||
ObjectBase::~ObjectBase() {
|
||||
// qDebug() << "~ObjectBase, now" << --_count;
|
||||
if (parent_) parent_->children_.removeAll(this);
|
||||
if (scene_) {
|
||||
scene_->__objectDeleted(this);
|
||||
scene_->setTreeChanged();
|
||||
scene_->setTreeStructChanged();
|
||||
}
|
||||
foreach(ObjectBase * c, children_) {
|
||||
c->parent_ = nullptr;
|
||||
delete c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ObjectBase * ObjectBase::clone(bool withChildren) {
|
||||
ObjectBase * o = new ObjectBase();
|
||||
o->prev_pass = prev_pass;
|
||||
o->is_init = false;
|
||||
o->accept_light = accept_light;
|
||||
o->accept_fog = accept_fog;
|
||||
o->visible_ = visible_;
|
||||
o->color_ = color_;
|
||||
o->type_ = type_;
|
||||
o->raw_matrix = raw_matrix;
|
||||
o->mat_ = mat_;
|
||||
o->trans = trans;
|
||||
o->itransform_ = itransform_;
|
||||
o->bound = bound;
|
||||
o->name_ = name_; // + "_copy";
|
||||
o->blend_src = blend_src;
|
||||
o->blend_dest = blend_dest;
|
||||
o->pos_h = pos_h;
|
||||
o->material_ = material_;
|
||||
o->mesh_ = mesh_;
|
||||
o->meta = meta;
|
||||
o->scene_ = nullptr;
|
||||
if (withChildren) {
|
||||
for (int i = 0; i < children_.size(); ++i)
|
||||
o->addChild(children_[i]->clone(withChildren));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::destroy() {
|
||||
if (mesh_) delete mesh_;
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::init() {
|
||||
calculateBoundingBox();
|
||||
// material_.reflection.create();
|
||||
// qDebug() << "init" << vbo.buffer_;
|
||||
is_init = true;
|
||||
}
|
||||
|
||||
|
||||
RenderPass ObjectBase::pass() const {
|
||||
RenderPass ret = rpSolid;
|
||||
if (material_)
|
||||
if (material_->hasTransparency()) ret = rpTransparent;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setScene(Scene * v) {
|
||||
scene_ = v;
|
||||
foreach(ObjectBase * c, children_)
|
||||
c->setScene(v);
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::addChild(ObjectBase * o) {
|
||||
if (o == this) return;
|
||||
if (o->parent_) o->parent_->children_.removeAll(o);
|
||||
children_ << o;
|
||||
o->parent_ = this;
|
||||
o->setScene(scene_);
|
||||
o->buildTransform();
|
||||
/*if (scene_) {
|
||||
ObjectBaseList cl = o->children(true);
|
||||
cl << o;
|
||||
//foreach (ObjectBase * i, cl) {
|
||||
// emit view_->objectAdded(i);
|
||||
//}
|
||||
}*/
|
||||
setSceneTreeChanged();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::removeChild(ObjectBase * o) {
|
||||
if (o == this) return;
|
||||
children_.removeAll(o);
|
||||
o->parent_ = nullptr;
|
||||
o->buildTransform();
|
||||
setSceneTreeChanged();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::removeChild(int index) {
|
||||
children_[index]->parent_ = nullptr;
|
||||
children_[index]->buildTransform();
|
||||
children_.removeAt(index);
|
||||
setSceneTreeChanged();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::clearChildren(bool deleteAll) {
|
||||
foreach(ObjectBase * i, children_) {
|
||||
i->scene_ = nullptr;
|
||||
i->parent_ = nullptr;
|
||||
i->clearChildren(deleteAll);
|
||||
if (deleteAll) {
|
||||
delete i;
|
||||
} else {
|
||||
i->buildTransform();
|
||||
}
|
||||
}
|
||||
children_.clear();
|
||||
setSceneTreeChanged();
|
||||
}
|
||||
|
||||
|
||||
ObjectBase * ObjectBase::child(int index) {
|
||||
if (index < 0 || index >= children_.size()) return nullptr;
|
||||
return children_[index];
|
||||
}
|
||||
|
||||
|
||||
ObjectBase * ObjectBase::child(const QString & name) {
|
||||
foreach(ObjectBase * i, children_)
|
||||
if (i->name_ == name) return i;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
const ObjectBase * ObjectBase::child(int index) const {
|
||||
if (index < 0 || index >= children_.size()) return nullptr;
|
||||
return children_[index];
|
||||
}
|
||||
|
||||
|
||||
const ObjectBase * ObjectBase::child(const QString & name) const {
|
||||
foreach(ObjectBase * i, children_)
|
||||
if (i->name_ == name) return i;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
ObjectBaseList ObjectBase::children(bool all_) {
|
||||
if (!all_) return children_;
|
||||
ObjectBaseList cl;
|
||||
addChildren(cl, this);
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
bool ObjectBase::isVisible(bool check_parents) const {
|
||||
if (!check_parents) return visible_;
|
||||
if (!visible_) return false;
|
||||
ObjectBase * p = parent_;
|
||||
while (p) {
|
||||
if (!p->visible_) return false;
|
||||
p = p->parent_;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setVisible(bool v) {
|
||||
visible_ = v;
|
||||
setSceneTreeChanged();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::rotateZ(GLfloat a) {
|
||||
raw_matrix = false;
|
||||
trans.setRotationZ(trans.rotationZ() + a);
|
||||
// angles_.setZ(angles_.z() + a);
|
||||
// while (angles_.z() < -360.f) angles_.setZ(angles_.z() + 360.f);
|
||||
// while (angles_.z() > 360.f) angles_.setZ(angles_.z() - 360.f);
|
||||
buildTransform();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setRotationZ(GLfloat a) {
|
||||
raw_matrix = false;
|
||||
trans.setRotationZ(a);
|
||||
// angles_.setZ(a);
|
||||
// while (angles_.z() < -360.f) angles_.setZ(angles_.z() + 360.f);
|
||||
// while (angles_.z() > 360.f) angles_.setZ(angles_.z() - 360.f);
|
||||
buildTransform();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setTransform(const Transform & t) {
|
||||
trans = t;
|
||||
buildTransform();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::addChildren(ObjectBaseList & list, ObjectBase * where) {
|
||||
foreach(ObjectBase * i, where->children_) {
|
||||
list << i;
|
||||
addChildren(list, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::calculateBoundingBox() {
|
||||
bound = Box3D();
|
||||
if (mesh_) {
|
||||
bound = mesh_->boundingBox();
|
||||
QVector<QVector3D> c = bound.corners(), tc;
|
||||
foreach(QVector3D p, c)
|
||||
tc << (itransform_ * QVector4D(p, 1)).toVector3D();
|
||||
bound = Box3D(tc);
|
||||
}
|
||||
foreach(ObjectBase * i, children_) {
|
||||
i->calculateBoundingBox();
|
||||
bound |= i->boundingBox();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::updateTransform() {
|
||||
buildTransform(true);
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setProperty(const QString & pn, const QVariant & v) {
|
||||
meta[pn] = v;
|
||||
}
|
||||
|
||||
|
||||
QVariant ObjectBase::property(const QString & pn, bool * exists) const {
|
||||
if (exists) *exists = meta.contains(pn);
|
||||
return meta.value(pn);
|
||||
}
|
||||
|
||||
|
||||
bool ObjectBase::hasProperty(const QString & pn) const {
|
||||
return meta.contains(pn);
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::removeProperty(const QString & pn) {
|
||||
meta.remove(pn);
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setMatrix(const QMatrix4x4 & t) {
|
||||
// raw_matrix = true;
|
||||
// mat_ = t;
|
||||
// pos_ = mat_.column(3).toVector3D();
|
||||
// mat_.setColumn(3, QVector4D(0., 0., 0., 1.));
|
||||
raw_matrix = false;
|
||||
trans.setMatrix(t);
|
||||
buildTransform();
|
||||
}
|
||||
|
||||
|
||||
QMatrix4x4 ObjectBase::matrix() const {
|
||||
return trans.matrix();
|
||||
}
|
||||
|
||||
|
||||
QVector3D ObjectBase::inParentSpace(const QVector3D & v) const {
|
||||
if (!parent_) return v;
|
||||
return (parent_->matrix() * QVector4D(v, 1)).toVector3D();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::transferTransformToChildren(bool only_scale) {
|
||||
QMatrix4x4 m = trans.matrix();
|
||||
if (only_scale) m = trans.matrixScale();
|
||||
foreach(ObjectBase * i, children_)
|
||||
i->trans.setMatrix(m * i->trans.matrix());
|
||||
if (only_scale)
|
||||
resetScale();
|
||||
else
|
||||
setMatrix(QMatrix4x4());
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::cleanTree() {
|
||||
for (int i = 0; i < children_.size(); ++i) {
|
||||
ObjectBase * o = children_[i];
|
||||
if (!o->hasChildren() && !o->mesh() && (o->type() == glMesh)) {
|
||||
delete o;
|
||||
--i;
|
||||
}
|
||||
o->cleanTree();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ObjectBase::isSelected(bool check_parents) const {
|
||||
if (!check_parents) return selected_;
|
||||
if (selected_) return true;
|
||||
ObjectBase * p = parent_;
|
||||
while (p) {
|
||||
if (p->selected_) return true;
|
||||
p = p->parent_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setSelected(bool yes) {
|
||||
// qDebug() << "select" << name() << view_;
|
||||
if (select_) selected_ = yes;
|
||||
if (!selected_) selected_aim = false;
|
||||
}
|
||||
|
||||
|
||||
ObjectBase * ObjectBase::selectedParent() const {
|
||||
ObjectBase * p = parent_;
|
||||
while (p) {
|
||||
if (p->selected_) return p;
|
||||
p = p->parent_;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setMaterial(Material * m, bool with_children) {
|
||||
material_ = m;
|
||||
if (with_children)
|
||||
foreach(ObjectBase * i, children_)
|
||||
i->setMaterial(m, true);
|
||||
setObjectsChanged();
|
||||
if (scene_) scene_->mat_changed = scene_->tree_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setColor(QColor c, bool with_children) {
|
||||
color_ = c;
|
||||
if (with_children)
|
||||
foreach(ObjectBase * i, children_)
|
||||
i->setColor(c, true);
|
||||
setObjectsChanged();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setMesh(Mesh * v) {
|
||||
if (scene_) v = scene_->attachMesh(v);
|
||||
mesh_ = v;
|
||||
setSceneTreeChanged();
|
||||
setObjectsChanged();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::buildTransform(bool force) {
|
||||
if (force) trans.setDirty();
|
||||
itransform_.setToIdentity();
|
||||
ObjectBase * p = parent_;
|
||||
if (p) itransform_ = p->itransform_;
|
||||
// if (raw_matrix) {
|
||||
// itransform_.translate(pos_);
|
||||
// itransform_ *= mat_;
|
||||
// //qDebug() << "raw_matrix" << itransform_;
|
||||
// } else
|
||||
localTransform(itransform_);
|
||||
// qDebug() << name_ << itransform_;
|
||||
foreach(ObjectBase * i, children_)
|
||||
i->buildTransform(force);
|
||||
setObjectsChanged();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::initInternal() {
|
||||
init();
|
||||
foreach(ObjectBase * i, children_)
|
||||
i->initInternal();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::localTransform(QMatrix4x4 & m) {
|
||||
m *= trans.matrix();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setSceneTreeChanged() {
|
||||
if (scene_) {
|
||||
scene_->setTreeChanged();
|
||||
scene_->setTreeStructChanged();
|
||||
}
|
||||
setObjectsChanged();
|
||||
}
|
||||
|
||||
|
||||
void ObjectBase::setObjectsChanged() {
|
||||
int p = pass();
|
||||
if (mesh_) {
|
||||
mesh_->setObjectsChanged(p, true);
|
||||
mesh_->setSelectionChanged(p, true);
|
||||
if (prev_pass != p) {
|
||||
mesh_->setObjectsChanged(prev_pass, true);
|
||||
mesh_->setSelectionChanged(prev_pass, true);
|
||||
}
|
||||
}
|
||||
prev_pass = p;
|
||||
}
|
||||
|
||||
|
||||
QMatrix4x4 ObjectBase::worldMatrix(QMatrix4x4 parent) const {
|
||||
QMatrix4x4 mat;
|
||||
// mat.translate(pos_);
|
||||
// if (raw_matrix) {
|
||||
// mat *= mat_;
|
||||
// } else {
|
||||
// if (angles_.z() != 0.f) mat.rotate(angles_.z(), 0., 0., 1.);
|
||||
// if (angles_.y() != 0.f) mat.rotate(angles_.y(), 0., 1., 0.);
|
||||
// if (angles_.x() != 0.f) mat.rotate(angles_.x(), 1., 0., 0.);
|
||||
// mat.scale(scale_);
|
||||
// }
|
||||
mat = trans.matrix();
|
||||
return parent * mat;
|
||||
}
|
||||
|
||||
|
||||
AimedObject::AimedObject() {
|
||||
aim_dist = 1.;
|
||||
}
|
||||
|
||||
|
||||
QVector3D AimedObject::worldAim() const {
|
||||
QVector3D ret = worldPos() + worldDirection() * aim_dist;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::setAim(const QVector3D & p) {
|
||||
QVector3D dir = p - pos();
|
||||
trans.setRotation(Transform::fromDirection(dir, trans.rotationY()));
|
||||
aim_dist = dir.length();
|
||||
buildTransform();
|
||||
// if (!p.isNull())
|
||||
// qDebug() << "setAim" << p << aim() << worldAim();
|
||||
}
|
||||
|
||||
|
||||
QVector3D AimedObject::direction() const {
|
||||
return trans.direction();
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::setDirection(const QVector3D & d) {
|
||||
// double len = qMax(aim_.length(), 0.001f);
|
||||
// aim_ = d.normalized() * len;
|
||||
buildTransform();
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::flyCloser(double s) {
|
||||
double tl = 1. / (1. + s);
|
||||
move(direction() * aim_dist * (1. - tl));
|
||||
aim_dist *= tl;
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::flyFarer(double s) {
|
||||
double tl = 1. * (1. + s);
|
||||
move(direction() * aim_dist * (1. - tl));
|
||||
aim_dist *= tl;
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::flyToDistance(double d) {
|
||||
move(direction() * (aim_dist - d));
|
||||
aim_dist = d;
|
||||
// qDebug() << d << (aim() - pos()).length() << aim();
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::moveForward(const float & x, bool withZ) {
|
||||
QVector3D dv = itransform_.mapVector(QVector3D(0, 0, -x));
|
||||
if (!withZ) dv[2] = 0.;
|
||||
move(dv);
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::moveLeft(const float & x, bool withZ) {
|
||||
QVector3D dv = itransform_.mapVector(QVector3D(-x, 0, 0));
|
||||
if (!withZ) dv[2] = 0.;
|
||||
move(dv);
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::moveUp(const float & x, bool onlyZ) {
|
||||
QVector3D dv = itransform_.mapVector(QVector3D(0, x, 0));
|
||||
if (onlyZ) dv[0] = dv[1] = 0.;
|
||||
move(dv);
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::orbitZ(const float & a) {
|
||||
QVector3D pa = aim();
|
||||
rotateZ(-a);
|
||||
move(pa - aim());
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::orbitXY(const float & a) {
|
||||
QVector3D pa = aim();
|
||||
rotateX(-a);
|
||||
move(pa - aim());
|
||||
}
|
||||
|
||||
|
||||
void AimedObject::transformChanged() {}
|
||||
|
||||
|
||||
Light::Light(): AimedObject(), shadow_map(0, true, GL_R16F) {
|
||||
type_ = glLight;
|
||||
light_type = Omni;
|
||||
intensity = 1.;
|
||||
angle_start = angle_end = 180.;
|
||||
decay_linear = decay_quadratic = decay_start = 0.;
|
||||
decay_const = decay_end = 1.;
|
||||
setDirection(0, 0, -1.);
|
||||
}
|
||||
|
||||
|
||||
Light::Light(const QVector3D & p, const QColor & c, float i): AimedObject(), shadow_map(0, true, GL_R16F) {
|
||||
type_ = glLight;
|
||||
light_type = Omni;
|
||||
intensity = i;
|
||||
color_ = c;
|
||||
angle_start = angle_end = 180.;
|
||||
decay_linear = decay_quadratic = decay_start = 0.;
|
||||
decay_const = decay_end = 1.;
|
||||
setPos(p);
|
||||
setDirection(0, 0, -1.);
|
||||
}
|
||||
|
||||
|
||||
ObjectBase * Light::clone(bool withChildren) {
|
||||
Light * o = new Light(*this);
|
||||
// GLObjectBase::clone(withChildren);
|
||||
o->is_init = false;
|
||||
o->name_ = name_; // + "_copy";
|
||||
o->scene_ = nullptr;
|
||||
o->children_.clear();
|
||||
if (withChildren) {
|
||||
for (int i = 0; i < children_.size(); ++i)
|
||||
o->addChild(children_[i]->clone(withChildren));
|
||||
}
|
||||
o->color_ = color_;
|
||||
o->light_type = light_type;
|
||||
o->trans = trans;
|
||||
o->aim_dist = aim_dist;
|
||||
o->angle_start = angle_start;
|
||||
o->angle_end = angle_end;
|
||||
o->intensity = intensity;
|
||||
o->decay_const = decay_const;
|
||||
o->decay_linear = decay_linear;
|
||||
o->decay_quadratic = decay_quadratic;
|
||||
o->meta = meta;
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
void Light::apply() {
|
||||
if (scene_) scene_->setLightsChanged();
|
||||
}
|
||||
|
||||
|
||||
QDataStream & operator<<(QDataStream & s, const ObjectBase * p) {
|
||||
ChunkStream cs;
|
||||
// qDebug() << "place" << p->name() << "...";
|
||||
cs.add(1, int(p->type_))
|
||||
.add(2, p->accept_light)
|
||||
.add(3, p->accept_fog)
|
||||
.add(4, p->visible_)
|
||||
.add(5, p->cast_shadow)
|
||||
.add(6, p->rec_shadow)
|
||||
.add(7, p->raw_matrix)
|
||||
.add(8, p->line_width)
|
||||
.add(9, int(p->render_mode))
|
||||
.add(14, p->mat_)
|
||||
.add(16, p->children_.size())
|
||||
.add(17, p->name_)
|
||||
.add(18, p->meta)
|
||||
.add(19, p->color_)
|
||||
.add(20, p->trans);
|
||||
// qDebug() << "place self done";
|
||||
if (p->type_ == ObjectBase::glLight) {
|
||||
// qDebug() << "place light ...";
|
||||
const Light * l = (const Light *)p;
|
||||
cs.add(101, l->angle_start)
|
||||
.add(102, l->angle_end)
|
||||
.add(103, l->intensity)
|
||||
.add(104, l->decay_const)
|
||||
.add(105, l->decay_linear)
|
||||
.add(106, l->decay_quadratic)
|
||||
.add(107, l->decay_start)
|
||||
.add(108, l->decay_end)
|
||||
.add(109, int(l->light_type))
|
||||
.add(111, l->distance());
|
||||
}
|
||||
if (p->type_ == ObjectBase::glCamera) {
|
||||
// qDebug() << "place camera ...";
|
||||
const Camera * c = (const Camera *)p;
|
||||
cs.add(200, c->aim())
|
||||
.add(201, c->fov_)
|
||||
.add(202, c->depth_start)
|
||||
.add(206, c->mirror_x)
|
||||
.add(207, c->mirror_y)
|
||||
.add(208, c->distance())
|
||||
.add(209, c->roll_);
|
||||
}
|
||||
// qDebug() << "place" << p->name() << cs.data().size() << s.device()->size();
|
||||
s << cs.data();
|
||||
foreach(const ObjectBase * c, p->children_)
|
||||
s << c;
|
||||
return s;
|
||||
}
|
||||
QDataStream & operator>>(QDataStream & s, ObjectBase *& p) {
|
||||
ChunkStream cs(s);
|
||||
p = nullptr;
|
||||
int ccnt = 0;
|
||||
Light * l = nullptr;
|
||||
Camera * c = nullptr;
|
||||
// qDebug() << "read obj ...";
|
||||
while (!cs.atEnd()) {
|
||||
switch (cs.read()) {
|
||||
case 1: {
|
||||
ObjectBase::Type type = (ObjectBase::Type)cs.getData<int>();
|
||||
switch (type) {
|
||||
case ObjectBase::glMesh: p = new ObjectBase(); break;
|
||||
case ObjectBase::glLight:
|
||||
p = new Light();
|
||||
l = (Light *)p;
|
||||
break;
|
||||
case ObjectBase::glCamera:
|
||||
p = new Camera();
|
||||
c = (Camera *)p;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (p) p->type_ = type;
|
||||
} break;
|
||||
case 2:
|
||||
if (p) p->accept_light = cs.getData<bool>();
|
||||
break;
|
||||
case 3:
|
||||
if (p) p->accept_fog = cs.getData<bool>();
|
||||
break;
|
||||
case 4:
|
||||
if (p) p->visible_ = cs.getData<bool>();
|
||||
break;
|
||||
case 5:
|
||||
if (p) p->cast_shadow = cs.getData<bool>();
|
||||
break;
|
||||
case 6:
|
||||
if (p) p->rec_shadow = cs.getData<bool>();
|
||||
break;
|
||||
case 7:
|
||||
if (p) p->raw_matrix = cs.getData<bool>();
|
||||
break;
|
||||
case 8:
|
||||
if (p) p->line_width = cs.getData<float>();
|
||||
break;
|
||||
case 9:
|
||||
if (p) p->render_mode = (ObjectBase::RenderMode)cs.getData<int>();
|
||||
break;
|
||||
case 14:
|
||||
if (p) p->mat_ = cs.getData<QMatrix4x4>();
|
||||
break;
|
||||
case 16:
|
||||
if (p) ccnt = cs.getData<int>();
|
||||
break;
|
||||
case 17:
|
||||
if (p) p->name_ = cs.getData<QString>();
|
||||
break;
|
||||
case 18:
|
||||
if (p) p->meta = cs.getData<QVariantMap>();
|
||||
break;
|
||||
case 19:
|
||||
if (p) p->color_ = cs.getData<QColor>();
|
||||
break;
|
||||
case 20:
|
||||
if (p) p->trans = cs.getData<Transform>();
|
||||
break;
|
||||
case 100:
|
||||
if (l) l->setAim(cs.getData<QVector3D>());
|
||||
break;
|
||||
case 101:
|
||||
if (l) l->angle_start = cs.getData<GLfloat>();
|
||||
break;
|
||||
case 102:
|
||||
if (l) l->angle_end = cs.getData<GLfloat>();
|
||||
break;
|
||||
case 103:
|
||||
if (l) l->intensity = cs.getData<GLfloat>();
|
||||
break;
|
||||
case 104:
|
||||
if (l) l->decay_const = cs.getData<GLfloat>();
|
||||
break;
|
||||
case 105:
|
||||
if (l) l->decay_linear = cs.getData<GLfloat>();
|
||||
break;
|
||||
case 106:
|
||||
if (l) l->decay_quadratic = cs.getData<GLfloat>();
|
||||
break;
|
||||
case 107:
|
||||
if (l) l->decay_start = cs.getData<GLfloat>();
|
||||
break;
|
||||
case 108:
|
||||
if (l) l->decay_end = cs.getData<GLfloat>();
|
||||
break;
|
||||
case 109:
|
||||
if (l) l->light_type = (Light::Type)cs.getData<int>();
|
||||
break;
|
||||
case 111:
|
||||
if (l) l->setDistance(cs.getData<double>());
|
||||
break;
|
||||
case 200:
|
||||
if (c) c->setAim(cs.getData<QVector3D>());
|
||||
break;
|
||||
case 201:
|
||||
if (c) c->setFOV(cs.getData<GLfloat>());
|
||||
break;
|
||||
case 202:
|
||||
if (c) c->setDepthStart(cs.getData<GLfloat>());
|
||||
break;
|
||||
case 206:
|
||||
if (c) c->mirror_x = cs.getData<bool>();
|
||||
break;
|
||||
case 207:
|
||||
if (c) c->mirror_y = cs.getData<bool>();
|
||||
break;
|
||||
case 208:
|
||||
if (c) c->setDistance(cs.getData<double>());
|
||||
break;
|
||||
case 209:
|
||||
if (c) c->roll_ = cs.getData<GLfloat>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// qDebug() << p->name() << ccnt;
|
||||
for (int i = 0; i < ccnt; ++i) {
|
||||
ObjectBase * c = nullptr;
|
||||
s >> c;
|
||||
if (!c) continue;
|
||||
c->parent_ = p;
|
||||
p->children_ << c;
|
||||
}
|
||||
p->buildTransform();
|
||||
return s;
|
||||
}
|
||||
453
src/core/scene/globject.h
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
QGL ObjectBase & Light
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLOBJECT_H
|
||||
#define GLOBJECT_H
|
||||
|
||||
#include "glframebuffer.h"
|
||||
#include "glmaterial.h"
|
||||
#include "gltransform.h"
|
||||
#include "gltypes.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT ObjectBase {
|
||||
friend class Scene;
|
||||
friend class RendererSelection;
|
||||
friend QGLENGINE_CORE_EXPORT QDataStream & operator<<(QDataStream & s, const ObjectBase * p);
|
||||
friend QGLENGINE_CORE_EXPORT QDataStream & operator>>(QDataStream & s, ObjectBase *& p);
|
||||
friend QGLENGINE_CORE_EXPORT 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;
|
||||
uint id() const { return id_; }
|
||||
|
||||
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;
|
||||
void setAimSelected(bool yes) { selected_aim = yes; }
|
||||
bool isAimSelected() const { return selected_aim; }
|
||||
|
||||
bool isSelectable() const { return select_; }
|
||||
void setSelectable(bool yes) { select_ = 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;
|
||||
|
||||
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 QGLENGINE_CORE_EXPORT 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 QGLENGINE_CORE_EXPORT 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;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
inline T globject_cast(ObjectBase * object) {
|
||||
return reinterpret_cast<T>(object);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline T globject_cast(const ObjectBase * object) {
|
||||
return reinterpret_cast<T>(object);
|
||||
}
|
||||
|
||||
|
||||
QGLENGINE_CORE_EXPORT QDataStream & operator<<(QDataStream & s, const ObjectBase * p);
|
||||
QGLENGINE_CORE_EXPORT QDataStream & operator>>(QDataStream & s, ObjectBase *& p);
|
||||
|
||||
inline ObjectBaseList lights2objectList(const QList<Light *> & v) {
|
||||
ObjectBaseList ret;
|
||||
foreach(Light * i, v)
|
||||
ret << (ObjectBase *)i;
|
||||
return ret;
|
||||
}
|
||||
inline ObjectBaseList cameras2objectList(const QList<Camera *> & v) {
|
||||
ObjectBaseList ret;
|
||||
foreach(Camera * i, v)
|
||||
ret << (ObjectBase *)i;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // GLOBJECT_H
|
||||
543
src/core/scene/glscene.cpp
Normal file
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
QGL Scene
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glscene.h"
|
||||
|
||||
#include "glcamera.h"
|
||||
#include "glmesh.h"
|
||||
#include "qglview.h"
|
||||
|
||||
#include <chunkstream.h>
|
||||
|
||||
|
||||
Scene::Scene() {
|
||||
root_ = new ObjectBase();
|
||||
root_->setScene(this);
|
||||
tree_changed = mat_changed = lights_changed = true;
|
||||
destroying = false;
|
||||
need_reload_materials = tree_struct_changed = true;
|
||||
sel_mode_ = smSingleSelection;
|
||||
}
|
||||
|
||||
|
||||
Scene::~Scene() {
|
||||
destroying = true;
|
||||
clear();
|
||||
delete root_;
|
||||
}
|
||||
|
||||
|
||||
Scene * Scene::clone() {
|
||||
Scene * ret = new Scene();
|
||||
ObjectBase * o = root_->clone();
|
||||
foreach(ObjectBase * co, o->children())
|
||||
ret->addObject(co);
|
||||
o->clearChildren();
|
||||
delete o;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void Scene::addObject(ObjectBase * o) {
|
||||
ObjectBaseList aol = o->children(true);
|
||||
attachObject(o);
|
||||
foreach(ObjectBase * c, aol)
|
||||
attachObject(c);
|
||||
root_->addChild(o);
|
||||
tree_changed = tree_struct_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void Scene::addScene(const Scene * s) {
|
||||
if (!s) return;
|
||||
// qDebug() << "addScene clone ...";
|
||||
ObjectBase * o = s->root_->clone();
|
||||
o->setName(s->name());
|
||||
// qDebug() << "addScene clone ok" << o << o->children(true).size();
|
||||
addObject(o);
|
||||
makeMaterialsUniqueNames();
|
||||
// qDebug() << "addScene add ok" << o;
|
||||
}
|
||||
|
||||
|
||||
void Scene::assignFrom(const Scene * s) {
|
||||
clear();
|
||||
if (!s) return;
|
||||
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::reinitAll() {
|
||||
for (auto * i: geometries)
|
||||
i->reinit();
|
||||
for (auto * i: td_geometries)
|
||||
i->reinit();
|
||||
setTreeChanged();
|
||||
mat_changed = lights_changed = true;
|
||||
need_reload_materials = true;
|
||||
tree_struct_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void Scene::objectsCountInternal(int * cnt, ObjectBase * where) {
|
||||
++(*cnt);
|
||||
foreach(ObjectBase * i, where->children())
|
||||
objectsCountInternal(cnt, i);
|
||||
}
|
||||
int Scene::objectsCount(bool all) {
|
||||
if (!all) return root_->childCount();
|
||||
int cnt = 0;
|
||||
objectsCountInternal(&cnt, root_);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
void Scene::removeObjectInternal(ObjectBase * o, ObjectBase * where) {
|
||||
if (destroying) return;
|
||||
foreach(ObjectBase * i, where->children()) {
|
||||
if (o == i) {
|
||||
where->removeChild(i);
|
||||
setObjectMeshChanged(i);
|
||||
} else
|
||||
removeObjectInternal(o, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Scene::emitSelectionChanged() {
|
||||
selected_top.clear();
|
||||
foreach(ObjectBase * o, selected_) {
|
||||
ObjectBase * po = o->selectedParent();
|
||||
if (!po) po = o;
|
||||
if (!selected_top.contains(po)) selected_top << po;
|
||||
}
|
||||
foreach(Mesh * m, geometries)
|
||||
m->setAllSelectionChanged(true);
|
||||
selectionChanged();
|
||||
}
|
||||
|
||||
|
||||
QString Scene::uniqueName(QString n, const QSet<QString> & names) {
|
||||
if (!names.contains(n)) return n;
|
||||
QString num;
|
||||
while (!n.isEmpty()) {
|
||||
if (n.right(1)[0].isDigit()) {
|
||||
num.push_front(n.right(1));
|
||||
n.chop(1);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (!n.endsWith('_')) n += '_';
|
||||
int in = num.toInt() + 1;
|
||||
QString nn = n + QString::number(in).rightJustified(3, '0');
|
||||
while (names.contains(nn))
|
||||
nn = n + QString::number(++in).rightJustified(3, '0');
|
||||
return nn;
|
||||
}
|
||||
|
||||
|
||||
void Scene::removeObject(ObjectBase * o, bool inChildren) {
|
||||
if (destroying) return;
|
||||
o->setScene(nullptr);
|
||||
setObjectMeshChanged(o);
|
||||
if (inChildren)
|
||||
removeObjectInternal(o, root_);
|
||||
else
|
||||
root_->removeChild(o);
|
||||
__objectDeleted(o);
|
||||
setTreeStructChanged();
|
||||
}
|
||||
|
||||
|
||||
void Scene::removeObject(ObjectBase & o, bool inChildren) {
|
||||
if (destroying) return;
|
||||
removeObject(&o, inChildren);
|
||||
}
|
||||
|
||||
|
||||
void Scene::clearObjects(bool deleteAll) {
|
||||
root_->clearChildren(deleteAll);
|
||||
cleanUnused();
|
||||
setTreeStructChanged();
|
||||
emitSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
void Scene::selectObject(ObjectBase * o, bool add_to_selection) {
|
||||
// qDebug() << "selectObject" << o << add_to_selection;
|
||||
if (!add_to_selection || (sel_mode_ == smSingleSelection)) clearSelection();
|
||||
if (o) {
|
||||
if (!add_to_selection)
|
||||
o->setSelected(true);
|
||||
else
|
||||
o->setSelected(!o->isSelected());
|
||||
gatherSelection();
|
||||
}
|
||||
emitSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
void Scene::selectObjects(ObjectBaseList ol, bool add_to_selection) {
|
||||
if (!add_to_selection || (sel_mode_ == smSingleSelection)) clearSelection();
|
||||
foreach(ObjectBase * o, ol) {
|
||||
if (!o) continue;
|
||||
o->setSelected(true);
|
||||
}
|
||||
gatherSelection();
|
||||
emitSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
void Scene::selectObjectsByMesh() {
|
||||
ObjectBaseList csl = selected_;
|
||||
QSet<Mesh *> sml;
|
||||
foreach(ObjectBase * o, csl)
|
||||
if (o->mesh()) sml << o->mesh();
|
||||
ObjectBaseList ol = root_->children(true);
|
||||
foreach(ObjectBase * o, ol) {
|
||||
if (sml.contains(o->mesh())) o->setSelected(true);
|
||||
}
|
||||
gatherSelection();
|
||||
emitSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
void Scene::selectObjectsByMaterial() {
|
||||
ObjectBaseList csl = selected_;
|
||||
QSet<Material *> sml;
|
||||
foreach(ObjectBase * o, csl)
|
||||
if (o->material()) sml << o->material();
|
||||
ObjectBaseList ol = root_->children(true);
|
||||
foreach(ObjectBase * o, ol) {
|
||||
if (sml.contains(o->material())) o->setSelected(true);
|
||||
}
|
||||
gatherSelection();
|
||||
emitSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
void Scene::clearSelection() {
|
||||
selected_.clear();
|
||||
selected_top.clear();
|
||||
ObjectBaseList ol = root_->children(true);
|
||||
foreach(ObjectBase * o, ol) {
|
||||
o->setSelected(false);
|
||||
o->setAimSelected(false);
|
||||
}
|
||||
emitSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
ObjectBaseList Scene::selectedObjects(bool top_only) const {
|
||||
return top_only ? selected_top : selected_;
|
||||
}
|
||||
|
||||
|
||||
ObjectBase * Scene::selectedObject() const {
|
||||
if (selected_.isEmpty()) return 0;
|
||||
return selected_[0];
|
||||
}
|
||||
|
||||
|
||||
void gatherMeshes(ObjectBase * o, QSet<Mesh *> & ums) {
|
||||
if (o->mesh()) ums << o->mesh();
|
||||
for (int i = 0; i < o->childCount(); ++i)
|
||||
gatherMeshes(o->child(i), ums);
|
||||
}
|
||||
void Scene::cleanUnused() {
|
||||
QSet<Mesh *> ums;
|
||||
gatherMeshes(root_, ums);
|
||||
for (int i = 0; i < geometries.size(); ++i) {
|
||||
if (ums.contains(geometries[i])) continue;
|
||||
td_geometries << geometries[i];
|
||||
geometries.removeAt(i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Box3D & Scene::boundingBox() const {
|
||||
root_->calculateBoundingBox();
|
||||
return root_->boundingBox();
|
||||
}
|
||||
|
||||
|
||||
Material * Scene::newMaterial(const QString & name) {
|
||||
materials << new Material(name);
|
||||
makeMaterialsUniqueNames();
|
||||
mat_changed = true;
|
||||
return materials.back();
|
||||
}
|
||||
|
||||
|
||||
void Scene::removeMaterial(Material * m) {
|
||||
if (!m || !materials.contains(m)) return;
|
||||
ObjectBaseList ol = root_->children(true);
|
||||
foreach(ObjectBase * o, ol)
|
||||
if (o->material_ == m) o->setMaterial(0);
|
||||
materials.removeAll(m);
|
||||
changed_materials.removeAll(m);
|
||||
mat_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void Scene::makeMaterialsUniqueNames() {
|
||||
QSet<QString> names;
|
||||
foreach(Material * m, materials) {
|
||||
if (m->name.isEmpty()) m->name = "default_000";
|
||||
m->name = uniqueName(m->name, names);
|
||||
names << m->name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ObjectBaseList Scene::objects(bool all) {
|
||||
return root_->children(all);
|
||||
}
|
||||
|
||||
|
||||
void Scene::removeLight(Light * l) {
|
||||
removeObject(l);
|
||||
}
|
||||
|
||||
|
||||
void Scene::dump() {
|
||||
qDebug() << "Scene" << name();
|
||||
qDebug() << "Meshes:" << geometries.size();
|
||||
qDebug() << "Objects:" << root_->children(true).size();
|
||||
}
|
||||
|
||||
|
||||
void Scene::gatherSelection() {
|
||||
selected_.clear();
|
||||
ObjectBaseList ol = root_->children(true);
|
||||
foreach(ObjectBase * o, ol)
|
||||
if (o->isSelected()) selected_ << o;
|
||||
}
|
||||
|
||||
|
||||
void Scene::attachObject(ObjectBase * o) {
|
||||
if (!o) return;
|
||||
o->setScene(this);
|
||||
if (o->mesh()) { // search suitable mesh in this scene
|
||||
o->setMesh(attachMesh(o->mesh()));
|
||||
setObjectMeshChanged(o);
|
||||
}
|
||||
if (o->material()) { // search suitable material in this scene
|
||||
uint ohash = o->material()->hash();
|
||||
bool need_new = true;
|
||||
foreach(Material * m, materials) {
|
||||
if (m == o->material()) { // already exists by ptr
|
||||
need_new = false;
|
||||
break;
|
||||
}
|
||||
if (m->hash() == ohash) { // already exists by hash
|
||||
need_new = false;
|
||||
o->setMaterial(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (need_new) { // need to clone material and add to scene
|
||||
Material * nmat = new Material();
|
||||
*nmat = *(o->material());
|
||||
nmat->setMapsChanged();
|
||||
o->setMaterial(nmat);
|
||||
materials << nmat;
|
||||
}
|
||||
}
|
||||
setTreeStructChanged();
|
||||
}
|
||||
|
||||
|
||||
Mesh * Scene::attachMesh(Mesh * mesh) {
|
||||
if (!mesh) return 0;
|
||||
uint mhash = mesh->hash();
|
||||
foreach(Mesh * m, geometries) {
|
||||
if (m == mesh) { // already exists by ptr
|
||||
return m;
|
||||
}
|
||||
if (m->hash() == mhash) { // already exists by hash
|
||||
return m;
|
||||
}
|
||||
}
|
||||
// need to clone mesh and add to scene
|
||||
Mesh * nmesh = mesh->clone();
|
||||
geometries << nmesh;
|
||||
return nmesh;
|
||||
}
|
||||
|
||||
|
||||
void Scene::setTreeChanged() {
|
||||
if (destroying) return;
|
||||
tree_changed = true;
|
||||
foreach(Mesh * m, geometries) {
|
||||
m->setAllObjectsChanged(true);
|
||||
m->setAllSelectionChanged(true);
|
||||
}
|
||||
gatherSelection();
|
||||
}
|
||||
|
||||
|
||||
void Scene::setTreeStructChanged() {
|
||||
tree_struct_changed = true;
|
||||
}
|
||||
|
||||
|
||||
void Scene::setObjectMeshChanged(ObjectBase * o) {
|
||||
if (o) o->setObjectsChanged();
|
||||
}
|
||||
|
||||
|
||||
void Scene::prepareTree(ObjectBase * o) {
|
||||
foreach(ObjectBase * co, o->children_) {
|
||||
if (co->isHidden()) continue;
|
||||
switch (co->type_) {
|
||||
case ObjectBase::glLight: {
|
||||
Light * l = globject_cast<Light *>(co);
|
||||
lights_used[l->light_type] << l;
|
||||
} break;
|
||||
case ObjectBase::glMesh:
|
||||
if (co->mesh()) {
|
||||
geometries_used[co->pass()][co->mesh()] << co;
|
||||
co->setObjectsChanged();
|
||||
}
|
||||
break;
|
||||
case ObjectBase::glCamera: cameras_used << globject_cast<Camera *>(co); break;
|
||||
default: break;
|
||||
}
|
||||
prepareTree(co);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Scene::prepare() {
|
||||
changed_materials.clear();
|
||||
foreach(Material * m, materials) {
|
||||
if (m->_changed) {
|
||||
need_reload_materials = tree_changed = true;
|
||||
changed_materials << m;
|
||||
}
|
||||
}
|
||||
|
||||
ObjectBaseList aol;
|
||||
if (!tree_changed && !mat_changed) return false;
|
||||
aol = root_->children(true);
|
||||
if (tree_changed) {
|
||||
geometries_used[rpSolid].clear();
|
||||
geometries_used[rpTransparent].clear();
|
||||
lights_used.clear();
|
||||
cameras_used.clear();
|
||||
prepareTree(root_);
|
||||
if (tree_struct_changed) {
|
||||
tree_struct_changed = false;
|
||||
cleanUnused();
|
||||
QMetaObject::invokeMethod(this, "treeChanged", Qt::QueuedConnection);
|
||||
}
|
||||
tree_changed = false;
|
||||
lights_changed = true;
|
||||
}
|
||||
mat_changed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Scene::destroy(QOpenGLExtraFunctions * f) {
|
||||
foreach(Mesh * g, geometries)
|
||||
g->destroy(f);
|
||||
}
|
||||
|
||||
|
||||
void Scene::destroyUnused(QOpenGLExtraFunctions * f) {
|
||||
foreach(Mesh * i, td_geometries)
|
||||
i->destroy(f);
|
||||
qDeleteAll(td_geometries);
|
||||
td_geometries.clear();
|
||||
}
|
||||
|
||||
|
||||
QDataStream & operator<<(QDataStream & s, const Scene * p) {
|
||||
ChunkStream cs;
|
||||
// qDebug() << "place" << p->name() << "...";
|
||||
QVector<short> geom_ind, mat_ind;
|
||||
ObjectBaseList cl = p->root_->children(true);
|
||||
geom_ind.reserve(cl.size());
|
||||
mat_ind.reserve(cl.size());
|
||||
foreach(ObjectBase * c, cl) {
|
||||
geom_ind << p->geometries.indexOf(c->mesh());
|
||||
mat_ind << p->materials.indexOf(c->material());
|
||||
}
|
||||
cs.add(1, p->name_).add(10, p->geometries).add(11, p->materials).add(20, p->root_).add(21, geom_ind).add(22, mat_ind);
|
||||
s << qCompress(cs.data());
|
||||
return s;
|
||||
}
|
||||
QDataStream & operator>>(QDataStream & s, Scene *& p) {
|
||||
p = new Scene();
|
||||
QByteArray ba;
|
||||
s >> ba;
|
||||
ba = qUncompress(ba);
|
||||
ChunkStream cs(ba);
|
||||
QVector<short> geom_ind, mat_ind;
|
||||
while (!cs.atEnd()) {
|
||||
switch (cs.read()) {
|
||||
case 1: cs.get(p->name_); break;
|
||||
case 10: cs.get(p->geometries); break;
|
||||
case 11: cs.get(p->materials); break;
|
||||
case 20:
|
||||
cs.get(p->root_);
|
||||
p->root_->setScene(p);
|
||||
break;
|
||||
case 21: cs.get(geom_ind); break;
|
||||
case 22: cs.get(mat_ind); break;
|
||||
}
|
||||
}
|
||||
p->makeMaterialsUniqueNames();
|
||||
ObjectBaseList cl = p->root_->children(true);
|
||||
int cnt = qMin(qMin(cl.size(), geom_ind.size()), mat_ind.size());
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
ObjectBase * c(cl[i]);
|
||||
if (geom_ind[i] >= 0) c->mesh_ = p->geometries[geom_ind[i]];
|
||||
if (mat_ind[i] >= 0) c->material_ = p->materials[mat_ind[i]];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
144
src/core/scene/glscene.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
QGL Scene
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GLSCENE_H
|
||||
#define GLSCENE_H
|
||||
|
||||
#include "gltypes.h"
|
||||
|
||||
|
||||
class QGLENGINE_CORE_EXPORT Scene: public QObject {
|
||||
Q_OBJECT
|
||||
friend class RendererBase;
|
||||
friend class Renderer;
|
||||
friend class RendererMaterial;
|
||||
friend class RendererService;
|
||||
friend class RendererSelection;
|
||||
friend class ObjectBase;
|
||||
friend class Light;
|
||||
friend QGLENGINE_CORE_EXPORT QDataStream & operator<<(QDataStream & s, const Scene * p);
|
||||
friend QGLENGINE_CORE_EXPORT 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();
|
||||
void reinitAll();
|
||||
|
||||
int objectsCount(bool all = false);
|
||||
ObjectBaseList objects(bool all = false);
|
||||
ObjectBase * rootObject() { return root_; }
|
||||
void removeObject(ObjectBase * o, bool inChildren = true);
|
||||
void removeObject(ObjectBase & o, bool inChildren = true);
|
||||
void clearObjects(bool deleteAll = false);
|
||||
|
||||
SelectionMode selectionMode() const { return sel_mode_; }
|
||||
void setSelectionMode(SelectionMode mode) { sel_mode_ = mode; }
|
||||
void selectObject(ObjectBase * o, bool add_to_selection = false);
|
||||
void selectObjects(ObjectBaseList ol, bool add_to_selection = false);
|
||||
void selectObjectsByMesh();
|
||||
void selectObjectsByMaterial();
|
||||
void clearSelection();
|
||||
ObjectBaseList selectedObjects(bool top_only = false) const;
|
||||
ObjectBase * selectedObject() const;
|
||||
void cleanUnused();
|
||||
|
||||
const Box3D & boundingBox() const;
|
||||
|
||||
QVector<Material *> getMaterials() const { return materials; }
|
||||
Material * newMaterial(const QString & name = QString());
|
||||
void removeMaterial(Material * m);
|
||||
void makeMaterialsUniqueNames();
|
||||
|
||||
void removeLight(Light * l);
|
||||
|
||||
void dump();
|
||||
void destroy(QOpenGLExtraFunctions * f);
|
||||
void destroyUnused(QOpenGLExtraFunctions * f);
|
||||
|
||||
protected:
|
||||
void prepareTree(ObjectBase * o);
|
||||
void gatherSelection();
|
||||
void objectsCountInternal(int * cnt, ObjectBase * where);
|
||||
void removeObjectInternal(ObjectBase * o, ObjectBase * where);
|
||||
void emitSelectionChanged();
|
||||
QString uniqueName(QString n, const QSet<QString> & names);
|
||||
|
||||
void attachObject(ObjectBase * o);
|
||||
Mesh * attachMesh(Mesh * mesh);
|
||||
void setTreeChanged();
|
||||
void setTreeStructChanged();
|
||||
void setMaterialsChanged() { mat_changed = true; }
|
||||
void setLightsChanged() { lights_changed = tree_changed = true; }
|
||||
void setObjectMeshChanged(ObjectBase * o);
|
||||
|
||||
|
||||
QString name_;
|
||||
ObjectBase * root_;
|
||||
bool tree_changed, mat_changed, lights_changed, destroying;
|
||||
bool need_reload_materials, tree_struct_changed;
|
||||
QVector<bool> mat_map_changed;
|
||||
|
||||
QVector<Mesh *> geometries, td_geometries;
|
||||
QVector<Material *> materials;
|
||||
|
||||
QMap<int, QMap<Mesh *, ObjectBaseList>> geometries_used;
|
||||
QMap<int, QList<Light *>> lights_used;
|
||||
QList<Camera *> cameras_used;
|
||||
QVector<Material *> changed_materials;
|
||||
|
||||
SelectionMode sel_mode_;
|
||||
ObjectBaseList selected_, selected_top;
|
||||
|
||||
protected slots:
|
||||
|
||||
signals:
|
||||
void __objectDeleted(ObjectBase * o);
|
||||
void __destroyed();
|
||||
void treeChanged();
|
||||
void selectionChanged();
|
||||
};
|
||||
|
||||
|
||||
QGLENGINE_CORE_EXPORT QDataStream & operator<<(QDataStream & s, const Scene * p);
|
||||
QGLENGINE_CORE_EXPORT QDataStream & operator>>(QDataStream & s, Scene *& p);
|
||||
|
||||
#endif // GLSCENE_H
|
||||
19
src/core/shaders.qrc
Normal file
@@ -0,0 +1,19 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>../../shaders/ds_final.glsl</file>
|
||||
<file>../../shaders/ds_geom.glsl</file>
|
||||
<file>../../shaders/ds_light.glsl</file>
|
||||
<file>../../shaders/ds_tonemap.glsl</file>
|
||||
<file>../../shaders/fxaa.frag</file>
|
||||
<file>../../shaders/fxaa.vert</file>
|
||||
<file>../../shaders/fxaa_v3.h</file>
|
||||
<file>../../shaders/selection.glsl</file>
|
||||
<file>../../shaders/selection_apply.glsl</file>
|
||||
<file>../../shaders/selection_frame.glsl</file>
|
||||
<file>../../shaders/selection_halo.glsl</file>
|
||||
<file>../../shaders/service_fill.glsl</file>
|
||||
<file>../../shaders/service_frame.glsl</file>
|
||||
<file>../../shaders/service_line.glsl</file>
|
||||
<file>../../shaders/sum.glsl</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
BIN
src/icons/add-type-camera.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/icons/add-type-empty.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/icons/add-type-geo.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/icons/add-type-light.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/icons/alpha.png
Normal file
|
After Width: | Height: | Size: 158 B |
BIN
src/icons/application-exit.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
src/icons/collapse.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
src/icons/configure.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
src/icons/dialog-cancel.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
src/icons/dialog-close.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
src/icons/document-edit.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
src/icons/document-import.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/icons/document-new.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
src/icons/document-open.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
src/icons/document-save-all.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
src/icons/document-save.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
src/icons/edit-clear-locationbar-rtl.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
src/icons/edit-clear.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
src/icons/edit-copy.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/icons/edit-delete.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
src/icons/edit-find.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
src/icons/edit-paste.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/icons/edit-rename.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
src/icons/expand.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/icons/format-fill-color.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
src/icons/go-jump.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
src/icons/go-top.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
src/icons/group.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/icons/layer-visible-off.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
src/icons/layer-visible-on.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
src/icons/light-+.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/icons/list-add.png
Normal file
|
After Width: | Height: | Size: 34 KiB |