diff --git a/qglengine/CMakeLists.txt b/qglengine/CMakeLists.txt new file mode 100644 index 0000000..f161de0 --- /dev/null +++ b/qglengine/CMakeLists.txt @@ -0,0 +1,65 @@ +project(qglengine) +cmake_minimum_required(VERSION 2.6) +find_package(QAD REQUIRED) +if (NOT Qt5) + message(WARNING "Building ${PROJECT_NAME} available only on Qt5!") + return() +endif() +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +if (IBPROJECT) + include(SDKMacros) +else() + option(LIB "System install" 0) + option(DEBUG "Build with -g3" 0) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -Wall") + if (DEBUG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + endif() +endif() +if (MINGW) + find_package(MinGW REQUIRED) +endif() +find_package(OpenGL REQUIRED) +include_directories(${QAD_INCLUDES} {CMAKE_CURRENT_SOURCE_DIR}) +find_qt(Qt5 Core Gui OpenGL Xml) +qt_sources(SRC) +qt_sources(FSRC DIR "formats") +list(APPEND SRC ${FSRC}) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +qt_add_library(qglengine_core SHARED out_CPP) +qt_target_link_libraries(qglengine_core qad_utils qad_widgets assimp ${OPENGL_LIBRARIES}) +message(STATUS "Building qglengine_core") +list(APPEND QT_MULTILIB_LIST qglengine_core) +add_subdirectory(widgets) +set(QT_MULTILIB_LIST ${QT_MULTILIB_LIST} PARENT_SCOPE) +if (LIBPROJECT) + sdk_install("qglengine" "qglengine_core" "${out_HDR}" "${out_QM}") +else() + if (LIB) + if (WIN32) + qt_install(FILES ${out_HDR} DESTINATION ${MINGW_INCLUDE}/qglengine) + qt_install(TARGETS qglengine_core DESTINATION ${MINGW_LIB}) + qt_install(TARGETS qglengine_core DESTINATION ${MINGW_BIN}) + qt_install(TARGETS qglengine_core DESTINATION QtBin) + else() + qt_install(FILES ${out_HDR} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/qglengine) + qt_install(TARGETS qglengine_core DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() + message(STATUS "Install qglengine_core to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + qt_install(TARGETS qglengine_core DESTINATION bin) + message(STATUS "Install qglengine_core to local \"bin\"") + endif() +endif() +if (NOT DEFINED ANDROID_PLATFORM) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/plugin") + #add_subdirectory(plugin) + endif() +endif() + +qt_sources(test_SRC DIR "qglview_test") +qt_wrap(${test_SRC} CPPS test_CPP) +qt_add_executable(qglview_test test_CPP) +qt_target_link_libraries(qglview_test qglengine_core qglengine_widgets) diff --git a/qglengine/formats/loader_assimp.cpp b/qglengine/formats/loader_assimp.cpp new file mode 100644 index 0000000..e43ae31 --- /dev/null +++ b/qglengine/formats/loader_assimp.cpp @@ -0,0 +1,133 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "loader_assimp.h" +#include "glscene.h" +#include "glmesh.h" +#include "glmaterial.h" +#include "globject.h" +#include +#include +#include +#include +#include + +QVector3D fromAiVector3D(const aiVector3D & v) {return QVector3D(v.x, v.y, v.z);} + Vector3i fromAiFace (const aiFace & v) {return Vector3i(v.mIndices[0], v.mIndices[1], v.mIndices[2]);} +QMatrix4x4 fromAiMatrix4D(const aiMatrix4x4 & v) {return QMatrix4x4(v.a1, v.a2, v.a3, v.a4, + v.b1, v.b2, v.b3, v.b4, + v.c1, v.c2, v.c3, v.c4, + v.d1, v.d2, v.d3, v.d4);} +bool isAiMeshTriangles(const aiMesh * m) {return (m->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) == aiPrimitiveType_TRIANGLE;} + + +Mesh * assimpMesh(const aiMesh * m) { + if (!m) return 0; + if (!isAiMeshTriangles(m)) return 0; + Mesh * ret = new Mesh(); + int vcnt = m->mNumVertices, tcnt = m->mNumFaces; + + QVector & v(ret->vertices()); v.resize(vcnt); + QVector & t(ret->texcoords()); t.resize(vcnt); + QVector< Vector3i> & ind(ret->indices()); + + for (int i = 0; i < vcnt; ++i) + v[i] = fromAiVector3D(m->mVertices[i]); + + if (m->HasNormals()) { + QVector & n(ret->normals()); + n.resize(vcnt); + for (int i = 0; i < vcnt; ++i) + n[i] = fromAiVector3D(m->mNormals[i]); + } + + if (m->HasTextureCoords(0)) { + for (int i = 0; i < vcnt; ++i) + t[i] = fromAiVector3D(m->mTextureCoords[0][i]).toVector2D(); + } + + if (m->HasFaces()) { + ind.resize(tcnt); + for (int i = 0; i < tcnt; ++i) + ind[i] = fromAiFace(m->mFaces[i]); + } else { + tcnt = vcnt / 3; + ind.resize(tcnt); + int si = 0; + for (int i = 0; i < tcnt; ++i) { + si = i+i+i; + ind[i] = Vector3i(si, si+1, si+2); + } + } + + //qDebug() << "add mesh" << v.size() << ret->normals().size() << ret->texcoords().size() << ret->indices().size(); + + return ret; +} + + +ObjectBase * assimpObject(const aiNode * n, const QVector & meshes) { + if (!n) return 0; + ObjectBase * ret = new ObjectBase(); + ret->setName(n->mName.C_Str()); + ret->setTransform(fromAiMatrix4D(n->mTransformation)); + //qDebug() << "add object" << ret << ret->name(); + for (uint i = 0; i < n->mNumMeshes; ++i) { + int mi = n->mMeshes[i]; + if (meshes[mi]) { + ret->setMesh(meshes[mi]); + //qDebug() << "set mesh" << mi << ret->mesh(); + break; + } + } + for (uint i = 0; i < n->mNumChildren; ++i) { + ObjectBase * co = assimpObject(n->mChildren[i], meshes); + if (co) ret->addChild(co); + } + + return ret; +} + + +Scene * loadScene(const QString & filepath) { + if (filepath.isEmpty()) return 0; + qDebug() << "[Loader Assimp] Import" << filepath << "..."; + Assimp::Importer importer; + const aiScene * ais = importer.ReadFile(filepath.toUtf8(), aiProcess_Triangulate | + aiProcess_SortByPType | + aiProcess_GenUVCoords | + aiProcess_TransformUVCoords); + if (!ais) { + qDebug() << "[Loader Assimp] Error: \"" + QString(importer.GetErrorString()) + "\""; + return 0; + } + qDebug() << "[Loader Assimp] Imported" << ais->mNumMeshes << "meshes"; + QVector meshes; + for (uint i = 0; i < ais->mNumMeshes; ++i) + meshes << assimpMesh(ais->mMeshes[i]); + + ObjectBase * root = assimpObject(ais->mRootNode, meshes); + if (!root) return 0; + + Scene * scene = new Scene(); + scene->setName(root->name()); + foreach (ObjectBase * o, root->children()) + scene->addObject(o); + + return scene; +} diff --git a/qglengine/formats/loader_assimp.h b/qglengine/formats/loader_assimp.h new file mode 100644 index 0000000..43070dc --- /dev/null +++ b/qglengine/formats/loader_assimp.h @@ -0,0 +1,26 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef LOADER_ASSIMP_H +#define LOADER_ASSIMP_H + +#include "gltypes.h" + +Scene * loadScene(const QString & filepath); + +#endif // LOADER_ASSIMP_H diff --git a/qglengine/formats/loader_qgl.cpp b/qglengine/formats/loader_qgl.cpp new file mode 100644 index 0000000..5804c21 --- /dev/null +++ b/qglengine/formats/loader_qgl.cpp @@ -0,0 +1,67 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "loader_qgl.h" +#include "glscene.h" +#include + + +Scene * loadFromQGLFile(const QString & filepath) { + QFile f(filepath); + if (!f.exists()) { + qDebug() << "[Loader QGL] Error: can`t open \"" + filepath + "\""; + return 0; + } + f.open(QIODevice::ReadOnly); + QDataStream s(&f); + s.setVersion(QDataStream::Qt_5_0); + char sign[4]; + s.readRawData(sign, 4); + if ((sign[0] != 'Q') || (sign[1] != 'G') || (sign[2] != 'L') || (sign[3] != 'E')) { + qDebug() << "[Loader QGL] Error: \"" + filepath + "\" is not valid QGLEngine file!"; + return 0; + } + ushort version = 0x1; + f.peek((char*)&version, 2); + if (version != 1) { + qDebug() << "[Loader QGL] Error: \"" + filepath + "\" unsupported version!"; + return 0; + } + Scene * ret = 0; + s.skipRawData(2); + s >> ret; + //root->buildTransform(); + qDebug() << "[Loader QGL] Loaded" << ret->objectsCount(true) << "objects from" << filepath; + return ret; +} + + +bool saveToQGLFile(const QString & filepath, const Scene * scene) { + QFile f(filepath); + if (!f.open(QIODevice::ReadWrite)) + return false; + f.resize(0); + QDataStream s(&f); + s.setVersion(QDataStream::Qt_5_0); + char sign[4] = {'Q', 'G', 'L', 'E'}; + ushort version = 0x1; + s.writeRawData(sign, 4); + s.writeRawData((char*)&version, 2); + s << scene; + return true; +} diff --git a/qglengine/formats/loader_qgl.h b/qglengine/formats/loader_qgl.h new file mode 100644 index 0000000..3d379f8 --- /dev/null +++ b/qglengine/formats/loader_qgl.h @@ -0,0 +1,28 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef LOADER_QGL_H +#define LOADER_QGL_H + +#include "gltypes.h" +#include + +Scene * loadFromQGLFile(const QString & filepath); +bool saveToQGLFile(const QString & filepath, const Scene * scene); + +#endif // LOADER_QGL_H diff --git a/qglengine/glbuffer.cpp b/qglengine/glbuffer.cpp new file mode 100644 index 0000000..9f17793 --- /dev/null +++ b/qglengine/glbuffer.cpp @@ -0,0 +1,87 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "glbuffer.h" + + +Buffer::Buffer(GLenum target, GLenum _usage) { + target_ = target; + usage_ = _usage; + buffer_ = 0; + prev_size = 0; +} + + +Buffer::~Buffer() { +} + + +void Buffer::init(QOpenGLExtraFunctions * f) { + if (!isInit()) { + f->glGenBuffers(1, &buffer_); + } +} + + +void Buffer::destroy(QOpenGLExtraFunctions * f) { + if (buffer_ != 0) { + f->glDeleteBuffers(1, &buffer_); + } + buffer_ = 0; +} + + +void Buffer::bind(QOpenGLExtraFunctions * f) { + f->glBindBuffer(target_, buffer_); +} + + +void Buffer::release(QOpenGLExtraFunctions * f) { + f->glBindBuffer(target_, 0); +} + + +void * Buffer::map(QOpenGLExtraFunctions * f, GLbitfield mode, int size) { + if (size < 0) size = prev_size; + return f->glMapBufferRange(target_, 0, size, mode); +} + + +void Buffer::unmap(QOpenGLExtraFunctions * f) { + f->glUnmapBuffer(target_); +} + + +bool Buffer::resize(QOpenGLExtraFunctions * f, int new_size) { + if (new_size <= 0) return false; + //qDebug() << "check resize buffer" << buffer_ << "bytes" << new_size << ", old =" << prev_size; + if (new_size <= prev_size) return false; + prev_size = new_size; + //qDebug() << "resize buffer " << buffer_ << target_ << "for" << new_size << "bytes"; + f->glBufferData(target_, new_size, 0, usage_); + return true; +} + + +void Buffer::load(QOpenGLExtraFunctions * f, const void * data, int size, int offset) { + if (!data || size <= 0) return; + //qDebug() << "load buffer" << buffer_ << "bytes" << size; + f->glBufferSubData(target_, offset, size, data); +} diff --git a/qglengine/glbuffer.h b/qglengine/glbuffer.h new file mode 100644 index 0000000..381c021 --- /dev/null +++ b/qglengine/glbuffer.h @@ -0,0 +1,56 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLBUFFER_H +#define GLBUFFER_H + +#include "gltypes.h" + + +class Buffer +{ + friend class ObjectBase; +public: + Buffer(GLenum target, GLenum usage = GL_DYNAMIC_DRAW); + ~Buffer(); + + void init (QOpenGLExtraFunctions * f); + void destroy (QOpenGLExtraFunctions * f); + + void bind (QOpenGLExtraFunctions * f); + void release (QOpenGLExtraFunctions * f); + void * map (QOpenGLExtraFunctions * f, GLbitfield mode, int size = -1); + void unmap (QOpenGLExtraFunctions * f); + + // returns true if size changed + bool resize (QOpenGLExtraFunctions * f, int new_size); + void load (QOpenGLExtraFunctions * f, const void * data, int size, int offset = 0); + + GLuint ID() const {return buffer_;} + GLenum usage() const {return usage_;} + bool isInit() const {return buffer_ != 0;} + +private: + GLenum target_, usage_; + GLuint buffer_; + int prev_size; + +}; + + +#endif // GLBUFFER_H diff --git a/qglengine/glcamera.cpp b/qglengine/glcamera.cpp new file mode 100644 index 0000000..7c7f6cb --- /dev/null +++ b/qglengine/glcamera.cpp @@ -0,0 +1,313 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "gltypes.h" +#include "qglview.h" + + +Camera::Camera() { + type_ = glCamera; + fov_ = 60.; + angle_limit_lower_xy = 0.f; + angle_limit_upper_xy = 360.f; + angles_.setY(270.f); + depth_start = 0.1f; + depth_end = 1000.f; + mirror_x = mirror_y = false; +} + + +void Camera::anglesFromPoints() { + QVector3D dv = aim_ - pos_, tv; + tv = QVector3D(dv.x(), dv.y(), 0.); + angles_.setZ(atan2f(tv.x(), tv.y()) * rad2deg); + angles_.setY(piClamp(atan2f(tv.length(), dv.z()) * rad2deg, angle_limit_lower_xy, angle_limit_upper_xy) + 180.f); +} + + +void Camera::apply(const GLfloat & aspect) { + glMatrixMode(GL_PROJECTION); + if (aspect <= 1.f) + glScalef(aspect, aspect, 1.f); + QMatrix4x4 pm = glMatrixPerspective(fov_, aspect, depth_start, depth_end); + //pm.perspective(fov_, aspect, depth_start, depth_end); + //qDebug() << pm;// << glMatrixPerspective(fov_, aspect, depth_start, depth_end); + //qDebug() << pm; + //setGLMatrix(pm); + glMatrixMode(GL_MODELVIEW); + pm.setToIdentity(); + pm.translate(0., 0., -distance()); + pm.rotate(angles_.y(), 1., 0., 0.); + pm.rotate(angles_.x(), 0., 1., 0.); + pm.rotate(angles_.z(), 0., 0., 1.); + //pm.translate(-aim_); + if (parent_) { + QMatrix4x4 pmat = parent_->worldTransform(); + offset_ = pmat.column(3).toVector3D(); + pmat(0, 3) = pmat(1, 3) = pmat(2, 3) = 0.; + pmat.translate(aim_); + pm *= pmat.inverted(); + //qDebug() << pmat; + } + //setGLMatrix(pm); + //qDebug() << angles_; +} + + +QMatrix4x4 Camera::offsetMatrix() const { + QMatrix4x4 ret; + ret.translate(parent_ ? -offset_ : -aim_); + return ret; +} + +/* +void Camera::localTransform(QMatrix4x4 & m) { + return; + if (parent_) + m *= parent_->worldTransform(); + QMatrix4x4 ret; + //qDebug() << "local camera"; + ret.translate(0., 0., -distance()); + ret.rotate(angles_.y(), 1., 0., 0.); + ret.rotate(angles_.x(), 0., 1., 0.); + ret.rotate(angles_.z(), 0., 0., 1.); + //m *= ret.inverted(); +} +*/ + +void Camera::assign(const Camera & c) { + pos_ = c.pos_; + aim_ = c.aim_; + fov_ = c.fov_; + angles_ = c.angles_; + angle_limit_lower_xy = c.angle_limit_lower_xy; + angle_limit_upper_xy = c.angle_limit_upper_xy; + mirror_x = c.mirror_x; + mirror_y = c.mirror_y; + depth_start = c.depth_start; + depth_end = c.depth_end; + buildTransform(); +} + + +ObjectBase * Camera::clone(bool withChildren) { + Camera * o = new Camera(*this); + //GLObjectBase::clone(withChildren); + o->is_init = false; + o->name_ = name_;// + "_copy"; + o->scene_ = nullptr; + o->children_.clear(); + if (withChildren) { + for (int i = 0; i < children_.size(); ++i) + o->addChild(children_[i]->clone(withChildren)); + } + o->pos_ = pos_; + o->aim_ = aim_; + o->fov_ = fov_; + o->angles_ = angles_; + o->angle_limit_lower_xy = angle_limit_lower_xy; + o->angle_limit_upper_xy = angle_limit_upper_xy; + o->mirror_x = mirror_x; + o->mirror_y = mirror_y; + o->depth_start = depth_start; + o->depth_end = depth_end; + o->meta = meta; + return o; +} + + +QMatrix4x4 Camera::viewMatrix() const { + QMatrix4x4 ret; + ret.translate(0., 0., -distance()); + ret.rotate(angles_.y(), 1., 0., 0.); + ret.rotate(angles_.x(), 0., 1., 0.); + ret.rotate(angles_.z(), 0., 0., 1.); + //pm.translate(-aim_); + if (parent_) { + QMatrix4x4 pmat = parent_->worldTransform(); + offset_ = pmat.column(3).toVector3D(); + pmat(0, 3) = pmat(1, 3) = pmat(2, 3) = 0.; + pmat.translate(aim_); + ret *= pmat.inverted(); + } + return ret; +} + + +QMatrix4x4 Camera::projectionMatrix(double aspect) const { + return glMatrixPerspective(fov_, aspect, depth_start, depth_end); +} + + +void Camera::panZ(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = QVector2D(dv.x(), dv.y()).length(); + angles_.setZ(angles_.z() + a); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tl, cosf(angles_.z() * deg2rad) * tl, dv.z()); + aim_ = pos_ + dv; + buildTransform(); +} + + +void Camera::panXY(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = dv.length(), tc; + angles_.setY(angles_.y() + a); + angles_.setY(piClamp(angles_.y(), angle_limit_lower_xy, angle_limit_upper_xy)); + tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + aim_ = pos_ + dv * tl; + buildTransform(); +} + + +void Camera::rotateZ(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = QVector2D(dv.x(), dv.y()).length(); + angles_.setZ(angles_.z() + a); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tl, cosf(angles_.z() * deg2rad) * tl, dv.z()); + aim_ = pos_ + dv; + buildTransform(); +} + + +void Camera::rotateXY(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = dv.length(), tc; + angles_.setY(angles_.y() + a); + angles_.setY(piClamp(angles_.y(), angle_limit_lower_xy, angle_limit_upper_xy)); + tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + aim_ = pos_ + dv * tl; + buildTransform(); +} + + +void Camera::orbitZ(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = QVector2D(dv.x(), dv.y()).length(); + angles_.setZ(angles_.z() + a); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tl, cosf(angles_.z() * deg2rad) * tl, dv.z()); + pos_ = aim_ - dv; + buildTransform(); +} + + +void Camera::orbitXY(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = dv.length(), tc; + angles_.setY(angles_.y() + a); + angles_.setY(piClamp(angles_.y(), angle_limit_lower_xy, angle_limit_upper_xy)); + tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + pos_ = aim_ - dv * tl; + buildTransform(); +} + + +void Camera::setAngleZ(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = QVector2D(dv.x(), dv.y()).length(); + angles_.setZ(a); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tl, cosf(angles_.z() * deg2rad) * tl, dv.z()); + aim_ = pos_ + dv; + buildTransform(); +} + + +void Camera::setAngleXY(const float & a) { + QVector3D dv = aim_ - pos_; + float tl = dv.length(), tc; + angles_.setY(a); + tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + //pos_ = aim_ - dv; + aim_ = pos_ + dv * tl; + buildTransform(); + //anglesFromPoints(); +} + + +void Camera::moveForward(const float & x, bool withZ) { + QVector3D dv;// = aim_ - pos_; + float tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, 0.); + if (withZ) dv.setZ(-cosf(angles_.y() * deg2rad)); + dv.normalize(); + dv *= x; + pos_ += dv; + aim_ += dv; + buildTransform(); +} + + +void Camera::moveLeft(const float & x, bool withZ) { + QVector3D dv;// = aim_ - pos_; + float tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad - float(M_PI_2)) * tc, cosf(angles_.z() * deg2rad - float(M_PI_2)) * tc, 0.f); + if (withZ) dv.setZ(-sinf(angles_.x() * deg2rad)); + dv.normalize(); + dv *= x; + pos_ += dv; + aim_ += dv; + buildTransform(); +} + + +void Camera::moveUp(const float & x, bool onlyZ) { + QVector3D dv; + if (onlyZ) + dv = QVector3D(0., 0., x); + else { + float tc = cosf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -sinf(angles_.y() * deg2rad)); + dv.normalize(); + dv *= x; + } + pos_ += dv; + aim_ += dv; + buildTransform(); +} + + +void Camera::flyCloser(const float & s) { + QVector3D dv = aim_ - pos_; + float tl = dv.length() / (1.f + s), tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + pos_ = aim_ - dv * tl; + buildTransform(); +} + + +void Camera::flyFarer(const float & s) { + QVector3D dv = aim_ - pos_; + float tl = dv.length() * (1.f + s), tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + pos_ = aim_ - dv * tl; + buildTransform(); +} + + +void Camera::flyToDistance(const float & d) { + QVector3D dv = aim_ - pos_; + float tc = -sinf(angles_.y() * deg2rad); + dv = QVector3D(sinf(angles_.z() * deg2rad) * tc, cosf(angles_.z() * deg2rad) * tc, -cosf(angles_.y() * deg2rad)); + pos_ = aim_ - dv * d; + buildTransform(); +} + diff --git a/qglengine/glcamera.h b/qglengine/glcamera.h new file mode 100644 index 0000000..ea11a0b --- /dev/null +++ b/qglengine/glcamera.h @@ -0,0 +1,107 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLCAMERA_H +#define GLCAMERA_H + +#include "globject.h" + +//extern QMatrix4x4 globCameraMatrix; +//extern Camera * currentCamera; + +class Camera: public ObjectBase +{ + friend class QGLView; + friend class GLParticlesSystem; + friend QDataStream & operator <<(QDataStream & s, const ObjectBase * p); + friend QDataStream & operator >>(QDataStream & s, ObjectBase *& p); +public: + Camera(); + + void setPos(const QVector3D & p) {pos_ = p; anglesFromPoints(); buildTransform();} + void setAim(const QVector3D & p) {aim_ = p; anglesFromPoints(); buildTransform();} + void move(const QVector3D & p) {pos_ += p; aim_ += p; buildTransform();} + void move(const float & x, const float & y = 0., const float & z = 0.) {pos_ += QVector3D(x, y, z); aim_ += QVector3D(x, y, z); buildTransform();} + 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 rotateZ(const float & a); + void rotateXY(const float & a); + void rotateRoll(const float & a) {angles_.setX(angles_.x() + a); buildTransform();} + void orbitZ(const float & a); + void orbitXY(const float & a); + void panZ(const float & a); + void panXY(const float & a); + void setFOV(const float & f) {fov_ = f;} + void setAngles(const QVector3D & a) {setRotation(a);} + void setAngleZ(const float & a); + void setAngleXY(const float & a); + void setAngleRoll(const float & a) {angles_.setX(a); buildTransform();} + void setAngleLowerLimitXY(const float & a) {angle_limit_lower_xy = a; buildTransform();} + void setAngleUpperLimitXY(const float & a) {angle_limit_upper_xy = a; buildTransform();} + void setAngleLimitsXY(const float & lower, const float & upper) {angle_limit_lower_xy = lower; angle_limit_upper_xy = upper; buildTransform();} + void setDepthStart(const float & d) {depth_start = d;} + void setDepthEnd(const float & d) {depth_end = d;} + void setMirrorX(bool yes) {mirror_x = yes;} + void setMirrorY(bool yes) {mirror_y = yes;} + void flyCloser(const float & s); + void flyFarer(const float & s); + void flyToDistance(const float & d); + + QVector3D aim() const {return aim_;} + QVector3D angles() const {return rotation();} + QVector3D direction() const {return (aim_ - pos_).normalized();} + QVector3D directionXY() const {QVector3D tv = aim_ - pos_; return QVector3D(tv.x(), tv.y(), 0.).normalized();} + float FOV() const {return fov_;} + float distance() const {return (pos_ - aim_).length();} + float angleZ() const {return angles_.z();} + float angleXY() const {return angles_.y();} + float angleRoll() const {return angles_.x();} + float angleLowerLimitXY() const {return angle_limit_lower_xy;} + float angleUpperLimitXY() const {return angle_limit_upper_xy;} + float depthStart() const {return depth_start;} + float depthEnd() const {return depth_end;} + bool isMirrorX() const {return mirror_x;} + bool isMirrorY() const {return mirror_y;} + void anglesFromPoints(); + void apply(const GLfloat & aspect = 1.); + void assign(const Camera & c); + + + virtual ObjectBase * clone(bool withChildren = true); + QMatrix4x4 viewMatrix() const; + QMatrix4x4 projectionMatrix(double aspect) const; + QMatrix4x4 offsetMatrix() const; + +private: + QVector3D aim_; + mutable QVector3D offset_; + GLfloat fov_; + GLfloat depth_start; + GLfloat depth_end; + GLfloat angle_limit_lower_xy; + GLfloat angle_limit_upper_xy; + bool mirror_x; + bool mirror_y; + +}; + +#endif // GLCAMERA_H diff --git a/qglengine/glframebuffer.cpp b/qglengine/glframebuffer.cpp new file mode 100644 index 0000000..7bd3142 --- /dev/null +++ b/qglengine/glframebuffer.cpp @@ -0,0 +1,295 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include "glframebuffer.h" +#include + + +Framebuffer::Framebuffer(QOpenGLExtraFunctions * f_, int colorAttachments_, bool withDepth, GLenum colorFormat_, GLenum _target): f(f_), + pbo(GL_PIXEL_PACK_BUFFER, GL_STREAM_DRAW) { + is_depth = withDepth; + color_format = colorFormat_; + target_ = _target; + colors.fill(0, colorAttachments_); + fbo = drbo = 0; + tex_d = 0; + wid = hei = 0; + pbo_queried = 0; + is_changed = false; +} + + +Framebuffer::~Framebuffer() { + deleteGLFramebuffer(fbo); + deleteGLRenderbuffer(drbo); + for (int i = 0; i < colors.size(); ++i) + deleteGLTexture(f, colors[i]); + deleteGLTexture(f, tex_d); +} + + +void Framebuffer::resize(int width, int height, bool force) { + if ((wid == width) && (hei == height) && !force) return; + wid = width; + hei = height; + deleteGLFramebuffer(fbo); + f->glGenFramebuffers(1, &fbo); + f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); + for (int i = 0; i < colors.size(); ++i) { + deleteGLTexture(f, colors[i]); + createGLTexture(f, colors[i], width, height, color_format, target_); + f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + f->glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, 4); + //f->glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); + f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target_, colors[i], 0); + } + if (is_depth) { + deleteGLTexture(f, tex_d); + deleteGLRenderbuffer(drbo); + f->glGenRenderbuffers(1, &drbo); + f->glBindRenderbuffer(GL_RENDERBUFFER, drbo); + f->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + f->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, drbo); + createGLTexture(f, tex_d, width, height, GL_DEPTH_COMPONENT); + f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + //f->glTexParameteri(target_, GL_GENERATE_MIPMAP_SGIS, GL_FALSE); + f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, target_, tex_d, 0); + } + f->glBindFramebuffer(GL_FRAMEBUFFER, 0); + if (pbo.isInit()) { + pbo.bind(f); + pbo.resize(f, width*height*4); + pbo.release(f); + } + is_changed = false; +} + + +QImage Framebuffer::grab() const { + //glReadPixels(0, 0, wid, hei, GL_RGBA, ); + //QImage ret(); + return QImage(); +} + + +void Framebuffer::queryPoint(int index, QPoint p) { + pbo_queried = 0; + if (index < 0 || index >= colors.size()) return; + if (!rect().contains(p) || !pbo.isInit()) return; + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index); + //QTime tm; tm.restart(); + pbo.bind(f); + f->glReadPixels(p.x(), height() - p.y(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0); + pbo_queried = 1; + pbo.release(f); + //qDebug() << tm.elapsed(); +} + + +void Framebuffer::queryPoints(int index, QRect rect_) { + pbo_queried = 0; + if (index < 0 || index >= colors.size()) return; + rect_ &= rect(); + if (rect_.isEmpty() || !pbo.isInit()) return; + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index); + //QTime tm; tm.restart(); + pbo.bind(f); + f->glReadPixels(rect_.x(), height() - rect_.bottom(), rect_.width(), rect_.height(), GL_RGBA, GL_UNSIGNED_BYTE, 0); + pbo_queried = rect_.width() * rect_.height(); + pbo.release(f); + //qDebug() << tm.elapsed(); +} + + +void Framebuffer::queryImage(int index) { + queryPoints(index, rect()); +} + + +uint Framebuffer::getPoint() const { + if (!pbo.isInit() || (pbo_queried == 0)) return 0; + //QTime tm; tm.restart(); + uint ret = 0; + pbo.bind(f); + //glClearError(); + void * map = pbo.map(f, GL_MAP_READ_BIT, sizeof(uint)); + //qDebug() << map << QString::number(glGetError(), 16); + if (map) + memcpy(&ret, map, sizeof(uint)); + pbo.unmap(f); + pbo.release(f); + //qDebug() << tm.elapsed(); + return ret; +} + + +QVector Framebuffer::getPoints() const { + QVector ret; + if (!pbo.isInit() || (pbo_queried == 0)) return ret; + ret.resize(pbo_queried); + //QTime tm; tm.restart(); + pbo.bind(f); + //glClearError(); + void * map = pbo.map(f, GL_MAP_READ_BIT, pbo_queried * sizeof(uint)); + //qDebug() << map << QString::number(glGetError(), 16); + if (map) + memcpy(ret.data(), map, pbo_queried * sizeof(uint)); + pbo.unmap(f); + pbo.release(f); + //qDebug() << tm.elapsed(); + return ret; +} + + +QImage Framebuffer::getImage() const { + QImage ret; + if (!pbo.isInit() || (pbo_queried == 0)) return ret; + ret = QImage(size(), QImage::Format_RGBA8888); + int bytes = width() * height() * 4; + //QTime tm; tm.restart(); + pbo.bind(f); + //glClearError(); + void * map = pbo.map(f, GL_MAP_READ_BIT, bytes); + //qDebug() << map << QString::number(glGetError(), 16); + if (map) + memcpy(ret.bits(), map, bytes); + pbo.unmap(f); + pbo.release(f); + //qDebug() << tm.elapsed(); + return ret; +} + + +QVector Framebuffer::grabF(int index) const { + QVector ret; + if (index < 0 || index >= colors.size()) return ret; + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index); + ret.resize(wid * hei * 4); + f->glReadPixels(0, 0, wid, hei, GL_RGBA, GL_FLOAT, ret.data()); + return ret; +} + + +void Framebuffer::blit(int index_from, GLuint fb_to, int index_to, QRect from, QRect to, GLbitfield mask, GLenum filter) { + if (index_from < 0 || index_from >= colors.size()) return; + GLenum e = GL_COLOR_ATTACHMENT0 + index_to; + f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_to); + f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + f->glReadBuffer(GL_COLOR_ATTACHMENT0 + index_from); + f->glDrawBuffers(1, &e); + f->glBlitFramebuffer(from.x(), from.y(), from.right(), from.bottom(), to.x(), to.y(), to.right(), to.bottom(), mask, filter); +} + + +void Framebuffer::bind() { + if (is_changed) resize(wid, hei); + if (fbo == 0) return; + //glFlush(); + f->glGetIntegerv(GL_VIEWPORT, prev_view); + //glClearError(); + f->glBindFramebuffer(GL_FRAMEBUFFER, fbo); + //qDebug() << QString::number(glGetError(), 16); + setWriteBuffers(); + f->glReadBuffer(GL_COLOR_ATTACHMENT0); + //f->glDrawBuffer(GL_COLOR_ATTACHMENT0); + f->glViewport(0, 0, wid, hei); +} + + +void Framebuffer::release() { + is_changed = false; + if (fbo == 0) return; + //glFlush(); + f->glBindFramebuffer(GL_FRAMEBUFFER, 0); + f->glViewport(prev_view[0], prev_view[1], prev_view[2], prev_view[3]); +} + + +void Framebuffer::setWriteBuffer(int index) { + //QVector buffers; buffers << GL_COLOR_ATTACHMENT0 + index; + GLenum e = GL_COLOR_ATTACHMENT0 + index; + f->glDrawBuffers(1, &e); +} + + +void Framebuffer::setWriteBuffers(int * indeces, int count) { + QVector buffers; + for (int i = 0; i < count; ++i) + buffers << GL_COLOR_ATTACHMENT0 + indeces[i]; + f->glDrawBuffers(buffers.size(), buffers.constData()); +} + + +void Framebuffer::setWriteBuffers() { + QVector buffers; + for (int i = 0; i < colors.size(); ++i) + buffers << GL_COLOR_ATTACHMENT0 + i; + f->glDrawBuffers(buffers.size(), buffers.constData()); +} + + +void Framebuffer::enablePixelBuffer() { + pbo.init(f); +} + + +void Framebuffer::bindColorTexture(int index, int channel) { + if (index < 0 || index >= colors.size()) return; + f->glActiveTexture(GL_TEXTURE0 + channel); + f->glBindTexture(GL_TEXTURE_2D, colors[index]); +} + + +void Framebuffer::bindColorTextures() { + for (int i = colors.size() - 1; i >= 0; --i) { + f->glActiveTexture(GL_TEXTURE0 + i); + f->glBindTexture(GL_TEXTURE_2D, colors[i]); + //f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + //f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } +} + + +void Framebuffer::bindDepthTexture(int channel) { + f->glActiveTexture(GL_TEXTURE0 + channel); + f->glBindTexture(GL_TEXTURE_2D, tex_d); +} + + +void Framebuffer::deleteGLRenderbuffer(GLuint & drbo) { + if (drbo != 0) + f->glDeleteRenderbuffers(1, &drbo); + drbo = 0; +} + + +void Framebuffer::deleteGLFramebuffer(GLuint & fbo) { + if (fbo != 0) + f->glDeleteFramebuffers(1, &fbo); + fbo = 0; +} diff --git a/qglengine/glframebuffer.h b/qglengine/glframebuffer.h new file mode 100644 index 0000000..d4704a3 --- /dev/null +++ b/qglengine/glframebuffer.h @@ -0,0 +1,81 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLFRAMEBUFFER_H +#define GLFRAMEBUFFER_H + +#include "glbuffer.h" + + +class Framebuffer +{ +public: + Framebuffer(QOpenGLExtraFunctions * f_, int colorAttachments = 1, bool withDepth = true, GLenum colorFormat = GL_RGBA8, GLenum _target = GL_TEXTURE_2D); + virtual ~Framebuffer(); + + GLuint id() const {return fbo;} + GLuint colorTexture(int index = 0) const {return colors[index];} + GLenum colorFormat() const {return color_format;} + GLuint depthTexture() const {return tex_d;} + GLenum target() const {return target_;} + int width() const {return wid;} + int height() const {return hei;} + QSize size() const {return QSize(wid, hei);} + QRect rect() const {return QRect(0, 0, wid, hei);} + QImage grab() const; + QVector grabF(int index) const; + void queryPoint(int index, QPoint p); + void queryPoints(int index, QRect rect); + void queryImage(int index); + uint getPoint() const; + QVector getPoints() const; + QImage getImage() const; + int queriedPoints() const {return pbo_queried;} + void blit(int index_from, GLuint fb_to, int index_to, QRect from, QRect to, GLbitfield mask = GL_COLOR_BUFFER_BIT, GLenum filter = GL_NEAREST ); + + void resize(int width, int height, bool force = false); + void bind(); + void release(); + void setReadBuffer(int index) {glReadBuffer(GL_COLOR_ATTACHMENT0 + index);} + void setWriteBuffer(int index); + void setWriteBuffers(int * indeces, int count); + void setWriteBuffers(); + void setColorFormat(GLenum format) {color_format = format; is_changed = true;} + void enablePixelBuffer(); + + void copyDepthFrom(GLuint tex) {;} + void bindColorTexture(int index, int channel = 0); + void bindColorTextures(); + void bindDepthTexture(int channel); + +private: + void deleteGLRenderbuffer(GLuint & drbo); + void deleteGLFramebuffer(GLuint & fbo); + + bool is_depth, is_changed; + int pbo_queried; + QOpenGLExtraFunctions * f; + mutable Buffer pbo; + QVector colors; + GLenum color_format, target_; + GLuint fbo, drbo, tex_d; + GLint prev_view[4], wid, hei; + +}; + +#endif // GLFRAMEBUFFER_H diff --git a/qglengine/glmaterial.cpp b/qglengine/glmaterial.cpp new file mode 100644 index 0000000..3c45fc3 --- /dev/null +++ b/qglengine/glmaterial.cpp @@ -0,0 +1,200 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "gltypes.h" +#include "gltexture_manager.h" +#include "qglview.h" + +using namespace QGLEngineShaders; + +/* +bool CubeTexture::create() { + //qDebug("create"); + destroy(); + glGenTextures(1, &id_); + glBindTexture(GL_TEXTURE_CUBE_MAP, id_); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + //glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); + //glClearError(); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, format_, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + //qDebug() << glGetError(); + changed_ = false; + return id_ > 0; +} + + +void CubeTexture::load() { + if (isEmpty()) return; + create(); + if (!path(0).isEmpty()) loadFront(path(0)); + if (!path(1).isEmpty()) loadBack(path(1)); + if (!path(2).isEmpty()) loadLeft(path(2)); + if (!path(3).isEmpty()) loadRight(path(3)); + if (!path(4).isEmpty()) loadTop(path(4)); + if (!path(5).isEmpty()) loadBottom(path(5)); +} + + +void CubeTexture::loadFromDirectory(const QString & dir) { + QDir d(dir); QFileInfoList sl; + sl = d.entryInfoList(QStringList("front.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadFront(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("back.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadBack(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("left.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadLeft(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("right.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadRight(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("top.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadTop(sl[0].absoluteFilePath()); + sl = d.entryInfoList(QStringList("bottom.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) loadBottom(sl[0].absoluteFilePath()); +} + + +void CubeTexture::loadPathesFromDirectory(const QString & dir) { + QDir d(dir); QFileInfoList sl; + sl = d.entryInfoList(QStringList("front.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[0] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("back.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[1] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("left.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[2] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("right.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[3] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("top.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[4] = sl[0].absoluteFilePath(); + sl = d.entryInfoList(QStringList("bottom.*"), QDir::Files | QDir::NoDotAndDotDot); if (!sl.isEmpty()) pathes[5] = sl[0].absoluteFilePath(); +} +*/ + + + +Map::Map() { + bitmap_id = 0; + color_amount = 1.f; + color_offset = 0.f; + bitmap_scale = QPointF(1., 1.); + _changed = true; + _layer = 0; +} + + +void Map::setBitmapPath(const QString & p) { + bitmap_path = p; + _changed = true; +} + + +void Map::load(TextureManager * tm) { + if (bitmap_id == 0) + bitmap_id = tm->loadTexture(bitmap_path, true, _type == mtNormal); +} + + +void Map::copyToQGLMap(QGLMap & m) const { + m.amount = color_amount; + m.offset = color_offset; + m.scale = QVector2D(bitmap_scale); + if (hasBitmap()) { + m.array_index = tarMaps; + m.map_index = _layer; + } else { + m.array_index = tarEmpty; + m.map_index = (_type == mtNormal ? emrBlue : emrWhite); + } +} + + + + +Material::Material(const QString _name)/*: map_reflection(512)*/ { + setTypes(); + name = _name; + color_diffuse = color_specular = Qt::darkGray; + color_emission = Qt::black; + glass = false; + transparency = reflectivity = 0.f; + map_roughness.color_amount = 0.75f; + map_specular.color_amount = 1.f; + iof = 1.f; + dispersion = 0.05f; + _changed = true; + _index = 0; +} + + +uint Material::hash() { + return qHash(name); +} + + +bool Material::hasTransparency() const { + return float(color_diffuse.alphaF()) * (1.f - transparency) < 1.f; +} + + +bool Material::isMapsChanged() const { + return map_diffuse ._changed || + map_normal ._changed || + map_specular ._changed || + map_roughness._changed || + map_emission ._changed || + map_relief ._changed; +} + + +bool Material::isMapChanged(int type) const { + switch (type) { + case mtDiffuse : return map_diffuse ._changed; + case mtNormal : return map_normal ._changed; + case mtSpecular : return map_specular ._changed; + case mtRoughness: return map_roughness._changed; + case mtEmission : return map_emission ._changed; + case mtRelief : return map_relief ._changed; + } + return false; +} + + +void Material::load(TextureManager * tm) { + map_diffuse .load(tm); + map_normal .load(tm); + map_specular .load(tm); + map_roughness.load(tm); + map_emission .load(tm); + map_relief .load(tm); +} + + +void Material::setMapsChanged() { + map_diffuse ._changed = true; + map_normal ._changed = true; + map_specular ._changed = true; + map_roughness._changed = true; + map_emission ._changed = true; + map_relief ._changed = true; +} + + +void Material::setTypes() { + map_diffuse ._type = mtDiffuse; + map_normal ._type = mtNormal; + map_specular ._type = mtSpecular; + map_roughness._type = mtRoughness; + map_emission ._type = mtEmission; + map_relief ._type = mtRelief; +} diff --git a/qglengine/glmaterial.h b/qglengine/glmaterial.h new file mode 100644 index 0000000..d1fb6a0 --- /dev/null +++ b/qglengine/glmaterial.h @@ -0,0 +1,173 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLMATERIAL_H +#define GLMATERIAL_H + +#include "glshaders_types.h" +#include "chunkstream.h" + +/* +class Texture { +public: + Texture(int _width, int _height, const GLenum & _format = GL_RGBA8, const GLenum & _target = GL_TEXTURE_2D) {wid = _width; hei = _height; format_ = _format; target_ = _target; id_ = 0;} + bool create() {destroy(); createGLTexture(id_, wid, hei, format_, target_); return id_ > 0;} + void destroy() {if (id_ > 0) glDeleteTextures(1, &id_); id_ = 0;} + void bind() {if (id_ > 0) glBindTexture(target_, id_);} + void release() {glBindTexture(target_, 0);} + int width() const {return wid;} + int height() const {return hei;} + GLenum format() const {return format_;} + GLenum target() const {return target_;} + GLuint id() const {return id_;} +private: + int wid, hei; + GLenum format_, target_; + GLuint id_; +}; + + +class CubeTexture { +public: + CubeTexture(int _size, const GLenum & _format = GL_RGBA8) {size = _size; format_ = _format; id_ = 0; changed_ = false; pathes.resize(6);} + bool create(); + void destroy() {if (id_ > 0) glDeleteTextures(1, &id_); id_ = 0;} + void bind() {if (changed_) {changed_ = false; create();} if (id_ > 0) glBindTexture(GL_TEXTURE_CUBE_MAP, id_);} + void release() {glBindTexture(GL_TEXTURE_CUBE_MAP, 0);} + void resize(int _size) {size = _size; changed_ = true;} + void loadFromDirectory(const QString & dir); + void loadFront(const QString & path) {bind(); pathes[0] = path; createGLTexture(id_, rotateQImageLeft(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_POSITIVE_X);} + void loadBack(const QString & path) {bind(); pathes[1] = path; createGLTexture(id_, rotateQImageRight(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_NEGATIVE_X);} + void loadLeft(const QString & path) {bind(); pathes[2] = path; createGLTexture(id_, QImage(path).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y);} + void loadRight(const QString & path) {bind(); pathes[3] = path; createGLTexture(id_, rotateQImage180(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_POSITIVE_Y);} + void loadTop(const QString & path) {bind(); pathes[4] = path; createGLTexture(id_, rotateQImageLeft(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);} + void loadBottom(const QString & path) {bind(); pathes[5] = path; createGLTexture(id_, rotateQImageLeft(QImage(path)).scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), format_, GL_TEXTURE_CUBE_MAP_POSITIVE_Z);} + void load(); + bool isEmpty() const {foreach (const QString & i, pathes) if (!i.isEmpty()) return false; return true;} + GLenum format() const {return format_;} + void setFormat(GLenum f) {format_ = f; changed_ = true;} + GLuint id() const {return id_;} + const QString & path(int side) const {return pathes[side];} + void setPath(int side, const QString & p) {pathes[side] = p;} + void loadPathesFromDirectory(const QString & dir); +private: + bool changed_; + int size; + GLenum format_; + GLuint id_; + QVector pathes; +}; +*/ + +class Map { +public: + Map(); + void setBitmapPath(const QString & p); + void clearBitmap() {setBitmapPath(QString());} + bool hasBitmap() const {return !bitmap_path.isEmpty();} + void load(TextureManager * tm); + void copyToQGLMap(QGLEngineShaders::QGLMap & m) const; + QString bitmap_path; + GLuint bitmap_id; + QPointF bitmap_offset; + QPointF bitmap_scale; + float color_amount; + float color_offset; + + bool _changed; + int _type, _layer; +}; + +class Material { +public: + Material(const QString _name = QString()); + uint hash(); + bool hasTransparency() const; + bool isMapsChanged() const; + bool isMapChanged(int type) const; + void load(TextureManager * tm); + void setMapsChanged(); + void setTypes(); + QString name; + QColor color_diffuse; + QColor color_specular; + QColor color_emission; + bool glass; + float transparency; + float reflectivity; + float iof; + float dispersion; + Map map_diffuse ; + Map map_normal ; + Map map_specular ; + Map map_roughness; + Map map_emission ; + Map map_relief ; + bool _changed; + int _index; + //GLCubeTexture map_reflection; +}; + + +inline QDataStream & operator <<(QDataStream & s, const Map & m) { + ChunkStream cs; + cs.add(1, m.bitmap_path).add(2, m.color_amount).add(3, m.color_offset).add(6, m.bitmap_scale); + s << cs.data(); return s; +} +inline QDataStream & operator >>(QDataStream & s, Map & m) { + ChunkStream cs(s); + cs.readAll(); + cs.get(1, m.bitmap_path).get(2, m.color_amount).get(3, m.color_offset).get(6, m.bitmap_scale); + return s; +} + +inline QDataStream & operator <<(QDataStream & s, const Material * m) { + ChunkStream cs; + cs.add(1, m->name).add(2, m->color_diffuse).add(3, m->color_specular).add(4, m->color_emission) + .add(5, m->transparency).add(6, m->reflectivity).add(7, m->glass).add(8, m->map_diffuse).add(9, m->map_normal) + .add(10, m->map_relief).add(11, m->map_specular).add(12, m->map_roughness).add(13, m->map_emission); + s << /*qCompress*/(cs.data()); return s; +} +inline QDataStream & operator >>(QDataStream & s, Material *& m) { + m = new Material(); + //QByteArray ba; + //s >> ba; + //ba = qUncompres(ba); + ChunkStream cs(s); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: cs.get(m->name); break; + case 2: cs.get(m->color_diffuse); break; + case 3: cs.get(m->color_specular); break; + case 4: cs.get(m->color_emission); break; + case 5: cs.get(m->transparency); break; + case 6: cs.get(m->reflectivity); break; + case 7: cs.get(m->glass); break; + case 8: cs.get(m->map_diffuse); break; + case 9: cs.get(m->map_normal); break; + case 10: cs.get(m->map_relief); break; + case 11: cs.get(m->map_specular); break; + case 12: cs.get(m->map_roughness); break; + case 13: cs.get(m->map_emission); break; + } + } + m->setTypes(); + return s; +} + +#endif // GLMATERIAL_H diff --git a/qglengine/glmesh.cpp b/qglengine/glmesh.cpp new file mode 100644 index 0000000..ced3d35 --- /dev/null +++ b/qglengine/glmesh.cpp @@ -0,0 +1,422 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "glmesh.h" +#include "globject.h" +#include + +using namespace QGLEngineShaders; + +static int _count = 0; + +Mesh::Mesh(): buffer_geom(GL_ARRAY_BUFFER, GL_STATIC_DRAW), + buffer_ind (GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW), + buffer_obj (GL_ARRAY_BUFFER, GL_STREAM_DRAW), + buffer_sel (GL_ARRAY_BUFFER, GL_STREAM_DRAW) { + vao = 0; + hash_ = 0; + changed = hash_changed = objects_changed = selected_changed = true; + //qDebug() << "Mesh, now" << ++_count; +} + + +Mesh::~Mesh() { + //qDebug() << "~Mesh, now" << --_count; + //destroy(); +} + + +Mesh * Mesh::clone() { + Mesh * c = new Mesh(); + c->vertices_ = vertices_ ; + c->normals_ = normals_ ; + c->texcoords_ = texcoords_; + c->triangles_ = triangles_; + c->hash_ = hash_; + c->hash_changed = hash_changed; + //qDebug() << "clone VBO"; + return c; +} + + +void Mesh::init(QOpenGLExtraFunctions * f) { + if (!isInit()) { + buffer_geom.init(f); + buffer_ind .init(f); + buffer_obj .init(f); + buffer_sel .init(f); + f->glGenVertexArrays(1, &vao); + } + changed = true; +} + + +void Mesh::destroy(QOpenGLExtraFunctions * f) { + if (vao != 0) { + f->glDeleteVertexArrays(1, &vao); + buffer_geom.destroy(f); + buffer_ind .destroy(f); + buffer_obj .destroy(f); + buffer_sel .destroy(f); + } + vao = 0; +} + + +void Mesh::calculateNormals() { + normals_.resize(vertices_.size()); + QVector3D dv1, dv2, n; + foreach (const Vector3i & t, triangles_) { + QVector3D & v0(vertices_[t.p0]); + QVector3D & v1(vertices_[t.p1]); + QVector3D & v2(vertices_[t.p2]); + dv1 = v1 - v0, dv2 = v2 - v0; + n = QVector3D::crossProduct(dv1, dv2).normalized(); + normals_[t.p0] = n; + normals_[t.p1] = n; + normals_[t.p2] = n; + } +} + + +void Mesh::calculateTangents() { + if (vertices_.isEmpty() || texcoords_.isEmpty()) return; + if (texcoords_.size() != vertices_.size()) return; + tangents_ .resize(vertices_.size()); + bitangents_.resize(vertices_.size()); + //qDebug() << "calculateBinormals" << vcnt << tcnt << vertices_.size() << texcoords_.size() << "..."; + QVector3D dv1, dv2; + QVector2D dt1, dt2; + QVector3D tan, bitan; + foreach (const Vector3i & t, triangles_) { + QVector3D & v0(vertices_ [t.p0]); + QVector3D & v1(vertices_ [t.p1]); + QVector3D & v2(vertices_ [t.p2]); + QVector2D & t0(texcoords_[t.p0]); + QVector2D & t1(texcoords_[t.p1]); + QVector2D & t2(texcoords_[t.p2]); + dv1 = v1 - v0, dv2 = v2 - v0; + dt1 = t1 - t0, dt2 = t2 - t0; + tan = (dv1 * dt2.y() - dv2 * dt1.y()).normalized(); + bitan = (dv2 * dt1.x() - dv1 * dt2.x()).normalized(); + tangents_ [t.p0] = tan; + tangents_ [t.p1] = tan; + tangents_ [t.p2] = tan; + bitangents_[t.p0] = bitan; + bitangents_[t.p1] = bitan; + bitangents_[t.p2] = bitan; + //qDebug() << " t" << t << vi << ti << dv1.toQVector3D() << "..."; + } + //qDebug() << "calculateBinormals" << vcnt << tcnt << tangents_.size(); +} + + +void Mesh::loadBuffer(QOpenGLExtraFunctions * f, Buffer & buf, const void * data, int size) { + if (!isInit()) init(f); + if (!buf.isInit() || !data) return; + buf.bind(f); + buf.resize(f, size); + buf.load(f, data, size); + //qDebug() << "loadBuffer" << size << "bytes"; + /*void * map = buf.map(f, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + qDebug() << map; + if (map) + memcpy(map, objects_.constData(), osize); + buf.unmap(f);*/ +} + + +bool Mesh::rebuffer(QOpenGLExtraFunctions * f) { + changed = false; + if (vertices_.isEmpty()) return true; + if (normals_.isEmpty()) + calculateNormals(); + calculateTangents(); + vert_count = qMin(vertices_.size(), normals_.size()); + vert_count = qMin(vert_count, tangents_.size()); + vert_count = qMin(vert_count, bitangents_.size()); + vert_count = qMin(vert_count, texcoords_.size()); + data_.resize(vert_count); + for (int i = 0; i < vert_count; ++i) { + Vertex & v(data_[i]); + v.pos = vertices_ [i]; + v.normal = normals_ [i]; + v.tangent = tangents_ [i]; + v.bitangent = bitangents_[i]; + v.tex = texcoords_ [i]; + } + int gsize = data_.size() * sizeof(Vertex); + int isize = triangles_.size() * sizeof(Vector3i); + f->glBindVertexArray(vao); + + buffer_geom.bind(f); + buffer_geom.resize(f, gsize); + buffer_geom.load(f, data_.constData(), gsize); + prepareDrawGeom(f); + + buffer_ind.bind(f); + buffer_ind.resize(f, isize); + buffer_ind.load(f, triangles_.constData(), isize); + + buffer_obj.bind(f); + prepareDrawObj(f); + + buffer_sel.bind(f); + prepareDrawSel(f); + + f->glBindVertexArray(0); + return !isEmpty(); +} + + +void Mesh::prepareDrawGeom(QOpenGLExtraFunctions * f) { + //qDebug() << "prepareDrawGeom"; + + f->glEnableVertexAttribArray(pos_loc ); + f->glEnableVertexAttribArray(normal_loc ); + f->glEnableVertexAttribArray(tangent_loc ); + f->glEnableVertexAttribArray(bitangent_loc); + f->glEnableVertexAttribArray(tex_loc ); + + int size = sizeof(Vertex); + f->glVertexAttribPointer(pos_loc , 3, GL_FLOAT, GL_FALSE, size, (const void *)pos_offset ); + f->glVertexAttribPointer(normal_loc , 3, GL_FLOAT, GL_FALSE, size, (const void *)normal_offset ); + f->glVertexAttribPointer(tangent_loc , 3, GL_FLOAT, GL_FALSE, size, (const void *)tangent_offset ); + f->glVertexAttribPointer(bitangent_loc, 3, GL_FLOAT, GL_FALSE, size, (const void *)bitangent_offset); + f->glVertexAttribPointer(tex_loc , 2, GL_FLOAT, GL_FALSE, size, (const void *)tex_offset ); +} + + +void Mesh::prepareDrawObj(QOpenGLExtraFunctions * f) { + //qDebug() << "prepareDrawObj"; + + f->glEnableVertexAttribArray(material_loc ); + f->glEnableVertexAttribArray(object_id_loc); + f->glEnableVertexAttribArray(color_loc ); + for (int i = 0; i < 4; ++i) { + f->glEnableVertexAttribArray(modelmatrix_loc + i); + } + + GLsizei size = sizeof(Object); + f->glVertexAttribIPointer(material_loc , 1, GL_UNSIGNED_INT , size, (const void *)material_offset ); + f->glVertexAttribIPointer(object_id_loc, 1, GL_UNSIGNED_INT , size, (const void *)object_id_offset); + f->glVertexAttribPointer (color_loc , 4, GL_FLOAT, GL_FALSE, size, (const void *)color_offset ); + for (int i = 0; i < 4; ++i) { + f->glVertexAttribPointer(modelmatrix_loc + i, 4, GL_FLOAT, GL_FALSE, size, (const void *)(modelmatrix_offset + sizeof(QVector4D)*i)); + } + + f->glVertexAttribDivisor(material_loc , 1); + f->glVertexAttribDivisor(object_id_loc, 1); + f->glVertexAttribDivisor(color_loc , 1); + for (int i = 0; i < 4; ++i) { + f->glVertexAttribDivisor(modelmatrix_loc + i, 1); + } + +} + + +void Mesh::prepareDrawSel(QOpenGLExtraFunctions * f) { + //qDebug() << "prepareDrawObj"; + + f->glEnableVertexAttribArray(is_selected_loc); + GLsizei size = 1; + f->glVertexAttribIPointer(is_selected_loc, 1, GL_UNSIGNED_BYTE, size, (const void *)is_selected_offset); + f->glVertexAttribDivisor(is_selected_loc, 1); + +} + + +void Mesh::draw(QOpenGLExtraFunctions * f, int count) { + if (isEmpty()) return; + if (!isInit()) init(f); + if (changed) rebuffer(f); + //qDebug() << "draw" << vert_count << count; + + f->glBindVertexArray(vao); + f->glDrawElementsInstanced(GL_TRIANGLES, triangles_.size() * 3, GL_UNSIGNED_INT, 0, count); + f->glBindVertexArray(0); +} + + +void Mesh::clear() { + vertices_.clear(); + normals_.clear(); + tangents_.clear(); + bitangents_.clear(); + texcoords_.clear(); + triangles_.clear(); + data_.clear(); + changed = hash_changed = true; +} + + +void Mesh::loadObject(QOpenGLExtraFunctions * f, const Object & object) { + loadBuffer(f, buffer_obj, &object, sizeof(Object)); +} + + +void Mesh::loadObjects(QOpenGLExtraFunctions * f, const QVector & objects) { + loadBuffer(f, buffer_obj, objects.constData(), objects.size() * sizeof(Object)); +} + + +void Mesh::loadSelections(QOpenGLExtraFunctions * f, const QVector & sels) { + //qDebug() << "loadSelections" << sels; + loadBuffer(f, buffer_sel, sels.constData(), sels.size()); +} + + +uint Mesh::hash() const { + if (hash_changed) { + hash_changed = false; + hash_ = qHashBits(vertices_ .constData(), vertices_ .size() * sizeof(QVector3D)); + hash_ ^= qHashBits(normals_ .constData(), normals_ .size() * sizeof(QVector3D)); + hash_ ^= qHashBits(texcoords_.constData(), texcoords_.size() * sizeof(QVector2D)); + hash_ ^= qHashBits(triangles_.constData(), triangles_.size() * sizeof( Vector3i)); + } + return hash_; +} + + +void Mesh::translatePoints(const QVector3D & dp) { + if (vertices_.isEmpty()) return; + int vcnt = vertices_.size(); + for (int i = 0; i < vcnt; ++i) { + vertices_[i] += dp; + } + changed = hash_changed = true; +} + + +void Mesh::scalePoints(const QVector3D & dp) { + if (vertices_.isEmpty()) return; + int vcnt = vertices_.size(); + for (int i = 0; i < vcnt; ++i) { + vertices_[i] *= dp; + } + changed = hash_changed = true; +} + + +void Mesh::append(const Mesh * m) { + if (!m) return; + if (m->isEmpty()) return; + if (normals_.isEmpty()) calculateNormals(); + int vcnt = vertices_.size(); + vertices_ .append(m->vertices_ ); + normals_ .append(m->normals_ ); + texcoords_.append(m->texcoords_); + QVector tri = m->triangles_; + for (int i = 0; i < tri.size(); ++i) + tri[i] += vcnt; + triangles_.append(tri); +} + + +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_; + 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_; + 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_); + //qDebug() << "place VBO done" << cs.data().size() << "..."; + s << /*qCompress*/(cs.data()); return s; +} + + +QDataStream & operator >>(QDataStream & s, Mesh *& m) { + m = new Mesh(); + //QByteArray ba; + //s >> ba; + //ba = qUncompress(ba); + ChunkStream cs(s); + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: cs.get(m->vertices_ ); break; + case 2: cs.get(m->normals_ ); break; + case 3: cs.get(m->texcoords_); break; + case 6: cs.get(m->triangles_); break; + } + } + m->changed = true; + return s; +} diff --git a/qglengine/glmesh.h b/qglengine/glmesh.h new file mode 100644 index 0000000..9bb770c --- /dev/null +++ b/qglengine/glmesh.h @@ -0,0 +1,98 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLMESH_H +#define GLMESH_H + +#include +#include "glbuffer.h" +#include "glshaders_types.h" + + +class Mesh +{ + friend class ObjectBase; + friend class Scene; + friend class Renderer; + friend QDataStream & operator <<(QDataStream & s, const Mesh * m); + friend QDataStream & operator >>(QDataStream & s, Mesh *& m); +public: + Mesh(); + ~Mesh(); + + Mesh * clone(); + + //GLVBO & operator =(const GLVBO & o) {return *this;} + + void init (QOpenGLExtraFunctions * f); + void destroy (QOpenGLExtraFunctions * f); + bool rebuffer(QOpenGLExtraFunctions * f); + void draw (QOpenGLExtraFunctions * f, int count); + void clear(); + void loadObject (QOpenGLExtraFunctions * f, const QGLEngineShaders::Object & object); + void loadObjects (QOpenGLExtraFunctions * f, const QVector & objects); + void loadSelections(QOpenGLExtraFunctions * f, const QVector & sels); + + int verticesCount() const {return vertices_.size();} + int trianglesCount() const {return triangles_.size();} + bool isInit() const {return vao != 0;} + bool isEmpty() const {return vertices_.isEmpty();} + uint hash() const; + + QVector & vertices () {changed = hash_changed = true; return vertices_;} + QVector & normals () {changed = hash_changed = true; return normals_;} + QVector & texcoords() {changed = hash_changed = true; return texcoords_;} + QVector< Vector3i> & indices () {changed = hash_changed = true; return triangles_;} + + void translatePoints(const QVector3D & dp); + void scalePoints (const QVector3D & dp); + void append(const Mesh * m); + + bool saveToFile(const QString & filename); + bool loadFromFile(const QString & filename); + + Box3D boundingBox() const; + + static void prepareDrawGeom(QOpenGLExtraFunctions * f); + static void prepareDrawObj (QOpenGLExtraFunctions * f); + static void prepareDrawSel (QOpenGLExtraFunctions * f); + +private: + void calculateNormals(); + void calculateTangents(); + void loadBuffer(QOpenGLExtraFunctions * f, Buffer & buf, const void * data, int size); + + QVector vertices_, normals_, tangents_, bitangents_; + QVector texcoords_; + QVector< Vector3i> triangles_; + + QVector data_; + GLenum vao; + Buffer buffer_geom, buffer_ind, buffer_obj, buffer_sel; + mutable uint hash_; + mutable bool hash_changed; + int vert_count; + bool changed, objects_changed, selected_changed; + +}; + + +QDataStream & operator <<(QDataStream & s, const Mesh * m); +QDataStream & operator >>(QDataStream & s, Mesh *& m); + +#endif // GLMESH_H diff --git a/qglengine/globject.cpp b/qglengine/globject.cpp new file mode 100644 index 0000000..a016f05 --- /dev/null +++ b/qglengine/globject.cpp @@ -0,0 +1,624 @@ +/* + GLObjectBase & Light + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "globject.h" +#include "glcamera.h" +#include "glscene.h" +#include "glmesh.h" +#include +static int _count = 0; + +ObjectBase::ObjectBase(Mesh * geom, Material * mat) { + type_ = glMesh; + render_mode = View; + pass_ = Solid; + scale_ = QVector3D(1., 1., 1.); + parent_ = nullptr; + color_ = Qt::white; + is_root = is_init = is_tex_loaded = 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 = 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(); + } + foreach (ObjectBase * c, children_) { + c->parent_ = nullptr; + delete c; + } +} + + +ObjectBase * ObjectBase::clone(bool withChildren) { + ObjectBase * o = new ObjectBase(); + o->pass_ = pass_; + o->is_init = false; + o->accept_light = accept_light; + o->accept_fog = accept_fog; + o->visible_ = visible_; + o->type_ = type_; + o->raw_matrix = raw_matrix; + o->mat_ = mat_; + o->pos_ = pos_; + o->angles_ = angles_; + o->scale_ = scale_; + 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; +} + + +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_) { + QList 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; +} + + +QList ObjectBase::children(bool all_) { + if (!all_) return children_; + QList 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::rotateX(GLfloat a) { + raw_matrix = false; + angles_.setX(angles_.x() + a); + buildTransform(); +} + + +void ObjectBase::rotateY(GLfloat a) { + raw_matrix = false; + angles_.setY(angles_.y() + a); + buildTransform(); +} + + +void ObjectBase::rotateZ(GLfloat a) { + raw_matrix = false; + 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::setRotationX(GLfloat a) { + raw_matrix = false; + angles_.setX(a); + buildTransform(); +} + + +void ObjectBase::setRotationY(GLfloat a) { + raw_matrix = false; + angles_.setY(a); + buildTransform(); +} + + +void ObjectBase::setRotationZ(GLfloat a) { + raw_matrix = false; + 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::setRotation(const QVector3D & a) { + raw_matrix = false; + angles_= a; + buildTransform(); +} + + +void ObjectBase::resetRotation() { + raw_matrix = false; + angles_ = QVector3D(0., 0., 0.); + buildTransform(); +} + + +void ObjectBase::addChildren(QList & list, ObjectBase * where) { + foreach (ObjectBase * i, where->children_) { + list << i; + addChildren(list, i); + } +} + + +void ObjectBase::loadTextures(bool with_children) { + if (with_children) + foreach (ObjectBase * i, children_) i->loadTextures(); + is_tex_loaded = true; + checkPass(); +} + + +void ObjectBase::calculateBoundingBox() { + bound = Box3D(); + if (mesh_) { + bound = mesh_->boundingBox(); + QVector c = bound.corners(), tc; + foreach (QVector3D p, c) + tc << (itransform_ * QVector4D(p, 1)).toVector3D(); + bound = Box3D(tc); + } + foreach (ObjectBase * i, children_) { + i->calculateBoundingBox(); + bound |= i->boundingBox(); + } +} + + +void ObjectBase::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::setTransform(const QMatrix4x4 & t) { + raw_matrix = true; + mat_ = t; + pos_ = mat_.column(3).toVector3D(); + mat_.setColumn(3, QVector4D(0., 0., 0., 1.)); + buildTransform(); +} + + +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; +} + + +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); + checkPass(); + is_tex_loaded = false; + setMeshChanged(); + if (scene_) scene_->mat_changed = true; +} + + +void ObjectBase::setColor(QColor c, bool with_children) { + color_ = c; + if (with_children) + foreach (ObjectBase * i, children_) i->setColor(c, true); + setMeshChanged(); +} + + +void ObjectBase::setMesh(Mesh * v) { + mesh_ = v; + setSceneTreeChanged(); + setMeshChanged(); +} + + +void ObjectBase::buildTransform() { + 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(); + setMeshChanged(); +} + + +void ObjectBase::initInternal() { + init(); + foreach (ObjectBase * i, children_) i->initInternal(); +} + + +void ObjectBase::localTransform(QMatrix4x4 & m) { + m.translate(pos_); + m.rotate(angles_.z(), 0., 0., 1.); + m.rotate(angles_.y(), 0., 1., 0.); + m.rotate(angles_.x(), 1., 0., 0.); + m.scale(scale_); +} + + +void ObjectBase::checkPass() { + pass_ = Solid; + if (material_) { + if (material_->hasTransparency()) + pass_ = Transparent; + } +} + + +void ObjectBase::setSceneTreeChanged() { + if (scene_) { + scene_->setTreeChanged(); + scene_->setTreeStructChanged(); + } + setMeshChanged(); +} + + +void ObjectBase::setMeshChanged() { + if (mesh_) mesh_->objects_changed = true; +} + + +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_); + } + return parent * mat; +} + + + + +Light::Light(): ObjectBase(), 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.; + direction.setZ(1.); +} + + +Light::Light(const QVector3D & p, const QColor & c, float i): ObjectBase(), shadow_map(0, true, GL_R16F) { + type_ = glLight; + light_type = Omni; + pos_ = p; + intensity = i; + color_ = c; + angle_start = angle_end = 180.; + decay_linear = decay_quadratic = decay_start = 0.; + decay_const = decay_end = 1.; + direction.setZ(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->light_type = light_type; + o->direction = direction; + 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(11, p->pos_).add(12, p->angles_) + .add(13, p->scale_).add(14, p->mat_).add(16, p->children_.size()) + .add(17, p->name_).add(18, p->meta).add(19, p->color_); + //qDebug() << "place self done"; + if (p->type_ == ObjectBase::glLight) { + //qDebug() << "place light ..."; + const Light * l = (const Light*)p; + cs.add(100, l->direction).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)); + } + 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(203, c->depth_end) + .add(204, c->angle_limit_lower_xy).add(205, c->angle_limit_upper_xy) + .add(206, c->mirror_x).add(207, c->mirror_y); + } + //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; + QVector3D cam_angles; + //qDebug() << "read obj ..."; + while (!cs.atEnd()) { + switch (cs.read()) { + case 1: { + ObjectBase::Type type = (ObjectBase::Type)cs.getData(); + switch (type) { + case ObjectBase::glMesh: p = new ObjectBase(); break; + case ObjectBase::glLight: p = new Light(); l = (Light*)p; break; + case ObjectBase::glCamera: p = new Camera(); c = (Camera*)p; break; + default : break; + } + if (p) p->type_ = type; + } break; + case 2: if (p) p->accept_light = cs.getData(); break; + case 3: if (p) p->accept_fog = cs.getData(); break; + case 4: if (p) p->visible_ = cs.getData(); break; + case 5: if (p) p->cast_shadow = cs.getData(); break; + case 6: if (p) p->rec_shadow = cs.getData(); break; + case 7: if (p) p->raw_matrix = cs.getData(); break; + case 8: if (p) p->line_width = cs.getData(); break; + case 9: if (p) p->render_mode = (ObjectBase::RenderMode)cs.getData(); break; + //case 10: if (p) p->material_ = cs.getData(); break; + case 11: if (p) p->pos_ = cs.getData(); break; + case 12: + if (p) p->angles_ = cs.getData(); + if (c) { + c->setAngles(cs.getData()); + cam_angles = c->angles(); + } + break; + case 13: if (p) p->scale_ = cs.getData(); break; + case 14: if (p) p->mat_ = cs.getData(); break; + //case 15: if (p) p->vbo = cs.getData(); break; + case 16: if (p) ccnt = cs.getData(); break; + case 17: if (p) p->name_ = cs.getData(); break; + case 18: if (p) p->meta = cs.getData(); break; + case 19: if (p) p->color_ = cs.getData(); break; + case 100: if (l) l->direction = cs.getData(); break; + case 101: if (l) l->angle_start = cs.getData(); break; + case 102: if (l) l->angle_end = cs.getData(); break; + case 103: if (l) l->intensity = cs.getData(); break; + case 104: if (l) l->decay_const = cs.getData(); break; + case 105: if (l) l->decay_linear = cs.getData(); break; + case 106: if (l) l->decay_quadratic = cs.getData(); break; + case 107: if (l) l->decay_start = cs.getData(); break; + case 108: if (l) l->decay_end = cs.getData(); break; + case 109: if (l) l->light_type = (Light::Type)cs.getData(); break; + case 200: if (c) c->setAim(cs.getData()); break; + case 201: if (c) c->setFOV(cs.getData()); break; + case 202: if (c) c->setDepthStart(cs.getData()); break; + case 203: if (c) c->setDepthEnd(cs.getData()); break; + case 204: if (c) c->setAngleLowerLimitXY(cs.getData()); break; + case 205: if (c) c->setAngleUpperLimitXY(cs.getData()); break; + case 206: if (c) c->mirror_x = cs.getData(); break; + case 207: if (c) c->mirror_y = cs.getData(); break; + } + } + if (c) c->setAngles(cam_angles); + //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; + } + return s; +} diff --git a/qglengine/globject.h b/qglengine/globject.h new file mode 100644 index 0000000..152018b --- /dev/null +++ b/qglengine/globject.h @@ -0,0 +1,275 @@ +/* + GLObjectBase & Light + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLOBJECT_H +#define GLOBJECT_H + +#include "glframebuffer.h" +#include "glmaterial.h" +#include "gltypes.h" + + +class ObjectBase +{ + friend class QGLView; + friend class Scene; + friend class Renderer; + friend QDataStream & operator <<(QDataStream & s, const ObjectBase * p); + friend QDataStream & operator >>(QDataStream & s, ObjectBase *& p); + friend QDataStream & operator >>(QDataStream & s, Scene *& p); +public: + enum Type {glMesh, glLight, glCamera, glParticlesSystem}; + enum Pass {Solid, Transparent, Reflection, User}; + 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 GLuint hList() {return list;} + virtual void init(); + virtual void update() {} + bool isInit() const {return is_init;} + bool isTexturesLoaded() const {return is_tex_loaded;} + Type type() const {return type_;} + + 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_.size() != 0;} + 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; + QList 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) {pos_ += dv; buildTransform();} + void moveTo(const QVector3D & dv) {pos_ = 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) {pos_.setX(pos_.x() + o); buildTransform();} + void moveY(GLfloat o) {pos_.setY(pos_.y() + o); buildTransform();} + void moveZ(GLfloat o) {pos_.setZ(pos_.z() + o); buildTransform();} + void setPosX(GLfloat o) {pos_.setX(o); buildTransform();} + void setPosY(GLfloat o) {pos_.setY(o); buildTransform();} + void setPosZ(GLfloat o) {pos_.setZ(o); buildTransform();} + void setPos(GLfloat x, GLfloat y, GLfloat z) {pos_ = QVector3D(x, y, z); buildTransform();} + void setPos(const QVector3D & p) {pos_ = p; buildTransform();} + void resetPos() {pos_ = QVector3D(0., 0., 0.); buildTransform();} + + QVector3D pos() const {return pos_;} + float posX() const {return pos_.x();} + float posY() const {return pos_.y();} + float posZ() const {return pos_.z();} + QVector3D worldPos() const {return (itransform_ * QVector4D(0, 0, 0, 1.)).toVector3D();} + QMatrix4x4 worldTransform() const {return itransform_;} + + QVector3D rotation() const {return angles_;} + float rotationX() const {return angles_.x();} + float rotationY() const {return angles_.y();} + float rotationZ() const {return angles_.z();} + void rotateX(GLfloat a); + void rotateY(GLfloat a); + void rotateZ(GLfloat a); + void setRotationX(GLfloat a); + void setRotationY(GLfloat a); + void setRotationZ(GLfloat a); + void setRotation(const QVector3D & a); + void resetRotation(); + + QVector3D scale() {return scale_;} + float scaleX() {return scale_.x();} + float scaleY() {return scale_.y();} + float scaleZ() {return scale_.z();} + void scale(const QVector3D & sv) {raw_matrix = false; scale_ *= 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; scale_.setX(scale_.x() + a); buildTransform();} + void scaleY(GLfloat a) {raw_matrix = false; scale_.setY(scale_.y() + a); buildTransform();} + void scaleZ(GLfloat a) {raw_matrix = false; scale_.setZ(scale_.z() + a); buildTransform();} + void setScale(const QVector3D & a) {raw_matrix = false; scale_ = a; buildTransform();} + void setScale(GLfloat a) {raw_matrix = false; scale_ = QVector3D(a, a, a); buildTransform();} + void setScaleX(GLfloat a) {raw_matrix = false; scale_.setX(a); buildTransform();} + void setScaleY(GLfloat a) {raw_matrix = false; scale_.setY(a); buildTransform();} + void setScaleZ(GLfloat a) {raw_matrix = false; scale_.setZ(a); buildTransform();} + void resetScale() {raw_matrix = false; scale_ = QVector3D(1., 1., 1.); buildTransform();} + + QMatrix4x4 transform() {return mat_;} + void setTransform(const QMatrix4x4 & t); + bool isRawMatrix() {return raw_matrix;} + + bool isAcceptLight() const {return accept_light;} + void setAcceptLight(bool yes) {accept_light = yes;} + + bool isAcceptFog() const {return accept_fog;} + void setAcceptFog(bool yes) {accept_fog = yes;} + + bool isSelected(bool check_parents = false) const; + void setSelected(bool yes); + void select() {setSelected(true);} + void deselect() {setSelected(false);} + ObjectBase * selectedParent() const; + + bool isSelectable() const {return select_;} + void setSelectable(bool yes) {select_ = yes;} + /* + bool isWriteDepth() const {return write_depth_;} + void setWriteDepth(bool yes) {write_depth_ = yes;}*/ + + GLenum srcAlpha() const {return blend_src;} + GLenum destAlpha() const {return blend_dest;} + void setSrcAlpha(GLenum mode) {blend_src = mode;} + void setDestAlpha(GLenum mode) {blend_dest = mode;} + + void setMaterial(Material * m, bool with_children = false); + Material * material() {return material_;} + + void setColor(QColor c, bool with_children = false); + QColor color() {return color_;} + + const Box3D & boundingBox() const {return bound;} + void setMesh(Mesh * v); + Mesh * mesh() {return mesh_;} + + void calculateBoundingBox(); + + void setProperty(const QString & pn, const QVariant & v); + QVariant property(const QString & pn, bool * exists = 0) const; + bool hasProperty(const QString & pn) const; + void removeProperty(const QString & pn); + + QVector3D pos_h; + + //QVector d_vertices, d_normals, d_uvs; + +protected: + void addChildren(QList & list, ObjectBase * where); + void loadTextures(bool with_children = false); + //void deleteTextures() {foreach (GLuint i, textures) currentQGLView->deleteTexture(i); textures.clear();} + void buildTransform(); + void initInternal(); + void checkPass(); + void setSceneTreeChanged(); + void setMeshChanged(); + virtual void localTransform(QMatrix4x4 & m); + QMatrix4x4 worldMatrix(QMatrix4x4 parent) const; + + int pass_; // Pass + bool is_init, is_tex_loaded, accept_light, accept_fog, /*write_depth_,*/ visible_, cast_shadow, rec_shadow, select_, selected_, raw_matrix; + bool is_root; + float line_width; + QColor color_; + uint id_; + Type type_; + RenderMode render_mode; + Box3D bound; + QVector3D pos_, angles_, scale_; + QList children_; + QMatrix4x4 itransform_, mat_; + QString name_; + GLenum blend_src, blend_dest; + ObjectBase * parent_; + Scene * scene_; + Material * material_; + Mesh * mesh_; + QVariantMap meta; + +}; + +inline bool operator <(const ObjectBase & f, const ObjectBase & s) {return f.pos_h.z() < s.pos_h.z();} + + +class AimedObject: public ObjectBase { + friend class QGLView; + friend class GLRendererBase; +public: + AimedObject(); + ~AimedObject(); +}; + + +class Light: public ObjectBase { + friend class QGLView; + friend class RendererBase; +public: + enum Type {Omni, Directional, Cone}; + + 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(); + + QVector3D direction, dir0, dir1; + float angle_start; + float angle_end; + float intensity; + float decay_const; + float decay_linear; + float decay_quadratic; + float decay_start; + float decay_end; + Type light_type; + Framebuffer shadow_map; + QMatrix4x4 shadow_matrix; + +protected: + +}; + +template +inline T globject_cast(ObjectBase * object) {return reinterpret_cast(object);} + +template +inline T globject_cast(const ObjectBase * object) {return reinterpret_cast(object);} + + +QDataStream & operator <<(QDataStream & s, const ObjectBase * p); +QDataStream & operator >>(QDataStream & s, ObjectBase *& p); + +#endif // GLOBJECT_H diff --git a/qglengine/glprimitives.cpp b/qglengine/glprimitives.cpp new file mode 100644 index 0000000..4f201d4 --- /dev/null +++ b/qglengine/glprimitives.cpp @@ -0,0 +1,263 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glprimitives.h" +#include "glmesh.h" + + + + +Mesh * Primitive::plane(float width, float length) { + Mesh * ret = new Mesh(); + QVector & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & t(ret->texcoords()); + QVector< Vector3i> & i(ret->indices ()); + float hw = width / 2.f, hl = length / 2.f; + for (int j = 0; j < 4; ++j) n << QVector3D(0., 0., 1.); + t << QVector2D(0., 0.) << QVector2D(0., 1.) << QVector2D(1., 1.) << QVector2D(1., 0.); + v << QVector3D(-hw, -hl, 0.) << QVector3D(-hw, hl, 0.) << QVector3D(hw, hl, 0.) << QVector3D(hw, -hl, 0.); + i << Vector3i(0, 2, 1) << Vector3i(0, 3, 2); + return ret; +} + + +Mesh * Primitive::cube(float width, float length, float height) { + Mesh * ret = new Mesh(); + QVector3D scale(width, length, height); + QVector & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & t(ret->texcoords()); + QVector< Vector3i> & i(ret->indices ()); + float hs = 0.5f; + int si = 0; + QMatrix4x4 mat; + + si = v.size(); + for (int j = 0; j < 4; ++j) n << QVector3D(0., -1., 0.); + t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.); + v << QVector3D(-hs, -hs, -hs) << QVector3D(hs, -hs, -hs) << QVector3D(hs, -hs, hs) << QVector3D(-hs, -hs, hs); + i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3); + + for (int r = 0; r < 3; ++r) { + si = v.size(); + mat.rotate(90., 0., 0., 1.); + QVector3D cn = mat.map(n[0]); + for (int j = 0; j < 4; ++j) { + n << cn; + v << mat.map(QVector4D(v[j])).toVector3D(); + } + t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.); + i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3); + } + + mat.setToIdentity(); + mat.rotate(90., 1., 0.,0.); + for (int r = 0; r < 2; ++r) { + si = v.size(); + mat.rotate(180., 1., 0.,0.); + QVector3D cn = mat.map(n[0]); + for (int j = 0; j < 4; ++j) { + n << cn; + v << mat.map(QVector4D(v[j])).toVector3D(); + } + t << QVector2D(0., 0.) << QVector2D(1., 0.) << QVector2D(1., 1.) << QVector2D(0., 1.); + i << Vector3i(si + 0, si + 1, si + 2) << Vector3i(si + 0, si + 2, si + 3); + } + + for (int i = 0; i < v.size(); ++i) + v[i] *= scale; + + return ret; +} + + +Mesh * Primitive::ellipsoid(int segments_wl, int segments_h, float width, float length, float height) { + Mesh * ret = new Mesh(); + QVector & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & t(ret->texcoords()); + QVector< Vector3i> & ind(ret->indices()); + double hh = height / 2.f; + int hseg = segments_h + 1, wlseg = segments_wl + 1; + double crw, crl, a, ch, twl; + + QVector3D cp; + for (int i = 0; i <= hseg; i++) { + ch = -cos((double)i / hseg * M_PI); + cp.setZ(ch * hh); + twl = sqrt(1. - ch * ch) / 2.; + crw = twl * width; + crl = twl * length; + int cvcnt = wlseg * 2; + for (int j = 0; j < cvcnt; j++) { + a = (double)j / (cvcnt - 1) * M_2PI; + cp.setX(crl * cos(a)); + cp.setY(crw * sin(a)); + v << cp; t << QVector2D((double)j / (cvcnt - 1), ch/2.f + 0.5f); + int si = v.size() - 1; + if (j > 0 && i > 0) { + ind << Vector3i(si - cvcnt - 1, si, si - 1); + ind << Vector3i(si - cvcnt, si, si - cvcnt - 1); + } + } + } + + n.resize(v.size()); + for (int i = 0; i < v.size(); i++) + n[i] = v[i].normalized(); + + return ret; +} + + +Mesh * Primitive::disc(int segments, float width, float length, bool up) { + Mesh * ret = new Mesh(); + QVector & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & t(ret->texcoords()); + QVector< Vector3i> & ind(ret->indices()); + + segments = qMax(segments + 1, 4); + QVector3D cp; + v << QVector3D(); + t << QVector2D(0.5f, 0.5f); + for (int i = 0; i < segments; i++) { + double a = (double)i / (segments - 1) * M_2PI; + cp.setX(length / 2. * cos(a)); + cp.setY(width / 2. * sin(a)); + v << cp; + t << QVector2D(cp.x() / width + 0.5f, cp.y() / length + 0.5f); + int si = v.size() - 1; + if (i > 0) { + if (up) + ind << Vector3i(si - 1, si, 0); + else + ind << Vector3i(si, si - 1, 0); + } + } + + n.resize(v.size()); + for (int i = 0; i < v.size(); i++) + n[i] = QVector3D(0, 0, up ? 1 : -1); + + return ret; +} + + +QVector3D coneNormal(double rx, double ry, double height, double ang) { + QVector3D norm; + norm.setX(rx * cos(ang)); + norm.setY(ry * sin(ang)); + norm.setZ(0.); + double rl = norm.length(); + double ca = atan2(rl, height); + norm *= cos(ca); + norm.setZ(norm.length() * tan(ca)); + return norm.normalized(); +} +Mesh * Primitive::cone(int segments, float width, float length, float height) { + Mesh * ret = new Mesh(); + QVector & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & t(ret->texcoords()); + QVector< Vector3i> & ind(ret->indices()); + + int seg = qMax(segments + 1, 4); + double rx = width / 2., ry = length / 2.; + QVector3D cp; + for (int i = 0; i < seg; i++) { + double a = (double)i / (seg - 1) * M_2PI; + cp.setX(ry * cos(a)); + cp.setY(rx * sin(a)); + if (i > 0) { + v << QVector3D(0, 0, height); + t << QVector2D((double)(i - 1) / (seg - 1), 1.f); + double ta = ((double)i - 0.5) / (seg - 1) * M_2PI; + n << coneNormal(rx, ry, height, ta); + } + v << cp; + t << QVector2D((double)i / (seg - 1), 0.f); + n << coneNormal(rx, ry, height, a); + int si = v.size() - 1; + if (i > 0) + ind << Vector3i(si - 1, si - 2, si); + } + + Mesh * cap = Primitive::disc(segments, width, length, false); + ret->append(cap); + delete cap; + + return ret; +} + + +Mesh * Primitive::cylinder(int segments, float width, float length, float height) { + Mesh * ret = new Mesh(); + QVector & v(ret->vertices ()); + QVector & n(ret->normals ()); + QVector & t(ret->texcoords()); + QVector< Vector3i> & ind(ret->indices()); + + int seg = qMax(segments + 1, 4); + double rx = width / 2., ry = length / 2.; + QVector3D cp, norm; + for (int i = 0; i < seg; i++) { + double a = (double)i / (seg - 1) * M_2PI; + cp.setX(ry * cos(a)); + cp.setY(rx * sin(a)); + cp.setZ(0.); + norm = cp.normalized(); + v << cp; + cp.setZ(height); + v << cp; + t << QVector2D((double)i / (seg - 1), 0.f); + t << QVector2D((double)i / (seg - 1), 1.f); + n << norm; n << norm; + int si = v.size() - 1; + if (i > 0) { + ind << Vector3i(si - 2, si - 1, si); + ind << Vector3i(si - 1, si - 2, si - 3); + } + } + + Mesh * cap = Primitive::disc(segments, width, length, false); + ret->append(cap); + delete cap; + cap = Primitive::disc(segments, width, length, true); + cap->translatePoints(QVector3D(0., 0., height)); + ret->append(cap); + delete cap; + + return ret; +} + + +Mesh * Primitive::arrow(int segments, float thick, float angle) { + double cone_d = 3. * thick; + double cone_h = cone_d / tan(angle * deg2rad); + Mesh * ret = new Mesh(); + Mesh * m = Primitive::cylinder(segments, thick, thick, 1. - cone_h); + ret->append(m); + delete m; + m = Primitive::cone(segments, cone_d, cone_d, cone_h); + m->translatePoints(QVector3D(0., 0., 1. - cone_h)); + ret->append(m); + delete m; + return ret; +} diff --git a/qglengine/glprimitives.h b/qglengine/glprimitives.h new file mode 100644 index 0000000..87657d6 --- /dev/null +++ b/qglengine/glprimitives.h @@ -0,0 +1,94 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLPRIMITIVE_CUBE_H +#define GLPRIMITIVE_CUBE_H + +#include "gltypes.h" + + +namespace Primitive { + + +Mesh * plane(float width = 1., float length = 1.); + +Mesh * cube(float width = 1., float length = 1., float height = 1.); + +Mesh * ellipsoid(int segments_wl, int segments_h, float width = 1., float length = 1., float height = 1.); + +Mesh * disc(int segments, float width = 1., float length = 1., bool up = true); + +Mesh * cone(int segments, float width = 1., float length = 1., float height = 1.); + +Mesh * cylinder(int segments, float width = 1., float length = 1., float height = 1.); + +Mesh * arrow(int segments = 16, float thick = 0.04, float angle = 30.); // length = 1 + + +} + +/* +class GLPrimitivePoint: public GLObjectBase +{ +public: + GLPrimitivePoint(double size = 1., QVector3D pos = QVector3D()) {sz = 8.;} + virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false); +private: + double sz; +}; + + + + +class GLPrimitiveLine: public GLObjectBase +{ +public: + GLPrimitiveLine(QVector3D p0_ = QVector3D(), QVector3D p1_ = QVector3D()) {p0 = p0_; p1 = p1_;} + virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false); + QVector3D point0() const {return p0;} + QVector3D point1() const {return p1;} + void setPoint0(const QVector3D & p) {p0 = p;} + void setPoint1(const QVector3D & p) {p1 = p;} +private: + QVector3D p0, p1; +}; + + + + +class GLPrimitiveEllipsoid: public GLObjectBase +{ +public: + GLPrimitiveEllipsoid(float width = 1., float length = 1., float height = 1., int seg_wl = 10, int seg_h = 10, QVector3D pos = QVector3D()); + virtual void init(); +private: + void putTriangle(const QVector3D & v0, const QVector3D & v1, const QVector3D & v2); + float w, l, h; + int swl, sh; +}; + + +class GLPrimitiveAxis: public GLObjectBase +{ +public: + GLPrimitiveAxis() {accept_fog = accept_light = cast_shadow = rec_shadow = select_ = false;} + virtual void draw(QOpenGLShaderProgram * prog, bool simplest = false); +}; +*/ + +#endif // GLPRIMITIVE_CUBE_H diff --git a/qglengine/glrendererbase.cpp b/qglengine/glrendererbase.cpp new file mode 100644 index 0000000..77d8527 --- /dev/null +++ b/qglengine/glrendererbase.cpp @@ -0,0 +1,296 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glrendererbase.h" +#include "globject.h" +#include "qglview.h" + + +GLRendererBase::GLRendererBase(QGLView * view_): view(view_) { + white_image = QImage(1, 1, QImage::Format_ARGB32); + white_image.fill(0xFFFFFFFF); + white_image_id = 0; + violent_image = QImage(1, 1, QImage::Format_ARGB32); + violent_image.fill(QColor(127, 127, 255)); + violent_image_id = 0; +} + + +void GLRendererBase::setupLight(const Light & l, int inpass_index, int gl_index) { + QVector3D lp = l.worldPos(), ld;// = (l.itransform_ * QVector4D(l.direction, 0.)).toVector3D().normalized(); + GLfloat pos[] = {0.f, 0.f, 0.f, 0.f}; + GLfloat dir[] = {0.f, 0.f, 0.f}; + GLfloat col[] = {0.f, 0.f, 0.f}; + pos[0] = l.light_type == Light::Directional ? -l.direction.x() : lp.x(); + pos[1] = l.light_type == Light::Directional ? -l.direction.y() : lp.y(); + pos[2] = l.light_type == Light::Directional ? -l.direction.z() : lp.z(); + pos[3] = l.light_type == Light::Directional ? 0. : 1.; + dir[0] = ld.x(); + dir[1] = ld.y(); + dir[2] = ld.z(); + //col[0] = l.visible_ ? l.color().redF() * l.intensity : 0.f; + //col[1] = l.visible_ ? l.color().greenF() * l.intensity : 0.f; + //col[2] = l.visible_ ? l.color().blueF() * l.intensity : 0.f; + glEnable(gl_index); + //glLightfv(gl_index, GL_AMBIENT, ambient); + glLightfv(gl_index, GL_DIFFUSE, col); + glLightfv(gl_index, GL_SPECULAR, col); + glLightfv(gl_index, GL_POSITION, pos); + glLightf(gl_index, GL_CONSTANT_ATTENUATION, l.decay_const); + glLightf(gl_index, GL_LINEAR_ATTENUATION, l.decay_linear); + glLightf(gl_index, GL_QUADRATIC_ATTENUATION, l.decay_quadratic); + if (l.light_type == Light::Cone) { + glLightfv(gl_index, GL_SPOT_DIRECTION, dir); + glLightf(gl_index, GL_SPOT_CUTOFF, l.angle_end / 2.f); + glLightf(gl_index, GL_SPOT_EXPONENT, (1.f - piClamp((l.angle_end - l.angle_start) / (l.angle_end + 0.001f), 0., 1.f)) * 128.f); + } else { + glLightf(gl_index, GL_SPOT_CUTOFF, 180.); + } + +} + + +void GLRendererBase::setupAmbientLight(const QColor & a, bool first_pass) { + GLfloat ambient[] = {0.0f, 0.0f, 0.0f, 1.f}; + if (first_pass) { + ambient[0] = view->ambientColor_.redF(); + ambient[1] = view->ambientColor_.greenF(); + ambient[2] = view->ambientColor_.blueF(); + } + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); +} + + +void GLRendererBase::setupShadersLights(int lights_count) { + /*foreach (QOpenGLShaderProgram * i, view->shaders_ppl) { + i->bind(); + i->setUniformValue("lightsCount", lights_count); + i->setUniformValue("acc_light", lights_count > 0); + //i->setUniformValue("mat", mvm); + }*/ +} + + +#define BIND_TEXTURE(ch, map) if (rp.prev_tex[ch] != mat.map.bitmap_id) { \ + rp.prev_tex[ch] = mat.map.bitmap_id; \ + glActiveTexture(GL_TEXTURE0 + ch); glBindTexture(GL_TEXTURE_2D, mat.map.bitmap_id); \ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, view->feature(QGLView::qglAnisotropicLevel).toInt());} + +void GLRendererBase::setupTextures(ObjectBase & o, GLRendererBase::RenderingParameters & rp, bool first_object) { +} + +#undef BIND_TEXTURE + + +void GLRendererBase::setupLights(int pass, int lights_per_pass) { + /*int light_start, light_end, lmax; + light_start = pass * lights_per_pass; + light_end = qMin((pass + 1) * lights_per_pass, view->lights_.size()); + setupAmbientLight(view->ambientColor_, pass == 0); + if (!view->lights_.isEmpty()) { + setupShadersLights(light_end - light_start); + for (int i = light_start; i < light_end; ++i) + setupLight(*view->lights_[i], i - light_start, GL_LIGHT0 + i - light_start); + lmax = light_start + 8; + for (int i = light_end; i < lmax; ++i) + glDisable(GL_LIGHT0 + i - light_start); + } else { + setupShadersLights(0); + for (int i = 0; i < 8; ++i) + glDisable(GL_LIGHT0 + i); + }*/ +} + + +void GLRendererBase::applyFilteringParameters() { + /*if (view->isFeatureEnabled(QGLView::qglLinearFiltering)) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + }*/ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, view->feature(QGLView::qglAnisotropicLevel).toInt()); +} + + +void GLRendererBase::renderObjects(int pass, int light_pass, void * shaders, bool textures, bool light, bool fog) { + /*RenderingParameters rpl; + rpl.pass = pass; + rpl.light_pass = light_pass; + rpl.shaders = shaders; + rpl.textures = textures; + rpl.light = rpl.prev_light = light; + rpl.fog = rpl.prev_fog = fog; + rpl.view_matrix = rp.view_matrix; + rpl.prev_view_matrix = rp.prev_view_matrix; + rpl.proj_matrix = rp.proj_matrix; + rpl.prev_proj_matrix = rp.prev_proj_matrix; + rpl.cam_offset_matrix = view->camera()->offsetMatrix(); + //qDebug() << "view:" << rp.view_matrix; + for (int i = 0; i < 32; ++i) rpl.prev_tex[i] = 0; + setupTextures(view->objects_, rpl, true); + glSetCapEnabled(GL_TEXTURE_2D, rpl.textures); + glSetCapEnabled(GL_BLEND, pass == ObjectBase::Transparent); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_TEXTURE_CUBE_MAP); + glPushMatrix(); + renderSingleObject(view->objects_, rpl); + glPopMatrix();*/ +} + + +void GLRendererBase::renderSingleObject(ObjectBase & o, RenderingParameters & rpl) { +// if (!o.isInit()) +// o.init(); +// if (!o.isTexturesLoaded()) +// o.loadTextures(); +// if (!o.visible_) return; +// if (rpl.pass == o.pass_) { +// //Material & mat(o.material_); +// QMatrix4x4 curview = rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_, prevview = rpl.prev_view_matrix * rpl.cam_offset_matrix * o.itransform_; +// //setupTextures(o, rpl, false); +// //mat.apply((QOpenGLShaderProgram*)rpl.shaders); +// glSetPolygonMode(o.render_mode != ObjectBase::View ? o.render_mode : (view->rmode != ObjectBase::View ? view->rmode : GL_FILL)); +// glLineWidth(o.line_width > 0.f ? o.line_width : view->lineWidth_); +// glPointSize(o.line_width > 0.f ? o.line_width : view->lineWidth_); +// o.update(); +// /*if (o.pass_ == GLObjectBase::Transparent) { +// glActiveTexture(GL_TEXTURE0 + 3); +// if (mat.reflectivity > 0.f) { +// glEnable(GL_TEXTURE_CUBE_MAP); +// //if (!mat.map_reflection.isEmpty()) mat.map_reflection.bind(); +// //else glDisable(GL_TEXTURE_CUBE_MAP); +// } else glDisable(GL_TEXTURE_CUBE_MAP); +// if (rpl.light_pass > 0) glDisable(GL_TEXTURE_CUBE_MAP); +// GLfloat gm[16], bc[4] = {mat.reflectivity, mat.reflectivity, mat.reflectivity, mat.reflectivity}; +// glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); +// glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); +// glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_CONSTANT); +// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); +// glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, bc); +// glGetFloatv(GL_MODELVIEW_MATRIX, gm); +// glMatrixMode(GL_TEXTURE); +// ///glLoadTransposeMatrixf(gm); +// glScalef(-1., -1., -1.); +// glMatrixMode(GL_MODELVIEW); +// glActiveTexture(GL_TEXTURE0); +// }*/ +// if (rpl.shaders) { +// //qDebug() << o.name() << curview << curview->determinant(); +// //setUniformMatrices((QOpenGLShaderProgram*)rpl.shaders, rpl.proj_matrix, curview, rpl.prev_proj_matrix, prevview); +// } else { +// glMatrixMode(GL_MODELVIEW); +// //setGLMatrix(curview); +// } +// o.draw((QOpenGLShaderProgram*)rpl.shaders); +// } +// foreach (ObjectBase * i, o.children_) +// renderSingleObject(*i, rpl); +} + + +void GLRendererBase::renderShadow(Light * l, QOpenGLShaderProgram * prog, QMatrix4x4 mat) { + Camera cam; + QVector3D wp = l->worldPos(); + cam.setPos(wp); + cam.setAim(wp + (l->worldTransform() * QVector4D(l->direction)).toVector3D()); + cam.setDepthStart(view->camera()->depthStart()); + cam.setDepthEnd(view->camera()->depthEnd()); + cam.setFOV(l->angle_end); + cam.apply(1.); + /*cam.rotateXY(l->angle_end); + QVector3D rdir = l->direction * cos(l->angle_end / 2. * deg2rad); + l->dir0 = cam.direction() - rdir; + cam.rotateXY(-l->angle_end); + cam.rotateZ(l->angle_end); + l->dir1 = cam.direction() - rdir;*/ + //qDebug() << rdir << l->dir0 << l->dir1; + RenderingParameters rpl; + rpl.pass = ObjectBase::Solid; + rpl.shaders = prog; + rpl.textures = rpl.light = rpl.fog = false; + //rpl.view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); + //rpl.proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX); + rpl.cam_offset_matrix = cam.offsetMatrix(); + QMatrix4x4 mbias; + mbias.scale(0.5, 0.5, 0.5); + mbias.translate(1., 1., 1.); + l->shadow_matrix = mbias*rpl.proj_matrix*rpl.view_matrix*rpl.cam_offset_matrix*mat;//;// * mbias; + //qDebug() << mbias; + //glPushMatrix(); + //renderSingleShadow(view->objects_, rpl); + //glPopMatrix(); +} + + +void GLRendererBase::renderSingleShadow(ObjectBase & o, RenderingParameters & rpl) { +// if (!o.isInit()) +// o.init(); +// if (!o.visible_) return; +// if (rpl.shaders) { +// //qDebug() << o.name() << curview << curview->determinant(); +// //setUniformMatrices((QOpenGLShaderProgram*)rpl.shaders, rpl.proj_matrix, rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_); +// } else { +// glMatrixMode(GL_MODELVIEW); +// //setGLMatrix(rpl.view_matrix * rpl.cam_offset_matrix * o.itransform_); +// } +// glPolygonMode(GL_FRONT_AND_BACK, o.render_mode != ObjectBase::View ? o.render_mode : (view->rmode != ObjectBase::View ? view->rmode : GL_FILL)); +// glLineWidth(o.line_width > 0.f ? o.line_width : view->lineWidth_); +// glPointSize(o.line_width > 0.f ? o.line_width : view->lineWidth_); +// o.draw((QOpenGLShaderProgram*)rpl.shaders, true); +// foreach (ObjectBase * i, o.children_) +// renderSingleShadow(*i, rpl); +} + + + + +GLRendererBase::RenderingParameters::RenderingParameters() { + shaders = nullptr; + cur_shader = nullptr; +} + + +void GLRendererBase::RenderingParameters::prepare() { + //proj_matrix = getGLMatrix(GL_PROJECTION_MATRIX); + //view_matrix = getGLMatrix(GL_MODELVIEW_MATRIX); + viewproj_matrix = proj_matrix * view_matrix; + normal_matrix = view_matrix.normalMatrix(); + proj_matrix_i = proj_matrix.inverted(); + view_matrix_i = view_matrix.inverted(); + viewproj_matrix_i = viewproj_matrix.inverted(); +} + + +void GLRendererBase::RenderingParameters::setUniform(QOpenGLShaderProgram * prog) { + if (!prog) return; + prog->setUniformValue("qgl_ModelViewMatrix", view_matrix); + prog->setUniformValue("qgl_ProjectionMatrix", proj_matrix); + prog->setUniformValue("qgl_ModelViewProjectionMatrix", viewproj_matrix); + prog->setUniformValue("qgl_NormalMatrix", normal_matrix); + prog->setUniformValue("qgl_ModelViewMatrixInverse", view_matrix_i); + prog->setUniformValue("qgl_ProjectionMatrixInverse", proj_matrix_i); + prog->setUniformValue("qgl_ModelViewProjectionMatrixInverse", viewproj_matrix_i); + prog->setUniformValue("qgl_ModelViewMatrixTranspose", view_matrix.transposed()); + prog->setUniformValue("qgl_ProjectionMatrixTranspose", proj_matrix.transposed()); + prog->setUniformValue("qgl_ModelViewProjectionMatrixTranspose", viewproj_matrix.transposed()); + prog->setUniformValue("qgl_ModelViewMatrixInverseTranspose", view_matrix_i.transposed()); + prog->setUniformValue("qgl_ProjectionMatrixInverseTranspose", proj_matrix_i.transposed()); + prog->setUniformValue("qgl_ModelViewProjectionMatrixInverseTranspose", viewproj_matrix_i.transposed()); +} diff --git a/qglengine/glrendererbase.h b/qglengine/glrendererbase.h new file mode 100644 index 0000000..db06c27 --- /dev/null +++ b/qglengine/glrendererbase.h @@ -0,0 +1,80 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLRENDERERBASE_H +#define GLRENDERERBASE_H + +#include "glcamera.h" +#include "glshaders.h" + +class GLRendererBase: public QObject , protected QOpenGLExtraFunctions +{ + friend class QGLView; + Q_OBJECT +public: + GLRendererBase(QGLView * view_); + virtual void prepareScene() {;} + virtual void renderScene() = 0; + + struct RenderingParameters { + RenderingParameters(); + void prepare(); + void setUniform(QOpenGLShaderProgram * prog); + int pass; + int light_pass; + bool light; + bool fog; + bool textures; + bool prev_light; + bool prev_fog; + GLuint prev_tex[32]; + void * shaders; + QMatrix4x4 view_matrix, view_matrix_i, prev_view_matrix; + QMatrix4x4 proj_matrix, proj_matrix_i, prev_proj_matrix; + QMatrix4x4 viewproj_matrix, viewproj_matrix_i; + QMatrix3x3 normal_matrix; + QMatrix4x4 cam_offset_matrix; + QOpenGLShaderProgram * cur_shader; + }; + + RenderingParameters rp; + +protected: + virtual void setupLight(const Light & l, int inpass_index, int gl_index); + virtual void setupAmbientLight(const QColor & a, bool first_pass); + virtual void setupShadersLights(int lights_count); + virtual void setupTextures(ObjectBase & object, GLRendererBase::RenderingParameters & rp, bool first_object = false); + virtual void setupShadersTextures(ObjectBase & object, GLRendererBase::RenderingParameters & rp) {} + virtual void reloadShaders() {} + virtual void init(int width, int height) {} + virtual void resize(int width, int height) {} + + void setupLights(int pass, int lights_per_pass); + inline void applyFilteringParameters(); + void renderObjects(int pass, int light_pass, void * shaders = 0, bool textures = true, bool light = true, bool fog = true); + void renderSingleObject(ObjectBase & o, RenderingParameters & rpl); + void renderShadow(Light * l, QOpenGLShaderProgram * prog = 0, QMatrix4x4 mat = QMatrix4x4()); + void renderSingleShadow(ObjectBase & o, RenderingParameters & rpl); + + QGLView * view; + QImage white_image, violent_image; + GLuint white_image_id, violent_image_id; + +}; + +#endif // GLRENDERERBASE_H diff --git a/qglengine/glscene.cpp b/qglengine/glscene.cpp new file mode 100644 index 0000000..18f4c9c --- /dev/null +++ b/qglengine/glscene.cpp @@ -0,0 +1,471 @@ +/* + GLObjectBase & Light + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glscene.h" +#include "glcamera.h" +#include "glmesh.h" +#include "qglview.h" +#include + + +Scene::Scene() { + root_ = new ObjectBase(); + root_->setScene(this); + tree_changed = mat_changed = lights_changed = true; + destroying = false; + need_reload_materials = tree_struct_changed = true; + sel_mode_ = smSingleSelection; +} + + +Scene::~Scene() { + destroying = true; + destroy(); + 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) { + QList 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) { + destroy(); + 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::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->selected_changed = true; + selectionChanged(); +} + + +QString Scene::uniqueName(QString n, const QSet & names) { + if (!names.contains(n)) + return n; + QString num; + while (!n.isEmpty()) { + if (n.right(1)[0].isDigit()) { + num.push_front(n.right(1)); + n.chop(1); + } else break; + } + if (!n.endsWith('_')) n += '_'; + int in = num.toInt() + 1; + QString nn = n + QString::number(in).rightJustified(3, '0'); + while (names.contains(nn)) + nn = n + QString::number(++in).rightJustified(3, '0'); + return nn; +} + + + +void Scene::removeObject(ObjectBase * o, bool inChildren) { + if (destroying) return; + o->setScene(nullptr); + setObjectMeshChanged(o); + if (inChildren) + removeObjectInternal(o, root_); + else + root_->removeChild(o); + __objectDeleted(o); + setTreeStructChanged(); +} + + +void Scene::removeObject(ObjectBase & o, bool inChildren) { + if (destroying) return; + removeObject(&o, inChildren); +} + + +void Scene::clearObjects(bool deleteAll) { + root_->clearChildren(deleteAll); + 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(); + } + foreach (Mesh * m, geometries) + m->selected_changed = true; + emitSelectionChanged(); +} + + +void Scene::selectObjects(QList ol, bool add_to_selection) { + if (!add_to_selection || (sel_mode_ == smSingleSelection)) clearSelection(); + foreach (ObjectBase * o, ol) { + if (!o) continue; + o->setSelected(true); + } + gatherSelection(); + foreach (Mesh * m, geometries) + m->selected_changed = true; + emitSelectionChanged(); +} + + +void Scene::clearSelection() { + selected_.clear(); + QList ol = root_->children(true); + foreach (ObjectBase * o, ol) { + o->selected_ = false; + } + emitSelectionChanged(); +} + + +QList Scene::selectedObjects(bool top_only) const { + return top_only ? selected_top : selected_; +} + + +ObjectBase * Scene::selectedObject() const { + if (selected_.isEmpty()) return 0; + return selected_[0]; +} + + +const Box3D & Scene::boundingBox() const { + root_->calculateBoundingBox(); + return root_->boundingBox(); +} + + +Material * Scene::newMaterial() { + materials << new Material(); + makeMaterialsUniqueNames(); + mat_changed = true; + return materials.back(); +} + + +void Scene::removeMaterial(Material * m) { + if (!m || !materials.contains(m)) return; + QList ol = root_->children(true); + foreach (ObjectBase * o, ol) + if (o->material_ == m) + o->setMaterial(0); + materials.removeAll(m); + materials_used.remove(m); + changed_materials.removeAll(m); + mat_changed = true; +} + + +void Scene::makeMaterialsUniqueNames() { + QSet names; + foreach (Material * m, materials) { + if (m->name.isEmpty()) m->name = "default_000"; + m->name = uniqueName(m->name, names); + names << m->name; + } +} + + +QList 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(); + QList ol = root_->children(true); + foreach (ObjectBase * o, ol) + if (o->selected_) + selected_ << o; +} + + +void Scene::attachObject(ObjectBase * o) { + if (!o) return; + o->setScene(this); + if (o->mesh()) { // search suitable mesh in this scene + uint ohash = o->mesh()->hash(); + bool need_new = true; + foreach (Mesh * m, geometries) { + if (m == o->mesh()) { // already exists by ptr + need_new = false; + setObjectMeshChanged(o); + break; + } + if (m->hash() == ohash) { // already exists by hash + need_new = false; + o->setMesh(m); + break; + } + } + if (need_new) { // need to clone mesh and add to scene + Mesh * nmesh = o->mesh()->clone(); + o->setMesh(nmesh); + geometries << nmesh; + } + } + 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; + } + } +} + + +void Scene::setTreeChanged() { + if (destroying) return; + tree_changed = true; + foreach (Mesh * m, geometries) + m->selected_changed = true; + gatherSelection(); +} + + +void Scene::setTreeStructChanged() { + tree_struct_changed = true; +} + + +void Scene::setObjectMeshChanged(ObjectBase * o) { + if (o) o->setMeshChanged(); +} + + +void Scene::prepareTree(ObjectBase * o) { + foreach (ObjectBase * co, o->children_) { + if (co->isHidden()) continue; + switch (co->type_) { + case ObjectBase::glLight: + lights_used << globject_cast(co); + break; + case ObjectBase::glMesh: + if (co->mesh()) { + geometries_used[co->mesh()] << co; + co->mesh()->objects_changed = co->mesh()->selected_changed = true; + } + break; + default: break; + } + prepareTree(co); + } +} + + +bool Scene::prepare() { + changed_materials.clear(); + foreach (Material * m, materials) { + if (m->_changed) { + need_reload_materials = true; + changed_materials << m; + } + } + + QList aol; + if (!tree_changed && !mat_changed) return false; + aol = root_->children(true); + if (tree_changed) { + tree_changed = false; + lights_changed = true; + if (tree_struct_changed) { + tree_struct_changed = false; + QMetaObject::invokeMethod(this, "treeChanged", Qt::QueuedConnection); + } + geometries_used.clear(); + lights_used.clear(); + prepareTree(root_); + } + if (mat_changed) { + mat_changed = false; + materials_used.clear(); + foreach (ObjectBase * o, aol) { + Material * m = o->material(); + if (!m) continue; + materials_used << m; + } + } + return true; +} + + +void Scene::destroy() { + root_->clearChildren(true); + qDeleteAll(geometries); + qDeleteAll(materials); + geometries.clear(); + materials.clear(); + emit __destroyed(); + emit treeChanged(); +} + + +QDataStream & operator <<(QDataStream & s, const Scene * p) { + ChunkStream cs; + //qDebug() << "place" << p->name() << "..."; + QVector geom_ind, mat_ind; + QList cl = p->root_->children(true); + geom_ind.reserve(cl.size()); + mat_ind.reserve(cl.size()); + foreach (ObjectBase * c, cl) { + geom_ind << p->geometries.indexOf(c->mesh()); + mat_ind << p->materials.indexOf(c->material()); + } + cs.add(1, p->name_).add(10, p->geometries).add(11, p->materials) + .add(20, p->root_).add(21, geom_ind).add(22, mat_ind); + s << qCompress(cs.data()); + //s << cs.data(); + return s; +} +QDataStream & operator >>(QDataStream & s, Scene *& p) { + p = new Scene(); + //ChunkStream cs(s); + + QByteArray ba; + s >> ba; + ba = qUncompress(ba); + ChunkStream cs(ba); + + QVector geom_ind, mat_ind; + while (!cs.atEnd()) { + switch (cs.read()) { + case 1 : cs.get(p->name_); break; + case 10: cs.get(p->geometries); break; + case 11: cs.get(p->materials); break; + case 20: cs.get(p->root_); p->root_->setScene(p); break; + case 21: cs.get(geom_ind); break; + case 22: cs.get(mat_ind); break; + } + } + p->makeMaterialsUniqueNames(); + QList cl = p->root_->children(true); + int cnt = qMin(qMin(cl.size(), geom_ind.size()), mat_ind.size()); + for (int i = 0; i < cnt; ++i) { + ObjectBase * c(cl[i]); + if (geom_ind[i] >= 0) c->mesh_ = p->geometries[geom_ind[i]]; + if (mat_ind [i] >= 0) c->material_ = p->materials [mat_ind [i]]; + } + return s; +} diff --git a/qglengine/glscene.h b/qglengine/glscene.h new file mode 100644 index 0000000..8a4b556 --- /dev/null +++ b/qglengine/glscene.h @@ -0,0 +1,139 @@ +/* + GLObjectBase & Light + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLSCENE_H +#define GLSCENE_H + +#include "gltypes.h" + + +class Scene: public QObject { + Q_OBJECT + friend class QGLView; + friend class RendererBase; + friend class Renderer; + friend class RendererMaterial; + friend class RendererService; + friend class ObjectBase; + friend class Light; + friend QDataStream & operator <<(QDataStream & s, const Scene * p); + friend QDataStream & operator >>(QDataStream & s, Scene *& p); +public: + explicit Scene(); + virtual ~Scene(); + + enum SelectionMode { + smNoSelection, + smSingleSelection, + smMultiSelection, + }; + + Q_ENUMS(SelectionMode) + + QString name() const {return name_;} + void setName(const QString & name) {name_ = name;} + + bool prepare(); + + Scene * clone(); + + /// Add object \"o\" to scene and take its ownership + /// All materials and geometries used by \"o\" tree + /// copied into this scene + void addObject(ObjectBase * o); + + void addScene(const Scene * s); + void assignFrom(const Scene * s); + + int objectsCount(bool all = false); + QList 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(QList ol, bool add_to_selection = false); + void clearSelection(); + QList selectedObjects(bool top_only = false) const; + ObjectBase * selectedObject() const; + + const Box3D & boundingBox() const; + + QVector getMaterials() const {return materials;} + Material * newMaterial(); + void removeMaterial(Material * m); + void makeMaterialsUniqueNames(); + + void removeLight(Light * l); + + void dump(); + void destroy(); + +protected: + void prepareTree(ObjectBase * o); + void gatherSelection(); + void objectsCountInternal(int * cnt, ObjectBase * where); + void removeObjectInternal(ObjectBase * o, ObjectBase * where); + void emitSelectionChanged(); + QString uniqueName(QString n, const QSet & names); + + void attachObject(ObjectBase * o); + void setTreeChanged(); + void setTreeStructChanged(); + void setMaterialsChanged() {mat_changed = true;} + void setLightsChanged() {lights_changed = true;} + void setObjectMeshChanged(ObjectBase * o); + + + QString name_; + ObjectBase * root_; + bool tree_changed, mat_changed, lights_changed, destroying; + bool need_reload_materials, tree_struct_changed; + QVector mat_map_changed; + + QVector geometries; + QVector materials; + + QMap > geometries_used; + QSet materials_used; + QList lights_used; + QVector changed_materials; + + SelectionMode sel_mode_; + QList selected_, selected_top; + +protected slots: + + +signals: + void __objectDeleted(ObjectBase * o); + void __destroyed(); + void treeChanged(); + //void treeStructChanged(); + void selectionChanged(); + +}; + + +QDataStream & operator <<(QDataStream & s, const Scene * p); +QDataStream & operator >>(QDataStream & s, Scene *& p); + +#endif // GLSCENE_H diff --git a/qglengine/glshaders.cpp b/qglengine/glshaders.cpp new file mode 100644 index 0000000..d256923 --- /dev/null +++ b/qglengine/glshaders.cpp @@ -0,0 +1,182 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "gltypes.h" +#include "qglview.h" +#include "glshaders.h" +#include "glshaders_headers.h" + +using namespace QGLEngineShaders; + + +bool addShader(QOpenGLShaderProgram * prog, QOpenGLShader::ShaderType type, QString & content, const QString & file) { + if (type == 0 || content.isEmpty()) { + content.clear(); + return true; + } + //qDebug() << "[QGLView] Shader" << file << "found" << (QOpenGLShader::ShaderTypeBit)(int)type << "section ..."; + switch (type) { + case QOpenGLShader::Fragment: + content.prepend(qgl_fragment_head); + content.prepend(qgl_uniform); + content.prepend(qgl_structs); + break; + case QOpenGLShader::Vertex : + content.prepend(qgl_vertex_head ); + break; + case QOpenGLShader::Geometry: + content.prepend(qgl_geometry_head); + break; + } + content.prepend(qgl_common_head); + bool ret = prog->addShaderFromSourceCode(type, content.toLatin1()); + if (!ret) qDebug() << "[QGLView] Shader" << file << "Compile error:\n" << prog->log(); + content.clear(); + return ret; +} + + +bool QGLEngineShaders::loadShadersMulti(QOpenGLShaderProgram *& prog, const QString & file) { + if (!prog) + prog = new QOpenGLShaderProgram(); + prog->removeAllShaders(); + QFile f(file); + if (!f.open(QIODevice::ReadOnly)) return false; + QTextStream ts(&f); + QString cur_shader, line, pl; + QOpenGLShader::ShaderType type = 0; + while (!ts.atEnd()) { + line = ts.readLine(); + pl = line.trimmed().remove(' ').remove('\t').mid(2).toLower(); + pl.chop(2); + if (pl == "vertex" || pl == "vert") { + if (!addShader(prog, type, cur_shader, file)) return false; + type = QOpenGLShader::Vertex; + continue; + } + if (pl == "fragment" || pl == "frag") { + if (!addShader(prog, type, cur_shader, file)) return false; + type = QOpenGLShader::Fragment; + continue; + } + if (pl == "geometry" || pl == "geom") { + if (!addShader(prog, type, cur_shader, file)) return false; + type = QOpenGLShader::Geometry; + continue; + } + if (pl == "tessellation_control") { + if (!addShader(prog, type, cur_shader, file)) return false; + type = QOpenGLShader::TessellationControl; + continue; + } + if (pl == "tessellation_evaluation") { + if (!addShader(prog, type, cur_shader, file)) return false; + type = QOpenGLShader::TessellationEvaluation; + continue; + } + cur_shader.append("\n"); + cur_shader.append(line); + } + if (!addShader(prog, type, cur_shader, file)) return false; + /// WARNING + /*prog->bindAttributeLocation("qgl_Vertex" , 1 ); + prog->bindAttributeLocation("qgl_Normal" , 2 ); + prog->bindAttributeLocation("qgl_Tangent" , 3 ); + prog->bindAttributeLocation("qgl_Bitangent" , 4 ); + prog->bindAttributeLocation("qgl_Texture" , 5 ); + prog->bindAttributeLocation("qgl_Material" , 6 ); + prog->bindAttributeLocation("qgl_Color" , 7 ); + prog->bindAttributeLocation("qgl_ModelViewMatrix" , 8 ); + prog->bindAttributeLocation("qgl_ModelViewProjectionMatrix", 12); + prog->bindAttributeLocation("qgl_NormalMatrix" , 16);*/ + if (!prog->link()) { + qDebug() << "[QGLView] Shader" << file << "Link error:\n" << prog->log(); + return false; + } + qDebug() << "[QGLView] Shader" << file << "ok"; + return true; +} + + +void QGLEngineShaders::setUniformMatrices(QOpenGLShaderProgram * prog, QMatrix4x4 proj, QMatrix4x4 view, QMatrix4x4 prevproj, QMatrix4x4 prevview) { + if (!prog) return; + if (!prog->isLinked()) return; + QMatrix4x4 mvpm = proj * view; + QMatrix4x4 pmvpm = prevproj * prevview; + QMatrix3x3 nm = view.normalMatrix(); + //nm.in; + prog->setUniformValue("qgl_ModelViewMatrix", view); + prog->setUniformValue("qgl_ProjectionMatrix", proj); + prog->setUniformValue("prev_ModelViewProjectioMatrix", pmvpm); + prog->setUniformValue("prev_ModelViewMatrix", prevview); + prog->setUniformValue("qgl_ModelViewProjectionMatrix", mvpm); + prog->setUniformValue("qgl_NormalMatrix", nm); + //prog->setUniformValue("qgl_BumpMatrix", nm.); + prog->setUniformValue("qgl_ModelViewMatrixTranspose", view.transposed()); + prog->setUniformValue("qgl_ProjectionMatrixTranspose", proj.transposed()); + prog->setUniformValue("qgl_ModelViewProjectionMatrixTranspose", mvpm.transposed()); +} + + +void QGLEngineShaders::setUniformLights(QOpenGLShaderProgram * prog, const QVector & lights, const QMatrix4x4 & mat, int shadow_start) { + for (int i = 0; i < lights.size(); ++i) + ;//setUniformLight(prog, lights[i], QString("qgl_Light[%1]").arg(i), mat, shadow_start + i); +} +/* +" vec3 position;\n" +" vec3 direction;\n" +" vec4 color;\n" +" float intensity;\n" +" float startAngle;\n" +" float endAngle;\n" +" float constantAttenuation;\n" +" float linearAttenuation;\n" +" float quadraticAttenuation;\n" +" sampler2DShadow shadow;\n" +" mat4 shadowMatrix;\n" +*/ +void QGLEngineShaders::setUniformLight(QOpenGLShaderProgram * prog, Light * light, QString ulightn, const QMatrix4x4 & mat, int shadow) { + if (!prog) return; + if (!prog->isLinked()) return; + QMatrix4x4 m = mat * light->worldTransform(); + QVector4D pos(0, 0, 0, 1.), dir(light->direction, 1);//, dir0(light->dir0), dir1(light->dir1); + pos = m * pos; + dir = ((m * dir) - pos).normalized(); + float ang_start = light->angle_start / 2.f, ang_end = light->angle_end / 2.f; + if (light->light_type == Light::Omni) + ang_start = ang_end = 180.; + //qDebug() << "light" << light->name() << ulightn << pos; + prog->setUniformValue((ulightn + ".position").toLatin1().constData(), pos); + prog->setUniformValue((ulightn + ".direction").toLatin1().constData(), dir); + prog->setUniformValue((ulightn + ".intensity").toLatin1().constData(), GLfloat(light->intensity)); + prog->setUniformValue((ulightn + ".startAngle").toLatin1().constData(), GLfloat(ang_start)); + prog->setUniformValue((ulightn + ".startAngleCos").toLatin1().constData(), GLfloat(cosf(ang_start * deg2rad))); + prog->setUniformValue((ulightn + ".endAngle").toLatin1().constData(), GLfloat(ang_end)); + prog->setUniformValue((ulightn + ".endAngleCos").toLatin1().constData(), GLfloat(cosf(ang_end * deg2rad))); + //prog->setUniformValue((ulightn + ".color").toLatin1().constData(), light->color()); + prog->setUniformValue((ulightn + ".constantAttenuation").toLatin1().constData(), GLfloat(light->decay_const)); + prog->setUniformValue((ulightn + ".linearAttenuation").toLatin1().constData(), GLfloat(light->decay_linear)); + prog->setUniformValue((ulightn + ".quadraticAttenuation").toLatin1().constData(), GLfloat(light->decay_quadratic)); + prog->setUniformValue((ulightn + ".shadow").toLatin1().constData(), shadow); + prog->setUniformValue((ulightn + ".shadowColor").toLatin1().constData(), shadow); + prog->setUniformValue((ulightn + ".shadowMatrix").toLatin1().constData(), light->shadow_matrix); + //qDebug() << light->shadow_matrix; + //prog->setUniformValue((ulightn + ".shadowDir0").toLatin1().constData(), (mat * dir0)); + //prog->setUniformValue((ulightn + ".shadowDir1").toLatin1().constData(), (mat * dir1)); + //qDebug() << light->direction << light->dir0 << light->dir1; +} diff --git a/qglengine/glshaders.h b/qglengine/glshaders.h new file mode 100644 index 0000000..cf3ac8f --- /dev/null +++ b/qglengine/glshaders.h @@ -0,0 +1,34 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLSHADERS_H +#define GLSHADERS_H + +#include "gltypes.h" + +namespace QGLEngineShaders { + +bool loadShadersMulti(QOpenGLShaderProgram *& prog, const QString & file); + +void setUniformMatrices(QOpenGLShaderProgram * prog, QMatrix4x4 proj, QMatrix4x4 view, QMatrix4x4 prevproj = QMatrix4x4(), QMatrix4x4 prevview = QMatrix4x4()); +void setUniformLights(QOpenGLShaderProgram * prog, const QVector & lights, const QMatrix4x4 & mat, int shadow_start); +void setUniformLight(QOpenGLShaderProgram * prog, Light * light, QString ulightn, const QMatrix4x4 & mat = QMatrix4x4(), int shadow = 0); + +} + +#endif // GLSHADERS_H diff --git a/qglengine/glshaders_headers.h b/qglengine/glshaders_headers.h new file mode 100644 index 0000000..1698297 --- /dev/null +++ b/qglengine/glshaders_headers.h @@ -0,0 +1,133 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLSHADERS_HEADERS_H +#define GLSHADERS_HEADERS_H + +namespace QGLEngineShaders { + +const int max_materials = 128; +const int max_lights = 64 ; + +const char qgl_common_head[] = + "#version 400 core\n" + //"#extension GL_EXT_texture_aray: require\n" + ""; + +const char qgl_vertex_head[] = + "layout(location = 1 ) in vec3 qgl_Vertex ;\n" + "layout(location = 2 ) in vec3 qgl_Normal ;\n" + "layout(location = 3 ) in vec3 qgl_Tangent ;\n" + "layout(location = 4 ) in vec3 qgl_Bitangent ;\n" + "layout(location = 5 ) in vec2 qgl_Texture ;\n" + "layout(location = 6 ) in uint qgl_Material ;\n" + "layout(location = 7 ) in uint qgl_ObjectSelected;\n" + "layout(location = 8 ) in uint qgl_ObjectID ;\n" + "layout(location = 9 ) in vec4 qgl_ObjectColor ;\n" + "layout(location = 10) in mat4 qgl_ModelMatrix ;\n" + "out vec2 qgl_FragTexture;\n" + "flat out uint qgl_MaterialIndex;\n" + "uniform mat4 qgl_ViewMatrix;\n" + "uniform mat4 qgl_ViewProjMatrix;\n" + "mat3 qgl_getNormalMatrix() {return mat3(qgl_ViewMatrix * qgl_ModelMatrix);}\n" + "vec4 qgl_ftransform() {return qgl_ViewProjMatrix * (qgl_ModelMatrix * vec4(qgl_Vertex, 1));}\n" + ""; + +const char qgl_fragment_head[] = + "in vec2 qgl_FragTexture;\n" + "flat in uint qgl_MaterialIndex;\n" + "out vec4 qgl_FragData[gl_MaxDrawBuffers];\n" + "vec4 qgl_materialTexture(uint type, vec2 coord, vec4 tex_shift) {\n" + " coord *= qgl_material[qgl_MaterialIndex].map[type].scale;\n" + " vec4 t = texture(qgl_texture_array[qgl_material[qgl_MaterialIndex].map[type].array_index],\n" + " vec3(coord, qgl_material[qgl_MaterialIndex].map[type].map_index));\n" + " t += tex_shift;\n" + " t = t * qgl_material[qgl_MaterialIndex].map[type].amount + qgl_material[qgl_MaterialIndex].map[type].offset;\n" + " return t;\n" + "}\n" + "#define qgl_FragColor qgl_FragData[0]\n" + ""; + +const char qgl_geometry_head[] = + ""; + +const char qgl_structs[] = + "#define QGL_MAPS_COUNT 6\n" + "#define QGL_MAP_DIFFUSE 0\n" + "#define QGL_MAP_NORMAL 1\n" + "#define QGL_MAP_SPECULAR 2\n" + "#define QGL_MAP_ROUGHNESS 3\n" + "#define QGL_MAP_EMISSION 4\n" + "#define QGL_MAP_RELIEF 5\n" + "#define QGL_TEXTURE_ARRAY_EMPTY 0\n" + "#define QGL_TEXTURE_ARRAY_MAPS 1\n" + "struct QGLMap {\n" + " float offset;\n" + " float amount;\n" + " vec2 scale;\n" + " uint array_index;\n" + " uint map_index;\n" + "};\n" + "struct QGLMaterial {\n" + " vec4 color_diffuse;\n" + " vec4 color_specular;\n" + " vec4 color_emission;\n" + " float transparency;\n" + " float reflectivity;\n" + " float iof;\n" + " float dispersion;\n" + " QGLMap map[QGL_MAPS_COUNT];\n" + "};\n" + "struct QGLLightParameter {\n" + " vec4 color;\n" + " float intensity;\n" + " float startAngle;\n" + " float startAngleCos;\n" + " float endAngle;\n" + " float endAngleCos;\n" + " float constantAttenuation;\n" + " float linearAttenuation;\n" + " float quadraticAttenuation;\n" + //" sampler2DShadow shadow;\n" + //" sampler2D shadowColor\n" + //" mat4 shadowMatrix;\n" + //" vec4 shadowDir0;\n" + //" vec4 shadowDir1;\n" + "};\n" + "struct QGLLightPosition {\n" + " vec4 position;\n" + " vec4 direction;\n" + "};\n" + ""; + +const char qgl_uniform[] = + "layout (std140) uniform QGLMaterialData {\n" + " QGLMaterial qgl_material[128];\n" + "};\n" + "layout (std140) uniform QGLLightParameterData {\n" + " QGLLightParameter qgl_light_parameter[64];\n" + "};\n" + "layout (std140) uniform QGLLightPositionData {\n" + " QGLLightPosition qgl_light_position[64];\n" + "};\n" + "uniform sampler2DArray qgl_texture_array[2];\n" + ""; + +} + +#endif // GLSHADERS_HEADERS_H diff --git a/qglengine/glshaders_types.cpp b/qglengine/glshaders_types.cpp new file mode 100644 index 0000000..4920afc --- /dev/null +++ b/qglengine/glshaders_types.cpp @@ -0,0 +1,41 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "glshaders_types.h" + + +QGLEngineShaders::QGLMap::QGLMap() { + offset = 0.; + amount = 1.; + scale = QVector2D(1., 1.); + array_index = map_index = 0; +} + + +QGLEngineShaders::QGLMaterial::QGLMaterial() { + color_diffuse = QVector4D(.5, .5, .5, 0.); + color_specular = QVector4D(.5, .5, .5, 0.); + color_emission = QVector4D(0., 0., 0., 0.); + transparency = 0.; + reflectivity = 0.; + iof = 0.; + dispersion = 0.; + map[mtNormal].map_index = emrBlue; + map[mtRoughness].amount = 0.75; +} diff --git a/qglengine/glshaders_types.h b/qglengine/glshaders_types.h new file mode 100644 index 0000000..83a8c12 --- /dev/null +++ b/qglengine/glshaders_types.h @@ -0,0 +1,148 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLSHADERS_TYPES_H +#define GLSHADERS_TYPES_H + +#include "gltypes.h" + +namespace QGLEngineShaders { + + +/// VBO + +// geometry +const GLsizei pos_offset = 0; +const GLsizei normal_offset = sizeof(QVector3D) + pos_offset ; +const GLsizei tangent_offset = sizeof(QVector3D) + normal_offset ; +const GLsizei bitangent_offset = sizeof(QVector3D) + tangent_offset ; +const GLsizei tex_offset = sizeof(QVector3D) + bitangent_offset; + +// object +const GLsizei material_offset = 0; +const GLsizei object_id_offset = sizeof(GLuint ) + material_offset ; +const GLsizei color_offset = sizeof(GLuint ) + object_id_offset ; +const GLsizei modelmatrix_offset = sizeof(QVector4D) + color_offset; + +const GLsizei is_selected_offset = 0; + +const GLuint pos_loc = 1 ; // qgl_Vertex +const GLuint normal_loc = 2 ; // qgl_Normal +const GLuint tangent_loc = 3 ; // qgl_Tangent +const GLuint bitangent_loc = 4 ; // qgl_Bitangent +const GLuint tex_loc = 5 ; // qgl_Texture + +const GLuint material_loc = 6 ; // qgl_Material +const GLuint object_id_loc = 8 ; // qgl_ObjectID +const GLuint color_loc = 9 ; // qgl_ObjectColor +const GLuint modelmatrix_loc = 10; // qgl_ModelViewProjectionMatrix + +const GLuint is_selected_loc = 7 ; // qgl_ObjectSelected + +#pragma pack(push, 1) +struct Vertex { + QVector3D pos; + QVector3D normal; + QVector3D tangent; + QVector3D bitangent; + QVector2D tex; +}; +struct Object { + Object() { + material = object_id = 0; + color = QVector4D(1,1,1,1); + QMatrix4x4().copyDataTo(modelmatrix); + } + GLuint material; + GLuint object_id; + QVector4D color; + GLfloat modelmatrix[16]; +}; +#pragma pack(pop) + + +/// UBO + +enum BindingPoints { + bpMaterials, + bpLightParameters, + bpLightPositions, +}; + +enum MapType { + mtDiffuse = 0, + mtNormal = 1, + mtSpecular = 2, + mtRoughness = 3, + mtEmission = 4, + mtRelief = 5, +}; +enum TextureArrayRole { + tarEmpty = 0, + tarMaps = 1, +}; +enum EmptyMapRole { + emrWhite = 0, + emrBlue = 1, +}; +#define QGL_MAPS_COUNT 6 +#pragma pack(push, 1) +struct QGLMap { + QGLMap(); + GLfloat offset; + GLfloat amount; + QVector2D scale; + GLuint array_index; + GLuint map_index; + GLfloat __res_2[2]; +}; +struct QGLMaterial { + QGLMaterial(); + QVector4D color_diffuse; + QVector4D color_specular; + QVector4D color_emission; + GLfloat transparency; + GLfloat reflectivity; + GLfloat iof; + GLfloat dispersion; + QGLMap map[QGL_MAPS_COUNT]; +}; +struct QGLLightParameter { + QVector4D color; + //QVector4D shadowColor; + GLfloat intensity; + GLfloat startAngle; + GLfloat startAngleCos; + GLfloat endAngle; + GLfloat endAngleCos; + GLfloat constantAttenuation; + GLfloat linearAttenuation; + GLfloat quadraticAttenuation; + //GLfloat shadow; + //GLfloat shadowMatrix[16]; +}; +struct QGLLightPosition { + QVector4D position; + QVector4D direction; +}; +#pragma pack(pop) + + +} + +#endif // GLSHADERS_TYPES_H diff --git a/qglengine/gltexture_manager.cpp b/qglengine/gltexture_manager.cpp new file mode 100644 index 0000000..fdb8ecd --- /dev/null +++ b/qglengine/gltexture_manager.cpp @@ -0,0 +1,201 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "gltexture_manager.h" +#include "gltypes.h" + +QStringList TextureManager::search_pathes("."); + + +QVector3D colorVector(QRgb c) { + return QVector3D(((uchar*)(&c))[0] / 255.f, ((uchar*)(&c))[1] / 255.f, ((uchar*)(&c))[2] / 255.f); +} + + +QString TextureManager::findFile(const QString & path) { + return ::findFile(path, search_pathes); +} + + +GLuint TextureManager::loadTexture(const QString & path, bool ownership, bool bump) { + QString p = findFile(path); + if (p.isEmpty()) return 0; + int tid = textureID(p, bump); + if (tid > 0) { + //qDebug() << "[TextureManager] Found" << path << "as" << tid; + return tid; + } + QImage image(p); + if (bump) convertToNormal(image); + //qDebug() << p << image.width() << image.height() << image.format() << bump; + GLuint tid_ = tid; + createGLTexture(f, tid_, image);///currentQGLView->bindTexture(image, GL_TEXTURE_2D); + tid = tid_; + if (tid == 0) { + qDebug() << "[TextureManager] Can`t load" << p; + return tid; + } + qDebug() << "[TextureManager] Loaded" << p << "as" << tid; + if (ownership) { + tex_ids[bump ? 1 : 0].insert(p, tid); + tex_im [bump ? 1 : 0].insert(p, image); + } + return tid; +} + + +GLuint TextureManager::loadTexture(const QImage & im, bool ownership, bool bump) { + if (im.isNull()) return 0; + QImage image(im); + if (bump) convertToNormal(image); + GLuint tid = 0; + createGLTexture(f, tid, im);///currentQGLView->bindTexture(image, GL_TEXTURE_2D); + if (tid == 0) { + qDebug() << "[TextureManager] Can`t load image"; + return tid; + } + //qDebug() << "[TextureManager] Loaded image as" << tid; + return tid; +} + + +QImage TextureManager::loadTextureImage(const QString & path, bool bump) { + QString p = findFile(path); + if (p.isEmpty()) return QImage(); + QImage ret = tex_im[bump ? 1 : 0].value(p); + if (!ret.isNull()) return ret; + ret = QImage(p); + if (bump) convertToNormal(ret); + tex_im[bump ? 1 : 0].insert(p, ret); + return ret; +} + + +void TextureManager::reloadTexture(GLuint tid, const QString & path) { + QString p = findFile(path); + if (p.isEmpty() || (tid == 0)) return; + QImage image(p); + createGLTexture(f, tid, image); + if (tid == 0) { + qDebug() << "[TextureManager] Can`t load" << p; + return; + } + qDebug() << "[TextureManager] Reloaded" << p << "as" << tid; +} + + +void TextureManager::reloadTexture(GLuint tid, const QImage & im) { + if (im.isNull() || (tid == 0)) return; + QImage image(im); + createGLTexture(f, tid, image); + qDebug() << "[TextureManager] Reloaded" << tid; +} + + +void TextureManager::convertToNormal(QImage & im) { + if (im.isNull()) return; + QImage sim = im.convertToFormat(QImage::Format_ARGB32); + float sum[3] = {0., 0., 0.}; + llong a = 0; + const uchar * sd = sim.constBits(); + for (int i = 0; i < sim.height(); i++) { + for (int j = 0; j < sim.width(); j++) { + sum[2] += sd[a] / 255.f - 0.5f; ++a; + sum[1] += sd[a] / 255.f - 0.5f; ++a; + sum[0] += sd[a] / 255.f - 0.5f; ++a; + ++a; + } + } + float wh = sim.width() * sim.height(); + sum[0] /= wh; + sum[1] /= wh; + sum[2] /= wh; + //qDebug() << sum[0] << sum[1] << sum[2]; + if ((qAbs(sum[0]) <= 0.05f) && (qAbs(sum[1]) <= 0.05f) && (sum[2] >= 0.25f)) /// already normal + return; + //qDebug() << "convert to normal"; + QImage dim = QImage(sim.width(), sim.height(), QImage::Format_ARGB32); + int tx, ty, w = sim.width(), h = sim.height(); + a = 0; + uchar * dd = dim.bits(); + for (int i = 0; i < sim.height(); i++) { + for (int j = 0; j < sim.width(); j++) { + tx = j - 1; + tx = tx < 0 ? w + tx : tx % w; + ty = i - 1; + ty = ty < 0 ? h + ty : ty % h; + QVector3D p[3], res; + p[0] = colorVector(sim.pixel(j, i)); + p[1] = colorVector(sim.pixel(j, ty)); + p[2] = colorVector(sim.pixel(tx, i)); + res.setY(piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f)); + res.setX(piClamp(0.5f - (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f)); + tx = (j + 1) % w; + ty = (i + 1) % h; + p[1] = colorVector(sim.pixel(j, ty)); + p[2] = colorVector(sim.pixel(tx, i)); + res.setY(piClamp(0.5f + (p[0].length() - p[1].length()) / 2.f, 0.f, 1.f)); + res.setX(piClamp(0.5f - (p[0].length() - p[2].length()) / 2.f, 0.f, 1.f)); + res.setZ(1.f); + dd[a] = res.z() * 255; ++a; + dd[a] = res.y() * 255; ++a; + dd[a] = res.x() * 255; ++a; + dd[a] = 255; ++a; + } + } + im = dim; + //im.save("_normal.png"); +} + + +bool TextureManager::loadTextures() { + QFileInfoList fil; + foreach (const QString & i, tex_pathes) + loadTexture(i, true); + tex_pathes.clear(); + return true; +} + + +void TextureManager::deleteTextures() { + for (int i = 0; i < 2; ++i) { + QList texs = tex_ids[i].values(); + qDebug() << "[TextureManager] Delete" << texs.size() << "textures"; + if (!texs.isEmpty()) f->glDeleteTextures(texs.size(), &texs[0]); + tex_ids[i].clear(); + tex_im [i].clear(); + } +} + + +void TextureManager::deleteTexture(const QString & name) { + for (int i = 0; i < 2; ++i) { + if (tex_ids[i].contains(name)) { + GLuint id = tex_ids[i][name]; + f->glDeleteTextures(1, &id); + tex_ids[i].remove(name); + tex_im [i].remove(name); + } + } +} + + +void TextureManager::clearImageCache() { + tex_im[0].clear(); + tex_im[1].clear(); +} diff --git a/qglengine/gltexture_manager.h b/qglengine/gltexture_manager.h new file mode 100644 index 0000000..61a3236 --- /dev/null +++ b/qglengine/gltexture_manager.h @@ -0,0 +1,64 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLTEXTUREMANAGER_H +#define GLTEXTUREMANAGER_H + +#include +#include +#include +#include +#include + + +class TextureManager { +public: + TextureManager(QOpenGLExtraFunctions * f_): f(f_) {} + virtual ~TextureManager() {} + + void addSearchPath(const QString & path) {search_pathes << path;} + static QStringList searchPathes() {return search_pathes;} + QString findFile(const QString & path); + GLuint loadTexture(const QString & path, bool ownership = true, bool bump = false); + GLuint loadTexture(const QImage & image, bool ownership = true, bool bump = false); + QImage loadTextureImage(const QString & path, bool bump = false); + void reloadTexture(GLuint tid, const QString & path); + void reloadTexture(GLuint tid, const QImage & image); + int textureID (const QString & path, bool bump = false) {return tex_ids[bump ? 1 : 0][path];} + QImage textureImage(const QString & path, bool bump = false) {return tex_im [bump ? 1 : 0][path];} + void addTexture(const QString & path) {tex_pathes << path;} + bool loadTextures(); + void deleteTextures(); + void deleteTexture(const QString & name); + void clearImageCache(); + +protected: + static void convertToNormal(QImage & im); + + static QStringList search_pathes; + + QMap tex_ids[2]; + QMap tex_im [2]; + QStringList tex_pathes; + + QOpenGLExtraFunctions * f; + +}; + + +#endif // GLTEXTUREMANAGER_H diff --git a/qglengine/gltexturearray.cpp b/qglengine/gltexturearray.cpp new file mode 100644 index 0000000..7fc9e76 --- /dev/null +++ b/qglengine/gltexturearray.cpp @@ -0,0 +1,96 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "gltexturearray.h" + + +Texture2DArray::Texture2DArray(bool filter) { + target_ = GL_TEXTURE_2D_ARRAY; + texture_ = 0; + layers_ = 0; + filtering_ = filter; +} + + +Texture2DArray::~Texture2DArray() { +} + + +void Texture2DArray::init(QOpenGLExtraFunctions * f) { + if (!isInit()) { + f->glGenTextures(1, &texture_); + } +} + + +void Texture2DArray::destroy(QOpenGLExtraFunctions * f) { + if (texture_ != 0) { + f->glDeleteTextures(1, &texture_); + } + texture_ = 0; +} + + +void Texture2DArray::bind(QOpenGLExtraFunctions * f, int channel) { + f->glActiveTexture(GL_TEXTURE0 + channel); + f->glBindTexture(target_, texture_); +} + + +void Texture2DArray::release(QOpenGLExtraFunctions * f) { + f->glBindTexture(target_, 0); +} + + +bool Texture2DArray::resize(QOpenGLExtraFunctions * f, QSize new_size, int layers_count) { + if (new_size.isNull() || layers_count <= 0) return false; + if ((size_ == new_size) && (layers_ >= layers_count)) return false; + size_ = new_size; + layers_ = layers_count; + f->glBindTexture(target_, texture_); + f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_REPEAT); + f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_REPEAT); + if (filtering_) { + f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + f->glTexParameteri(target_, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); + } else { + f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + f->glTexImage3D(target_, 0, GL_RGBA8, size_.width(), size_.height(), layers_, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + return true; +} + + +void Texture2DArray::load(QOpenGLExtraFunctions * f, const QImage & image, int layer) { + if (image.isNull() || size_.isNull() || layer < 0 || layer >= layers_) return; + QImage im = image.mirrored(false, true) + .scaled(size_, Qt::IgnoreAspectRatio, Qt::SmoothTransformation) + .convertToFormat(QImage::Format_RGBA8888); + //qDebug() << "Texture2DArray::load image" << image.size() << "to layer" << layer; + f->glTexSubImage3D(target_, 0, 0, 0, layer, size_.width(), size_.height(), 1, GL_RGBA, GL_UNSIGNED_BYTE, im.constBits()); +} + + +void Texture2DArray::mipmaps(QOpenGLExtraFunctions * f) { + f->glBindTexture(target_, texture_); + f->glGenerateMipmap(target_); +} diff --git a/qglengine/gltexturearray.h b/qglengine/gltexturearray.h new file mode 100644 index 0000000..8eec745 --- /dev/null +++ b/qglengine/gltexturearray.h @@ -0,0 +1,56 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLTEXTUREARRAY_H +#define GLTEXTUREARRAY_H + +#include "gltypes.h" + + +class Texture2DArray +{ + friend class ObjectBase; +public: + Texture2DArray(bool filter); + ~Texture2DArray(); + + void init (QOpenGLExtraFunctions * f); + void destroy (QOpenGLExtraFunctions * f); + + void bind (QOpenGLExtraFunctions * f, int channel = 0); + void release (QOpenGLExtraFunctions * f); + + // returns true if size changed + bool resize (QOpenGLExtraFunctions * f, QSize new_size, int layers_count); + void load (QOpenGLExtraFunctions * f, const QImage & image, int layer); + void mipmaps (QOpenGLExtraFunctions * f); + + GLuint ID() const {return texture_;} + bool isInit() const {return texture_ != 0;} + +private: + GLenum target_; + GLuint texture_; + QSize size_; + int layers_; + bool filtering_; + +}; + + +#endif // GLTEXTUREARRAY_H diff --git a/qglengine/gltypes.cpp b/qglengine/gltypes.cpp new file mode 100644 index 0000000..6132e8e --- /dev/null +++ b/qglengine/gltypes.cpp @@ -0,0 +1,365 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "glcamera.h" +#include "qglview.h" +#include "gltexture_manager.h" + +#include + +//__GLWidget__ * currentQGLView; +//QMutex globMutex; + + +QString readCharsUntilNull(QDataStream & s) { + QString str; + char ch; + s.readRawData(&ch, 1); + while (ch != '\0') { + str += ch; + s.readRawData(&ch, 1); + } + return str; +} + + +QString findFile(const QString & file, const QStringList & pathes) { + QFileInfo fi(QString(file).replace("\\", "/")); + //qDebug() << "search" << file << "in" << pathes; + if (fi.exists()) return fi.absoluteFilePath(); + QString fn = fi.fileName(); + if (fn.contains("/")) fn = fn.mid(fn.lastIndexOf("/")); + foreach (QString p, pathes) { + QFileInfoList fil = QDir(p).entryInfoList(QStringList(fn), QDir::Files | QDir::NoDotAndDotDot); + //qDebug() << "findFile" << fn << "in" << p << "->" << fil.size(); + if (!fil.isEmpty()) + return fil[0].absoluteFilePath(); + } + return QString(); +} + + +void glDrawQuad(QOpenGLShaderProgram * prog, QVector4D * corner_dirs, GLfloat x, GLfloat y, GLfloat w, GLfloat h) { + //glResetAllTransforms(); + glSetPolygonMode(GL_FILL); + glDisable(GL_LIGHTING); + glEnable(GL_TEXTURE_2D); + int loc = prog ? prog->attributeLocation("qgl_Color") : -1, + locv = prog ? prog->attributeLocation("qgl_Vertex") : -1, + loct = prog ? prog->attributeLocation("qgl_Texture") : -1, + locc = prog ? prog->attributeLocation("view_corner") : -1; + //if (prog) {qDebug() << locv << loct << locc;} + QOpenGLFunctions * glFuncs = QOpenGLContext::currentContext()->functions(); + if (prog) { + static const GLfloat cols [] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; + static const GLfloat verts[] = {x, y, x+w, y, x, y+h, x+w, y+h}; + static const GLfloat texs [] = {0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 1.f}; + GLfloat vcs[] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + if (corner_dirs) { + vcs[0] = corner_dirs[0].x(); vcs[1] = corner_dirs[0].y(); vcs[2] = corner_dirs[0].z(); + vcs[3] = corner_dirs[1].x(); vcs[4] = corner_dirs[1].y(); vcs[5] = corner_dirs[1].z(); + vcs[6] = corner_dirs[2].x(); vcs[7] = corner_dirs[2].y(); vcs[8] = corner_dirs[2].z(); + vcs[9] = corner_dirs[3].x(); vcs[10] = corner_dirs[3].y(); vcs[11] = corner_dirs[3].z(); + } + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glFuncs->glBindBuffer(GL_ARRAY_BUFFER, 0); + glFuncs->glEnableVertexAttribArray(loc); + glFuncs->glVertexAttribPointer(loc, 3, GL_FLOAT, 0, 0, cols); + glFuncs->glEnableVertexAttribArray(locv); + glFuncs->glVertexAttribPointer(locv, 2, GL_FLOAT, 0, 0, verts); + glFuncs->glEnableVertexAttribArray(loct); + glFuncs->glVertexAttribPointer(loct, 2, GL_FLOAT, 0, 0, texs); + glFuncs->glEnableVertexAttribArray(locc); + glFuncs->glVertexAttribPointer(locc, 3, GL_FLOAT, 0, 0, vcs); + glFuncs->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glFuncs->glDisableVertexAttribArray(loc); + glFuncs->glDisableVertexAttribArray(locv); + glFuncs->glDisableVertexAttribArray(loct); + glFuncs->glDisableVertexAttribArray(locc); + } else { + glBegin(GL_TRIANGLE_STRIP); + glColor4f(1.f, 1.f, 1.f, 1.f); + glTexCoord2f(0.f, 0.f); glVertex2f(x, y); + glTexCoord2f(1.f, 0.f); glVertex2f(x+w, y); + glTexCoord2f(0.f, 1.f); glVertex2f(x, y+h); + glTexCoord2f(1.f, 1.f); glVertex2f(x+w, y+h); + glEnd(); + } +} + + +QMatrix4x4 getGLMatrix(GLenum matrix) { + GLfloat gm[16]; + glGetFloatv(matrix, gm); + float qm[16]; + for (int i = 0; i < 16; ++i) + qm[i] = gm[i]; + return QMatrix4x4(qm).transposed(); +} + + +void setGLMatrix(QMatrix4x4 matrix) { + GLfloat gm[16]; + float qm[16]; + matrix.transposed().copyDataTo(qm); + for (int i = 0; i < 16; ++i) + gm[i] = qm[i]; + glLoadMatrixf(gm); +} + + +void qglMultMatrix(const QMatrix4x4 & m) { + GLfloat gm[16]; + float qm[16]; + m.transposed().copyDataTo(qm); + for (int i = 0; i < 16; ++i) + gm[i] = qm[i]; + glMultMatrixf(gm); +} + + +void createGLTexture(QOpenGLExtraFunctions * f, GLuint & tex, int width, int height, const GLenum & format, const GLenum & target) { + //glClearError(); + if (tex == 0) { + f->glGenTextures(1, &tex); + f->glBindTexture(target, tex); + } + //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_COMPONENT16 || format == GL_DEPTH_COMPONENT24 || format == GL_DEPTH_COMPONENT32) + f->glTexImage2D(target, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, nullptr); + else { + int type = GL_UNSIGNED_BYTE; + int fmt = GL_RGBA; + if (format == GL_RGB32F || format == GL_RGB16F || format == GL_RGBA32F || format == GL_RGBA16F) + type = GL_FLOAT; + if (format == GL_RGB32F || format == GL_RGB16F || format == GL_RGB8 || format == GL_RGB) + fmt = GL_RGB; + f->glTexImage2D(target, 0, format, width, height, 0, fmt, type, nullptr); + //glGenerateMipmap(target); + //qDebug() << "glTexImage2D" << width << height << QString::number(t, 16); + } + //qDebug() << QString::number(glGetError(), 16); +} + + +void createGLTexture(QOpenGLExtraFunctions * f, GLuint & tex, const QImage & image, const GLenum & format, const GLenum & target) { + if (tex == 0) { + f->glGenTextures(1, &tex); + } + f->glBindTexture(target, tex); + QImage im = image.mirrored(false, true).convertToFormat(QImage::Format_RGBA8888); + //const QImage & cim(im); + f->glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + f->glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + f->glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_REPEAT); + if (target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_3D) { + f->glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + f->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + f->glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); + } else { + f->glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + f->glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + glClearError(); + f->glTexImage2D(target, 0, format, im.width(), im.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, im.constBits()); + if (target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_3D) { + f->glGenerateMipmap(target); + } + //qDebug() << target << format << tex << im.width() << im.height() << im.bits() << QString::number(glGetError(), 16); +} + + +QMatrix4x4 glMatrixPerspective(float angle, float aspect, float near_, float far_) { + QMatrix4x4 ret; + float t = 1.f / (tanf(angle * deg2rad / 2.f)), e = 2.4e-7f; + if (aspect >= 1.) { + ret(0, 0) = t / aspect; + ret(1, 1) = t; + } else { + ret(0, 0) = t; + ret(1, 1) = t * aspect; + } + ret(2, 2) = e - 1.f;//far_ / (far_ - near_) - 1.; + ret(2, 3) = (e - 2.f) * near_;//2. * far_ * near_ / (far_ - near_); + ret(3, 2) = -1.f; + ret(3, 3) = 0.f; + return ret; +} + + +QImage rotateQImageLeft(const QImage & im) { + QImage ri(im.height(), im.width(), im.format()); + QPainter p(&ri); + p.rotate(90); + p.drawImage(0, -im.height(), im); + p.end(); + return ri; +} + + +QImage rotateQImageRight(const QImage & im) { + QImage ri(im.height(), im.width(), im.format()); + QPainter p(&ri); + p.rotate(-90); + p.drawImage(-im.width(), 0, im); + p.end(); + return ri; +} + + + + +QColor colorFromString(const QString & str) { + QString s = str.trimmed(); + int i = s.indexOf("\t"); + float r, g, b; + r = s.left(i).toFloat(); s = s.right(s.length() - i - 1); i = s.indexOf("\t"); + g = s.left(i).toFloat(); s = s.right(s.length() - i - 1); + b = s.toFloat(); + return QColor(r * 255.f, g * 255.f, b * 255.f); +} + + +QVector3D orthToVector(const QVector3D & v, const float & scale) { + if (v.isNull()) return QVector3D(); + QVector3D rv, fn, sn; + if (v.x() != 0.f) rv.setZ(1.); + else if (v.y() != 0.f) rv.setX(1.); + else rv.setY(1.); + fn = QVector3D::crossProduct(v, rv).normalized(); + sn = QVector3D::crossProduct(v, fn).normalized(); + return fn * urand(scale) + sn * urand(scale); +} + + +QVector3D rotateVector(const QVector3D & v, const QVector3D & a) { + QMatrix4x4 m; + m.rotate(a.z(), 0., 0., 1.); + m.rotate(a.y(), 0., 1., 0.); + m.rotate(a.x(), 1., 0., 0.); + return m * v; +} + + +void setVectorLength(QVector3D & v, const float & l) { + float vl = v.length(); + if (vl == 0.f) return; + float c = l / vl; + v *= c; +} + + +void lengthenVector(QVector3D & v, const float & l) { + float vl = v.length(); + if (l == 0.f || vl == 0.f) return; + float c = 1.f + l / vl; + v *= c; +} + + +Vector3i::Vector3i(const QString & str) { + QString s = str.trimmed(); + int i = s.indexOf("\t"); + p0 = s.left(i).toInt(); s = s.right(s.length() - i - 1); i = s.indexOf("\t"); + p1 = s.left(i).toInt(); s = s.right(s.length() - i - 1); + p2 = s.toInt(); +} + + +void glEnableDepth() { + glEnable(GL_DEPTH_TEST); + //glDepthFunc(GL_GREATER); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); +} + + +void glDisableDepth() { + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); +} + + +void glClearFramebuffer(const QColor & color, bool depth) { + glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF()); + //glClearDepth(0.); + if (depth) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear(GL_COLOR_BUFFER_BIT); +} + + + + +Box3D::Box3D(const QVector & points) { + x = y = z = width = length = height = angle_z = angle_xy = angle_roll = 0.f; + if (points.isEmpty()) return; + float ix, iy, iz, ax, ay, az; + ix = ax = points[0].x(); + iy = ay = points[0].y(); + iz = az = points[0].z(); + for (int i = 1; i < points.size(); ++i) { + ix = qMin(ix, points[i].x()); ax = qMax(ax, points[i].x()); + iy = qMin(iy, points[i].y()); ay = qMax(ay, points[i].y()); + iz = qMin(iz, points[i].z()); az = qMax(az, points[i].z()); + } + x = ix; + y = iy; + z = iz; + length = ax - ix; + width = ay - iy; + height = az - iz; +} + + +QVector Box3D::corners() const { + QVector ret; + ret << QVector3D(x, y, z) << QVector3D(x, y + width, z) << QVector3D(x, y, z + height) << QVector3D(x, y + width, z + height) + << QVector3D(x + length, y, z) << QVector3D(x + length, y + width, z) + << QVector3D(x + length, y, z + height) << QVector3D(x + length, y + width, z + height); + return ret; +} + + +Box3D & Box3D::operator |=(const Box3D & o) { + if (o.isEmpty()) return *this; + if (isEmpty()) *this = o; + else { + GLfloat mx = x + length, my = y + width, mz = z + height; + GLfloat omx = o.x + o.length, omy = o.y + o.width, omz = o.z + o.height; + x = qMin(x, o.x); y = qMin(y, o.y); z = qMin(z, o.z); + mx = qMax(mx, omx); my = qMax(my, omy); mz = qMax(mz, omz); + length = mx - x; width = my - y; height = mz - z; + } + return *this; +} + + +QVector3D vectorFromString(const QString & str) { + QTextStream s(const_cast(&str), QIODevice::ReadOnly); + QVector3D ret; + float f(0.f); + s >> f; ret.setX(f); + s >> f; ret.setY(f); + s >> f; ret.setZ(f); + return ret; +} diff --git a/qglengine/gltypes.h b/qglengine/gltypes.h new file mode 100644 index 0000000..caa3f9e --- /dev/null +++ b/qglengine/gltypes.h @@ -0,0 +1,259 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef GLTYPES_H +#define GLTYPES_H + +#if WIN32 || WIN64 || _WIN32 || _WIN64 || __WIN32__ || __WIN64__ +# define WINDOWS +#endif +#if __QNX__ || __QNXNTO__ +# define QNX +#endif +#ifdef __APPLE__ +# define MAC +#endif +#ifndef WINDOWS +# ifndef QNX +# ifndef MAC +# define LINUX +# endif +# endif +#endif +#if __GNUC__ +# define CC_GCC +#elif _MSC_VER +# define CC_VC +#endif + + +#include +//#ifndef WINDOWS +//# ifdef MAC +//# include +//# include +//# include +//# else +//# include +//# include +//# include +//# endif +//#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef QNX +# include +# include +#else +# include +# include +#endif +#include + + +//#ifdef WINDOWS +//# define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +//# define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +//#endif + +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif +#ifndef M_2PI +# define M_2PI 6.28318530717958647692 +#endif +#ifndef M_PI_3 +# define M_PI_3 1.04719755119659774615 +#endif + +#ifndef GL_RGBA16F +# define GL_RGBA16F GL_RGBA16F_ARB +#endif + +using std::complex; + +#ifndef PIP_VERSION +typedef long long llong; +typedef unsigned char uchar; +typedef unsigned short int ushort; +typedef unsigned int uint; +typedef unsigned long ulong; +typedef unsigned long long ullong; +typedef long double ldouble; + +const float deg2rad = atanf(1.f) / 45.f; +const float rad2deg = 45.f / atanf(1.f); + +# ifdef WINDOWS +inline int random() {return rand();} +# endif +#else +#define random randomi +#endif + +#ifdef CC_VC +inline float round(const float & v) {return floor(v + 0.5);} +#endif +inline float randomu() {return float(random()) / RAND_MAX;} + +inline const QSizeF operator *(const QSizeF & f, const QSizeF & s) {return QSizeF(f.width() * s.width(), f.height() * s.height());} +#ifndef PIP_VERSION +template inline void piSwap(Type & f, Type & s) {Type t = f; f = s; s = t;} +template inline Type piMin(const Type & f, const Type & s) {return (f > s) ? s : f;} +template inline Type piMin(const Type & f, const Type & s, const Type & t) {return (f < s && f < t) ? f : ((s < t) ? s : t);} +template inline Type piMax(const Type & f, const Type & s) {return (f < s) ? s : f;} +template inline Type piMax(const Type & f, const Type & s, const Type & t) {return (f > s && f > t) ? f : ((s > t) ? s : t);} +template inline Type piClamp(const Type & v, const Type & min, const Type & max) {return (v > max ? max : (v < min ? min : v));} +inline ushort letobe_s(ushort v) {return (v << 8) | (v >> 8);} +inline uint letobe_i(const uint & v) {return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | ((v << 24) & 0xFF000000);} +#endif +// return [-1, 1] +inline float urand(const float & scale = 1.) {return ((float)rand() / RAND_MAX - .5f) * (scale + scale);} +// return [0, 1] +inline float uprand(const float & scale = 1.) {return ((float)rand() / RAND_MAX) * scale;} +QString readCharsUntilNull(QDataStream & s); +QString findFile(const QString & file, const QStringList & pathes); +inline QColor operator *(const QColor & c, float v) {return QColor(piClamp(c.red() * v, 0, 255), piClamp(c.green() * v, 0, 255), piClamp(c.blue() * v, 0, 255), piClamp(c.alpha() * v, 0, 255));} +inline QColor operator /(const QColor & c, float v) {return QColor(piClamp(c.red() / v, 0, 255), piClamp(c.green() / v, 0, 255), piClamp(c.blue() / v, 0, 255), piClamp(c.alpha() / v, 0, 255));} + + +inline void qglColor(const QColor & c) {glColor4f(c.redF(), c.greenF(), c.blueF(), c.alphaF());} +inline void glClearError() {int c = 100; while (glGetError() != GL_NO_ERROR && --c > 0) glGetError();} +inline void glSetCapEnabled(GLenum cap, bool on = true) {if (on) glEnable(cap); else glDisable(cap);} +inline void glSetPolygonMode(GLenum mode) {glPolygonMode(GL_FRONT_AND_BACK, mode);} +inline void deleteGLTexture(QOpenGLExtraFunctions * f, GLuint & tex) {if (tex != 0) f->glDeleteTextures(1, &tex); tex = 0;} +void glEnableDepth(); +void glDisableDepth(); +void glClearFramebuffer(const QColor & color = Qt::black, bool depth = true); +void glDrawQuad(QOpenGLShaderProgram * prog = nullptr, QVector4D * corner_dirs = nullptr, GLfloat x = -1.f, GLfloat y = -1.f, GLfloat w = 2.f, GLfloat h = 2.f); +void createGLTexture(QOpenGLExtraFunctions * f, GLuint & tex, int width, int height, const GLenum & format = GL_RGBA, const GLenum & target = GL_TEXTURE_2D); +void createGLTexture(QOpenGLExtraFunctions * f, GLuint & tex, const QImage & image, const GLenum & format = GL_RGBA, const GLenum & target = GL_TEXTURE_2D); +QMatrix4x4 glMatrixPerspective(float angle, float aspect, float near_, float far_); +QImage rotateQImageLeft(const QImage & im); +QImage rotateQImageRight(const QImage & im); +inline QImage rotateQImage180(const QImage & im) {return im.mirrored(true, true);} + +class QGLView; +class ObjectBase; +class Light; +class Camera; +class Texture; +class CubeTexture; +class Map; +class Material; +class TextureManager; +class Texture2DArray; +class Mesh; +class Scene; +class RendererBase; +class Renderer; +class RendererMaterial; +class RendererService; + +struct Box3D { + GLfloat x; + GLfloat y; + GLfloat z; + GLfloat width; + GLfloat length; + GLfloat height; + GLfloat angle_z; + GLfloat angle_xy; + GLfloat angle_roll; + Box3D() {x = y = z = width = length = height = angle_z = angle_xy = angle_roll = 0.f;} + Box3D(const QVector3D & center, GLfloat hwid, GLfloat hlen, GLfloat hhei) {x = center.x() - hwid; y = center.y() - hlen; z = center.z() - hhei; width = 2 * hwid; length = 2 * hlen; height = 2 * hhei; angle_z = angle_xy = angle_roll = 0.f;} + Box3D(const QVector & points); + bool isEmpty() const {return (qAbs(width) < 1E-6f) && (qAbs(length) < 1E-6f) && (qAbs(height) < 1E-6f);} + QVector3D randomPoint() const {return QVector3D(uprand(length) + x, uprand(width) + y, uprand(height) + z);} + QVector3D pos() const {return QVector3D(x, y, z);} + QVector3D size() const {return QVector3D(length, width, height);} + QVector3D center() const {return QVector3D(length / 2.f + x, width / 2.f + y, height / 2.f + z);} + QVector3D angles() const {return QVector3D(angle_xy, angle_roll, angle_z);} + QVector corners() const; + void setPos(const QVector3D & p) {x = p.x(); y = p.y(); z = p.z();} + void setAngles(const QVector3D & a) {angle_xy = a.x(); angle_roll = a.y(); angle_z = a.z();} + void setSize(const QVector3D & s) {length = s.x(); width = s.y(); height = s.z();} + Box3D & moveTo(const QVector3D & v) {x = v.x(); y = v.y(); z = v.z(); return *this;} + Box3D & move(const QVector3D & v) {x += v.x(); y += v.y(); z += v.z(); return *this;} + Box3D movedTo(const QVector3D & v) const {Box3D t(*this); t.x = v.x(); t.y = v.y(); t.z = v.z(); return t;} + Box3D moved(const QVector3D & v) const {Box3D t(*this); t.x += v.x(); t.y += v.y(); t.z += v.z(); return t;} + Box3D & operator |=(const Box3D & o); +}; + +inline QDebug operator <<(QDebug d, const Box3D & v) {d << "Box3D {start (" << v.x << "," << v.y << "," << v.z << "), size (" << v.length << "," << v.width << "," << v.height << ")}"; return d;} + +#pragma pack(push, 1) +struct Vector3i { + Vector3i(int p0_ = 0, int p1_ = 0, int p2_ = 0) {p0 = p0_; p1 = p1_; p2 = p2_;} + Vector3i(const QString & str); + Vector3i movedX(const int & o) {return Vector3i(p0 + o, p1, p2);} + Vector3i movedY(const int & o) {return Vector3i(p0, p1 + o, p2);} + Vector3i movedZ(const int & o) {return Vector3i(p0, p1, p2 + o);} + Vector3i moved(const int & x, const int & y, const int & z) {return Vector3i(p0 + x, p1 + y, p2 + z);} + GLint p0; + GLint p1; + GLint p2; + bool operator ==(const Vector3i & o) const {return p0 == o.p0 && p1 == o.p1 && p2 == o.p2;} + bool operator !=(const Vector3i & o) const {return p0 != o.p0 || p1 != o.p1 || p2 != o.p2;} + void operator +=(int v) {p0 += v; p1 += v; p2 += v;} + QVector3D toQVector3D() const {return QVector3D(p0, p1, p2);} +}; +#pragma pack(pop) + +inline Vector3i operator +(const Vector3i & f, const Vector3i & s) {return Vector3i(f.p0 + s.p0, f.p1 + s.p1, f.p2 + s.p2);} +inline Vector3i operator -(const Vector3i & f, const Vector3i & s) {return Vector3i(f.p0 - s.p0, f.p1 - s.p1, f.p2 - s.p2);} +inline Vector3i operator /(const Vector3i & f, const int & s) {return Vector3i(f.p0 / s, f.p1 / s, f.p2 / s);} +inline uint qHash(const Vector3i & v) {return v.p0 ^ ((v.p1 << 8) | (v.p1 >> 24)) ^ ((v.p2 << 16) | (v.p2 >> 16));} +inline QDebug operator <<(QDebug d, const Vector3i & v) {d.nospace() << "{" << v.p0 << ", " << v.p1 << ", " << v.p2 << "}"; return d.space();} + +inline QDataStream & operator <<(QDataStream & s, const Vector3i & v) {s << v.p0 << v.p1 << v.p2; return s;} +inline QDataStream & operator >>(QDataStream & s, Vector3i & v) {s >> v.p0 >> v.p1 >> v.p2; return s;} + +QVector3D vectorFromString(const QString & str); +QColor colorFromString(const QString & str); +inline QVector4D QColor2QVector(const QColor & c) {return QVector4D(c.redF(), c.greenF(), c.blueF(), c.alphaF());} +inline float cosABV(const QVector3D & v0, const QVector3D & v1) { + float l = v0.length() * v1.length(); + if (l == 0.f) return 0.; + return (QVector3D::dotProduct(v0, v1)) / l; +} +inline QVector3D projection(const QVector3D & v, const QVector3D & to) {return to.normalized() * v.length() * cosABV(v, to);} +QVector3D orthToVector(const QVector3D & v, const float & scale = 1.); +QVector3D rotateVector(const QVector3D & v, const QVector3D & a); +void setVectorLength(QVector3D & v, const float & l); +void lengthenVector(QVector3D & v, const float & l); +inline float squareLength(const QVector3D & from, const QVector3D & to) {return (to.x() - from.x())*(to.x() - from.x()) + (to.y() - from.y())*(to.y() - from.y()) + (to.z() - from.z())*(to.z() - from.z());} +inline QVector3D directionFromAngles(const QVector3D & a) {return rotateVector(QVector3D(1., 0., 0.), a);} +inline float frac(const float & x, const float & b) {return x - int(x / b) * b;} + + +#endif // GLTYPES_H diff --git a/qglengine/glwidget.cpp b/qglengine/glwidget.cpp new file mode 100644 index 0000000..44f6f1a --- /dev/null +++ b/qglengine/glwidget.cpp @@ -0,0 +1,236 @@ +#include "glwidget.h" +#include "qglview.h" +#include + + +GLWidget::GLWidget(QWidget *parent) : QWidget(parent) { + view_ = new QGLView(); + view_->setFlag(Qt::FramelessWindowHint); + container = QWidget::createWindowContainer(view_, this); + lay = new QVBoxLayout(this); + lay->addWidget(container); + lay->setContentsMargins(0, 0, 0, 0); + lay->setSpacing(0); + setMouseTracking(true); + setWindowIcon(QIcon("://icons/qglview.png")); + connect(view_, &QGLView::doubleClick, this, &GLWidget::viewDoubleClicked); +} + + +QColor GLWidget::backColor() const { + return view_->backColor(); +} + + +qreal GLWidget::lineWidth() const { + return view_->lineWidth(); +} + + +qreal GLWidget::FOV() const { + return view_->FOV(); +} + + +qreal GLWidget::depthStart() const { + return view_->depthStart(); +} + + +qreal GLWidget::depthEnd() const { + return view_->depthEnd(); +} + + +QColor GLWidget::ambientColor() const { + return view_->ambientColor(); +} + + +bool GLWidget::isLightEnabled() const { + return view_->isLightEnabled(); +} + + +bool GLWidget::isGrabMouseEnabled() const { + return view_->isGrabMouseEnabled(); +} + + +bool GLWidget::isMouseRotateEnabled() const { + return view_->isMouseRotateEnabled(); +} + + +bool GLWidget::isMouseSelectionEnabled() const { + return view_->isMouseSelectionEnabled(); +} + + +bool GLWidget::isCameraOrbit() const +{ + return view_->isCameraOrbit(); +} + + +bool GLWidget::isHoverHaloEnabled() const { + return view_->isHoverHaloEnabled(); +} + + +QColor GLWidget::hoverHaloColor() const { + return view_->hoverHaloColor(); +} + + +qreal GLWidget::hoverHaloFillAlpha() const { + return view_->hoverHaloFillAlpha(); +} + + +bool GLWidget::isSelectionHaloEnabled() const { + return view_->isSelectionHaloEnabled(); +} + + +QColor GLWidget::selectionHaloColor() const { + return view_->selectionHaloColor(); +} + + +qreal GLWidget::selectionHaloFillAlpha() const { + return view_->selectionHaloFillAlpha(); +} + + +Scene * GLWidget::scene() { + return view_->scene(); +} + + +void GLWidget::addObject(ObjectBase * o) { + view_->scene()->addObject(o); +} + + +QByteArray GLWidget::saveCamera() { + return view_->saveCamera(); +} + + +void GLWidget::restoreCamera(const QByteArray &ba) { + view_->restoreCamera(ba); +} + + +void GLWidget::stop() { + view_->stop(); +} + + +void GLWidget::start(float freq) { + view_->start(freq); +} + + +void GLWidget::setBackColor(const QColor & c) { + view_->setBackColor(c); +} + + +void GLWidget::setLineWidth(const qreal & arg) { + view_->setLineWidth(arg); +} + + +void GLWidget::setFOV(const qreal & arg) { + view_->setFOV(arg); +} + + +void GLWidget::setDepthStart(const qreal & arg) { + view_->setDepthStart(arg); +} + + +void GLWidget::setDepthEnd(const qreal & arg) { + view_->setDepthEnd(arg); +} + + +void GLWidget::setAmbientColor(const QColor & arg) { + view_->setAmbientColor(arg); +} + + +void GLWidget::setLightEnabled(const bool & arg) { + view_->setLightEnabled(arg); +} + + +void GLWidget::setGrabMouseEnabled(const bool & arg) { + view_->setGrabMouseEnabled(arg); +} + + +void GLWidget::setMouseRotateEnabled(const bool & arg) { + view_->setMouseRotateEnabled(arg); +} + + +void GLWidget::setMouseSelectionEnabled(const bool & arg) { + view_->setMouseSelectionEnabled(arg); +} + + +void GLWidget::setCameraOrbit(const bool & arg) { + view_->setCameraOrbit(arg); +} + + +void GLWidget::setHoverHaloEnabled(const bool & arg) { + view_->setHoverHaloEnabled(arg); +} + + +void GLWidget::setHoverHaloColor(const QColor & arg) { + view_->setHoverHaloColor(arg); +} + + +void GLWidget::setHoverHaloFillAlpha(const qreal & arg) { + view_->setHoverHaloFillAlpha(arg); +} + + +void GLWidget::setSelectionHaloEnabled(const bool & arg) { + view_->setSelectionHaloEnabled(arg); +} + + +void GLWidget::setSelectionHaloColor(const QColor & arg) { + view_->setSelectionHaloColor(arg); +} + + +void GLWidget::setSelectionHaloFillAlpha(const qreal & arg) { + view_->setSelectionHaloFillAlpha(arg); +} + + +void GLWidget::viewDoubleClicked() { +// qDebug() << "click widget!!"; + if (view_->windowState() == Qt::WindowFullScreen) { +// view_->hide(); + container = QWidget::createWindowContainer(view_, this); + lay->addWidget(container); + container->show(); +// show(); + } else { +// hide(); + view_->setParent(nullptr); + view_->showFullScreen(); + lay->removeWidget(container); + } +// qDebug() << "click widge done!"; +} diff --git a/qglengine/glwidget.h b/qglengine/glwidget.h new file mode 100644 index 0000000..e88180a --- /dev/null +++ b/qglengine/glwidget.h @@ -0,0 +1,89 @@ +#ifndef GLWIDGET_H +#define GLWIDGET_H + +#include + + +class QGLView; +class ObjectBase; +class Scene; + +class GLWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY (QColor backColor READ backColor WRITE setBackColor) + Q_PROPERTY (qreal lineWidth READ lineWidth WRITE setLineWidth) + Q_PROPERTY (qreal FOV READ FOV WRITE setFOV) + Q_PROPERTY (qreal depthStart READ depthStart WRITE setDepthStart) + Q_PROPERTY (qreal depthEnd READ depthEnd WRITE setDepthEnd) + Q_PROPERTY (QColor ambientColor READ ambientColor WRITE setAmbientColor) + Q_PROPERTY (bool grabMouse READ isGrabMouseEnabled WRITE setGrabMouseEnabled) + Q_PROPERTY (bool mouseRotate READ isMouseRotateEnabled WRITE setMouseRotateEnabled) + Q_PROPERTY (bool mouseSelection READ isMouseSelectionEnabled WRITE setMouseSelectionEnabled) + Q_PROPERTY (bool cameraOrbit READ isCameraOrbit WRITE setCameraOrbit) + Q_PROPERTY (bool hoverHalo READ isHoverHaloEnabled WRITE setHoverHaloEnabled) + Q_PROPERTY (QColor hoverHaloColor READ hoverHaloColor WRITE setHoverHaloColor) + Q_PROPERTY (qreal hoverHaloFillAlpha READ hoverHaloFillAlpha WRITE setHoverHaloFillAlpha) + Q_PROPERTY (bool selectionHalo READ isSelectionHaloEnabled WRITE setSelectionHaloEnabled) + Q_PROPERTY (QColor selectionHaloColor READ selectionHaloColor WRITE setSelectionHaloColor) + Q_PROPERTY (qreal selectionHaloFillAlpha READ selectionHaloFillAlpha WRITE setSelectionHaloFillAlpha) +public: + explicit GLWidget(QWidget *parent = nullptr); + QGLView * view() {return view_;} + + QColor backColor() const; + qreal lineWidth() const; + qreal FOV() const; + qreal depthStart() const; + qreal depthEnd() const; + QColor ambientColor() const; + bool isLightEnabled() const; + bool isGrabMouseEnabled() const; + bool isMouseRotateEnabled() const; + bool isMouseSelectionEnabled() const; + bool isCameraOrbit() const; + bool isHoverHaloEnabled() const; + QColor hoverHaloColor() const; + qreal hoverHaloFillAlpha() const; + bool isSelectionHaloEnabled() const; + QColor selectionHaloColor() const; + qreal selectionHaloFillAlpha() const; + Scene * scene(); + + void addObject(ObjectBase * o); + QByteArray saveCamera(); + void restoreCamera(const QByteArray & ba); + +public slots: + void stop(); + void start(float freq = 60.0); + void setBackColor(const QColor & c); + void setLineWidth(const qreal & arg); + void setFOV(const qreal & arg); + void setDepthStart(const qreal & arg); + void setDepthEnd(const qreal & arg); + void setAmbientColor(const QColor & arg); + void setLightEnabled(const bool & arg); + void setGrabMouseEnabled(const bool & arg); + void setMouseRotateEnabled(const bool & arg); + void setMouseSelectionEnabled(const bool & arg); + void setCameraOrbit(const bool & arg); + void setHoverHaloEnabled(const bool & arg); + void setHoverHaloColor(const QColor & arg); + void setHoverHaloFillAlpha(const qreal & arg); + void setSelectionHaloEnabled(const bool & arg); + void setSelectionHaloColor(const QColor & arg); + void setSelectionHaloFillAlpha(const qreal & arg); + +private slots: + void viewDoubleClicked(); + +private: + QWidget * container; + QGLView * view_; + QLayout * lay; + +signals: +}; + +#endif // GLWIDGET_H diff --git a/qglengine/icons/add-type-camera.png b/qglengine/icons/add-type-camera.png new file mode 100644 index 0000000..13b5588 Binary files /dev/null and b/qglengine/icons/add-type-camera.png differ diff --git a/qglengine/icons/add-type-empty.png b/qglengine/icons/add-type-empty.png new file mode 100644 index 0000000..df5f5b3 Binary files /dev/null and b/qglengine/icons/add-type-empty.png differ diff --git a/qglengine/icons/add-type-geo.png b/qglengine/icons/add-type-geo.png new file mode 100644 index 0000000..b983991 Binary files /dev/null and b/qglengine/icons/add-type-geo.png differ diff --git a/qglengine/icons/add-type-light.png b/qglengine/icons/add-type-light.png new file mode 100644 index 0000000..a896c1a Binary files /dev/null and b/qglengine/icons/add-type-light.png differ diff --git a/qglengine/icons/alpha.png b/qglengine/icons/alpha.png new file mode 100644 index 0000000..5435669 Binary files /dev/null and b/qglengine/icons/alpha.png differ diff --git a/qglengine/icons/application-exit.png b/qglengine/icons/application-exit.png new file mode 100644 index 0000000..8adbcba Binary files /dev/null and b/qglengine/icons/application-exit.png differ diff --git a/qglengine/icons/collapse.png b/qglengine/icons/collapse.png new file mode 100644 index 0000000..ec329aa Binary files /dev/null and b/qglengine/icons/collapse.png differ diff --git a/qglengine/icons/configure.png b/qglengine/icons/configure.png new file mode 100644 index 0000000..2a81957 Binary files /dev/null and b/qglengine/icons/configure.png differ diff --git a/qglengine/icons/dialog-close.png b/qglengine/icons/dialog-close.png new file mode 100644 index 0000000..6072634 Binary files /dev/null and b/qglengine/icons/dialog-close.png differ diff --git a/qglengine/icons/document-edit.png b/qglengine/icons/document-edit.png new file mode 100644 index 0000000..1ecb10c Binary files /dev/null and b/qglengine/icons/document-edit.png differ diff --git a/qglengine/icons/document-import.png b/qglengine/icons/document-import.png new file mode 100644 index 0000000..8cc3eb0 Binary files /dev/null and b/qglengine/icons/document-import.png differ diff --git a/qglengine/icons/document-new.png b/qglengine/icons/document-new.png new file mode 100644 index 0000000..e4d8b47 Binary files /dev/null and b/qglengine/icons/document-new.png differ diff --git a/qglengine/icons/document-open.png b/qglengine/icons/document-open.png new file mode 100644 index 0000000..63380e4 Binary files /dev/null and b/qglengine/icons/document-open.png differ diff --git a/qglengine/icons/document-save-all.png b/qglengine/icons/document-save-all.png new file mode 100644 index 0000000..73405f7 Binary files /dev/null and b/qglengine/icons/document-save-all.png differ diff --git a/qglengine/icons/document-save.png b/qglengine/icons/document-save.png new file mode 100644 index 0000000..aee3e22 Binary files /dev/null and b/qglengine/icons/document-save.png differ diff --git a/qglengine/icons/edit-clear-locationbar-rtl.png b/qglengine/icons/edit-clear-locationbar-rtl.png new file mode 100644 index 0000000..0e6d645 Binary files /dev/null and b/qglengine/icons/edit-clear-locationbar-rtl.png differ diff --git a/qglengine/icons/edit-clear.png b/qglengine/icons/edit-clear.png new file mode 100644 index 0000000..cae930b Binary files /dev/null and b/qglengine/icons/edit-clear.png differ diff --git a/qglengine/icons/edit-copy.png b/qglengine/icons/edit-copy.png new file mode 100644 index 0000000..a8178ca Binary files /dev/null and b/qglengine/icons/edit-copy.png differ diff --git a/qglengine/icons/edit-delete.png b/qglengine/icons/edit-delete.png new file mode 100644 index 0000000..38f11cd Binary files /dev/null and b/qglengine/icons/edit-delete.png differ diff --git a/qglengine/icons/edit-find.png b/qglengine/icons/edit-find.png new file mode 100644 index 0000000..140e581 Binary files /dev/null and b/qglengine/icons/edit-find.png differ diff --git a/qglengine/icons/edit-paste.png b/qglengine/icons/edit-paste.png new file mode 100644 index 0000000..a9aac10 Binary files /dev/null and b/qglengine/icons/edit-paste.png differ diff --git a/qglengine/icons/edit-rename.png b/qglengine/icons/edit-rename.png new file mode 100644 index 0000000..4b47837 Binary files /dev/null and b/qglengine/icons/edit-rename.png differ diff --git a/qglengine/icons/expand.png b/qglengine/icons/expand.png new file mode 100644 index 0000000..6a963b6 Binary files /dev/null and b/qglengine/icons/expand.png differ diff --git a/qglengine/icons/go-jump.png b/qglengine/icons/go-jump.png new file mode 100644 index 0000000..7437eff Binary files /dev/null and b/qglengine/icons/go-jump.png differ diff --git a/qglengine/icons/go-top.png b/qglengine/icons/go-top.png new file mode 100644 index 0000000..489674e Binary files /dev/null and b/qglengine/icons/go-top.png differ diff --git a/qglengine/icons/layer-visible-off.png b/qglengine/icons/layer-visible-off.png new file mode 100644 index 0000000..b1f04a9 Binary files /dev/null and b/qglengine/icons/layer-visible-off.png differ diff --git a/qglengine/icons/layer-visible-on.png b/qglengine/icons/layer-visible-on.png new file mode 100644 index 0000000..9117bea Binary files /dev/null and b/qglengine/icons/layer-visible-on.png differ diff --git a/qglengine/icons/light-+.png b/qglengine/icons/light-+.png new file mode 100644 index 0000000..e7d8d80 Binary files /dev/null and b/qglengine/icons/light-+.png differ diff --git a/qglengine/icons/list-add.png b/qglengine/icons/list-add.png new file mode 100644 index 0000000..a15dd10 Binary files /dev/null and b/qglengine/icons/list-add.png differ diff --git a/qglengine/icons/object-flip-horizontal.png b/qglengine/icons/object-flip-horizontal.png new file mode 100644 index 0000000..2c0579e Binary files /dev/null and b/qglengine/icons/object-flip-horizontal.png differ diff --git a/qglengine/icons/object-flip-vertical.png b/qglengine/icons/object-flip-vertical.png new file mode 100644 index 0000000..7a9ee82 Binary files /dev/null and b/qglengine/icons/object-flip-vertical.png differ diff --git a/qglengine/icons/picker.png b/qglengine/icons/picker.png new file mode 100644 index 0000000..da411da Binary files /dev/null and b/qglengine/icons/picker.png differ diff --git a/qglengine/icons/qglview.png b/qglengine/icons/qglview.png new file mode 100644 index 0000000..21bc50b Binary files /dev/null and b/qglengine/icons/qglview.png differ diff --git a/qglengine/icons/qglview.xcf b/qglengine/icons/qglview.xcf new file mode 100644 index 0000000..50d33a6 Binary files /dev/null and b/qglengine/icons/qglview.xcf differ diff --git a/qglengine/icons/transform-move.png b/qglengine/icons/transform-move.png new file mode 100644 index 0000000..b20fc44 Binary files /dev/null and b/qglengine/icons/transform-move.png differ diff --git a/qglengine/icons/transform-rotate.png b/qglengine/icons/transform-rotate.png new file mode 100644 index 0000000..5063158 Binary files /dev/null and b/qglengine/icons/transform-rotate.png differ diff --git a/qglengine/icons/transform-scale.png b/qglengine/icons/transform-scale.png new file mode 100644 index 0000000..9ff1d44 Binary files /dev/null and b/qglengine/icons/transform-scale.png differ diff --git a/qglengine/icons/type-camera.png b/qglengine/icons/type-camera.png new file mode 100644 index 0000000..e7fcde0 Binary files /dev/null and b/qglengine/icons/type-camera.png differ diff --git a/qglengine/icons/type-empty.png b/qglengine/icons/type-empty.png new file mode 100644 index 0000000..337f0de Binary files /dev/null and b/qglengine/icons/type-empty.png differ diff --git a/qglengine/icons/type-geo.png b/qglengine/icons/type-geo.png new file mode 100644 index 0000000..d6b825c Binary files /dev/null and b/qglengine/icons/type-geo.png differ diff --git a/qglengine/icons/type-light.png b/qglengine/icons/type-light.png new file mode 100644 index 0000000..5301173 Binary files /dev/null and b/qglengine/icons/type-light.png differ diff --git a/qglengine/icons/view-refresh.png b/qglengine/icons/view-refresh.png new file mode 100644 index 0000000..66f0ceb Binary files /dev/null and b/qglengine/icons/view-refresh.png differ diff --git a/qglengine/openglwindow.cpp b/qglengine/openglwindow.cpp new file mode 100644 index 0000000..f6fa56c --- /dev/null +++ b/qglengine/openglwindow.cpp @@ -0,0 +1,95 @@ +#include "openglwindow.h" +#include +#include +#include +#include + + +OpenGLWindow::OpenGLWindow(QWindow *parent) + : QWindow(parent) + , m_context(nullptr) + , m_device(nullptr) +{ + setFlag(Qt::FramelessWindowHint); + setSurfaceType(QWindow::OpenGLSurface); + QSurfaceFormat format = QSurfaceFormat::defaultFormat(); +// qDebug() << format; +#ifdef QT_OPENGL_ES_2 + format.setRenderableType(QSurfaceFormat::OpenGLES); +#else + if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { + format.setVersion(4, 0); + format.setProfile(QSurfaceFormat::CoreProfile); + } +#endif + format.setDepthBufferSize(24); + format.setSamples(8); +// format.setStencilBufferSize(8); + setFormat(format); + QSurfaceFormat::setDefaultFormat(format); +} + + +OpenGLWindow::~OpenGLWindow() { + delete m_device; +} + + +void OpenGLWindow::render(QPainter *painter) { +} + + +void OpenGLWindow::initialize() { +} + + +void OpenGLWindow::render() { +// if (!m_device) m_device = new QOpenGLPaintDevice; +// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); +// m_device->setSize(size() * devicePixelRatio()); +// m_device->setDevicePixelRatio(devicePixelRatio()); +// QPainter painter(m_device); +// render(&painter); +} + + +void OpenGLWindow::renderLater() { + requestUpdate(); +} + + +bool OpenGLWindow::event(QEvent *event) { + switch (event->type()) { + case QEvent::UpdateRequest: + renderNow(); + return true; + default: + return QWindow::event(event); + } +} + + +void OpenGLWindow::exposeEvent(QExposeEvent *event) { + if (isExposed()) renderNow(); +} + + +void OpenGLWindow::renderNow() { + if (!isExposed()) + return; + bool needsInitialize = false; + if (!m_context) { + m_context = new QOpenGLContext(this); + m_context->setFormat(requestedFormat()); + m_context->create(); + needsInitialize = true; + } + m_context->makeCurrent(this); + if (needsInitialize) { + initializeOpenGLFunctions(); + initialize(); + } + render(); + m_context->swapBuffers(this); +} + diff --git a/qglengine/openglwindow.h b/qglengine/openglwindow.h new file mode 100644 index 0000000..82735b7 --- /dev/null +++ b/qglengine/openglwindow.h @@ -0,0 +1,36 @@ +#include +#include + +class QPainter; +class QOpenGLContext; +class QOpenGLPaintDevice; + + +class OpenGLWindow: public QWindow, protected QOpenGLExtraFunctions +{ + Q_OBJECT +public: + explicit OpenGLWindow(QWindow *parent = nullptr); + ~OpenGLWindow(); + + virtual void render(QPainter *painter); + virtual void render(); + + virtual void initialize(); + + QOpenGLContext * context() {return m_context;} + +public slots: + void renderLater(); + void renderNow(); + +protected: + bool event(QEvent *event) override; + + void exposeEvent(QExposeEvent *event) override; + +private: + QOpenGLContext *m_context; + QOpenGLPaintDevice *m_device; +}; + diff --git a/qglengine/plugin/CMakeLists.txt b/qglengine/plugin/CMakeLists.txt new file mode 100644 index 0000000..773f6c4 --- /dev/null +++ b/qglengine/plugin/CMakeLists.txt @@ -0,0 +1,12 @@ +project(qglengine_plugin) +include_directories("..") +add_definitions(-DQT_PLUGIN) +add_definitions(-DQT_NO_DEBUG) +add_definitions(-DQT_SHARED) +add_definitions(-DQDESIGNER_EXPORT_WIDGETS) +find_qt(${QtVersions} Core Designer Gui Widgets OpenGL) +qt_sources(SRC) +qt_wrap(${SRC} CPPS out_CPP QMS out_QM) +qt_add_library(${PROJECT_NAME} SHARED out_CPP) +qt_target_link_libraries(${PROJECT_NAME} qglengine) +qt_install(TARGETS ${PROJECT_NAME} DESTINATION QtPlugins/designer) diff --git a/qglengine/plugin/qglview_designerplugin.cpp b/qglengine/plugin/qglview_designerplugin.cpp new file mode 100644 index 0000000..708776b --- /dev/null +++ b/qglengine/plugin/qglview_designerplugin.cpp @@ -0,0 +1,14 @@ +#include "qglview_designerplugin.h" +#include "qglviewplugin.h" + + +QGLViewDesignerPlugin::QGLViewDesignerPlugin(QObject * parent): QObject(parent) +{ + m_widgets.append(new QGLViewPlugin(this)); +} + + +QList QGLViewDesignerPlugin::customWidgets() const { + return m_widgets; +} + diff --git a/qglengine/plugin/qglview_designerplugin.h b/qglengine/plugin/qglview_designerplugin.h new file mode 100644 index 0000000..db6fc67 --- /dev/null +++ b/qglengine/plugin/qglview_designerplugin.h @@ -0,0 +1,22 @@ +#ifndef QGLVIEW_DESIGNERPLUGIN_H +#define QGLVIEW_DESIGNERPLUGIN_H + +#include +#include + + +class QGLViewDesignerPlugin: public QObject, public QDesignerCustomWidgetCollectionInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "qad.qglview") + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) +public: + QGLViewDesignerPlugin(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; + +}; + +#endif // QGLVIEW_DESIGNERPLUGIN_H diff --git a/qglengine/plugin/qglviewplugin.cpp b/qglengine/plugin/qglviewplugin.cpp new file mode 100644 index 0000000..97db1e4 --- /dev/null +++ b/qglengine/plugin/qglviewplugin.cpp @@ -0,0 +1,95 @@ +#include "../glwidget.h" +#include "qglviewplugin.h" +#include +#include "glprimitives.h" +#include "qglview.h" + + +QGLViewPlugin::QGLViewPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + + +void QGLViewPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) + return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + + +bool QGLViewPlugin::isInitialized() const { + return m_initialized; +} + + +QWidget * QGLViewPlugin::createWidget(QWidget * parent) { + GLWidget * w = new GLWidget(parent); + if (m_initialized) { + auto axis = new GLObjectBase(); + GLObjectBase * obj; + float al = 1.; + obj = new GLPrimitiveLine(QVector3D(0, 0, -al), QVector3D(0, 0, al)); + obj->material().color_diffuse = Qt::darkBlue; obj->setAcceptLight(false); + obj->setSelectable(false); + axis->addChild(obj); + obj = new GLPrimitiveLine(QVector3D(-al, 0, 0), QVector3D(al, 0, 0)); + obj->material().color_diffuse = Qt::darkRed; obj->setAcceptLight(false); + obj->setSelectable(false); + axis->addChild(obj); + obj = new GLPrimitiveLine(QVector3D(0, -al, 0), QVector3D(0, al, 0)); + obj->material().color_diffuse = Qt::darkGreen; obj->setAcceptLight(false); + obj->setSelectable(false); + axis->addChild(obj); + w->view()->addObject(axis); + auto cam_light = new Light(); + cam_light->intensity = 0.5; + cam_light->setName("Camera_Light"); + w->view()->camera()->addChild(cam_light); + w->start(); + } + return w; +} + + +QString QGLViewPlugin::name() const { + return QLatin1String("GLWidget"); +} + + +QString QGLViewPlugin::group() const { + return QLatin1String("Display Widgets"); +} + + +QIcon QGLViewPlugin::icon() const { + return QIcon("://icons/qglview.png"); +} + + +QString QGLViewPlugin::toolTip() const { + return QLatin1String(""); +} + + +QString QGLViewPlugin::whatsThis() const { + return QLatin1String(""); +} + + +bool QGLViewPlugin::isContainer() const { + return false; +} + + +QString QGLViewPlugin::domXml() const { + return QLatin1String("\n\n"); +} + + +QString QGLViewPlugin::includeFile() const { + return QLatin1String("glwidget.h"); +} + diff --git a/qglengine/plugin/qglviewplugin.h b/qglengine/plugin/qglviewplugin.h new file mode 100644 index 0000000..6516afb --- /dev/null +++ b/qglengine/plugin/qglviewplugin.h @@ -0,0 +1,33 @@ +#ifndef QGLVIEWPLUGIN_H +#define QGLVIEWPLUGIN_H + +#include +#include + + +class QGLViewPlugin: public QObject, public QDesignerCustomWidgetInterface +{ + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + explicit QGLViewPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; + +}; + +#endif //QGLVIEWPLUGIN_H diff --git a/qglengine/qglview.cpp b/qglengine/qglview.cpp new file mode 100644 index 0000000..41dd4a8 --- /dev/null +++ b/qglengine/qglview.cpp @@ -0,0 +1,446 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "qglview.h" +#include "glmesh.h" +#include "gltexture_manager.h" +#include +#include +#include +#include + +using namespace QGLEngineShaders; + + +QGLView::QGLView(): OpenGLWindow(), renderer_(this) { + setIcon(QIcon(":/icons/qglview.png")); + deleting_ = false; + timer = 0; + need_init_ = is_first_draw = true; + backColor_ = Qt::darkGray; + hoverHaloColor_ = QColor(195, 140, 255); + selectionHaloColor_ = QColor(175, 255, 140); + ambientColor_ = QColor(10, 10, 10); + lastPos = QPoint(-1, -1); + lineWidth_ = 1.; + max_anisotropic = 1; + max_texture_chanels = 8; + cameraOrbit_ = lightEnabled_ = canSelect_ = true; + shaders_supported = selecting_ = customMouseMove_ = false; + sel_button = Qt::LeftButton; + sel_mod = Qt::ControlModifier; + fps_cnt = 0; + fps_tm = fps_ = 0.; + fogDensity_ = fogEnd_ = 1.; + fogStart_ = 0.; + 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.); + mouse_first = mouseSelect_ = hoverHalo_ = selectionHalo_ = true; + mouseRotate_ = true; + fogEnabled_ = is_init = grabMouse_ = shaders_bind = changed_ = false; + rmode = ObjectBase::Fill; +// sel_pen = QPen(Qt::black, 1, Qt::DashLine); +// sel_brush = QBrush(QColor(170, 100, 255, 120)); + scene_ = new Scene(); + connect(scene_, SIGNAL(selectionChanged()), this, SIGNAL(selectionChanged())); + connect(scene_, SIGNAL(__destroyed()), this, SLOT(__destroyed())); + camera_ = new Camera(); + camera_->setAim(QVector3D()); + camera_->setPos(QVector3D(2, 2, 2)); + camera_->setName("Camera"); + emit cameraPosChanged(camera_->pos()); + //camera().aim_ = camera().pos_; + ktm_.restart(); + + Mesh * m = Primitive::cube(); + ObjectBase * o = new ObjectBase(m); + o->setColor(Qt::cyan); + scene()->addObject(o); + delete m; + +} + + +QGLView::~QGLView() { + deleting_ = true; + stop(); + scene_->destroy(); + delete scene_; +} + + +void QGLView::stop() { + if (timer) killTimer(timer); +} + + +void QGLView::start(float freq) { + timer = startTimer(freq <= 0.f ? 0 : int(1000.f / freq)); +} + + +Scene::SelectionMode QGLView::selectionMode() const { + return scene_->selectionMode(); +} + + +void QGLView::setSelectionMode(Scene::SelectionMode m) { + scene_->setSelectionMode(m); +} + + +void QGLView::selectObject(ObjectBase * o, bool add_to_selection) { + scene_->selectObject(o, add_to_selection); +} + + +void QGLView::clearSelection() { + scene_->clearSelection(); +} + + +QList QGLView::selectedObjects() const { + return scene_->selectedObjects(); +} + + +ObjectBase * QGLView::selectedObject() const { + return scene_->selectedObject(); +} + + + +void QGLView::resizeEvent(QResizeEvent * e) { + renderLater(); +} + + +void QGLView::timerEvent(QTimerEvent *) { + renderNow(); + //if (ktm_.elapsed() < QApplication::keyboardInputInterval()) return; + Qt::KeyboardModifiers km = QApplication::keyboardModifiers(); + foreach (int i, keys_) + emit keyEvent((Qt::Key)i, km); +} + + +void QGLView::render() { + resizeGL(width(), height()); + emit glBeginPaint(); + renderer_.mouse_pos = mapFromGlobal(QCursor::pos()); + renderer_.renderScene(); + emit glPainting(); + emit glEndPaint(); + fps_tm += time.elapsed(); + time.restart(); + fps_cnt++; + if (fps_tm < 1000.) return; + fps_ = fps_cnt / fps_tm * 1000.; + fps_tm = 0.; + fps_cnt = 0; +} + + +void QGLView::initialize() { + checkCaps(); + renderer_.reloadShaders(); + renderer_.init(width(), height()); + 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_MAX_ANISOTROPY_EXT); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + is_init = true; + need_init_ = false; + emit glInitializeDone(); +} + + +void QGLView::checkCaps() { + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropic); + shaders_supported = QOpenGLShaderProgram::hasOpenGLShaderPrograms(); +} + + +void QGLView::__destroyed() { + renderer_.rend_mat.mat_thumbnails.clear(); + hov_objects.clear(); +} + + +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); + mouse_first = true; + //qDebug() << "resize" << width << height; + iaspect = (aspect == 0.f) ? 0. : 1 / aspect; + glViewport(0, 0, width, height); + emit glResize(width, height); +} + + +void QGLView::mouseReleaseEvent(QMouseEvent * e) { + bool add_ts = e->modifiers().testFlag(sel_mod); + if (selecting_) { + selecting_ = false; + canSelect_ = true; + renderer_.mouse_rect = QRect(); + scene_->selectObjects(hov_objects.toList(), add_ts); + return; + } + if (canSelect_ && mouseSelect_ && e->button() == Qt::LeftButton) { + if ((lastPos - downPos).manhattanLength() < QApplication::startDragDistance() && !hov_objects.isEmpty()) { + scene_->selectObject(hov_objects[0], add_ts); + } + } + canSelect_ = e->buttons() == 0; + emit glMouseReleaseEvent(e); +} + + +void QGLView::mousePressEvent(QMouseEvent * e) { + if (selecting_) { + downPos = e->pos(); + selecting_ = false; + renderer_.mouse_rect = QRect(); + return; + } + if (!QRect(QPoint(), size()).contains(e->pos())) return; + lastPos = e->pos(); + downPos = lastPos; + emit glMousePressEvent(e); +} + + +void QGLView::mouseMoveEvent(QMouseEvent * e) { + QPoint cpos = e->pos(); + if (selecting_) { + 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(), size()); + if (mouseRotate_) { + float dx = e->x() - lastPos.x(); + float dy = e->y() - lastPos.y(); + if (e->buttons().testFlag(Qt::MidButton)) { + if (cameraOrbit_) { + camera()->orbitZ(dx / 4.f); + camera()->orbitXY(dy / 4.f); + } else { + camera()->rotateZ(dx / 4.f); + camera()->rotateXY(dy / 4.f); + } + emit cameraPosChanged(camera()->pos()); + } else if (e->buttons().testFlag(Qt::RightButton)) { + float ad = camera()->distance(); + camera()->moveLeft(dx / 1000.f * ad); + camera()->moveUp(dy / 1000.f * ad); + emit cameraPosChanged(camera()->pos()); + } + } + lastPos = e->pos(); + if (customMouseMove_) emit customMouseMoveEvent(e->pos(), lastPos, e->buttons()); + if (grabMouse_) { + QCursor::setPos(mapToGlobal(QRect(QPoint(), size()).center())); + static bool mouse_sec = false; + if (mouse_sec) { + mouse_sec = false; + return; + } + if (mouse_first) { + mouse_first = false; + mouse_sec = true; + return; + } + lastPos = g_rect.center(); + int dx = e->x() - lastPos.x(); + int dy = e->y() - lastPos.y(); + emit glMouseMoveEvent(new QMouseEvent(QEvent::MouseMove, QPoint(dx, dy), e->button(), e->buttons(), e->modifiers())); + return; + } + emit glMouseMoveEvent(e); +} + + +void QGLView::wheelEvent(QWheelEvent * e) { + if (mouseRotate_) { + if (e->delta() > 0) camera()->flyCloser(0.1f); + if (e->delta() < 0) camera()->flyFarer(0.1f); + emit cameraPosChanged(camera()->pos()); + } + emit glWheelEvent(e); +} + + +void QGLView::leaveEvent(QEvent * ) { + lastPos = QPoint(-1, -1); + //qDebug() << lastPos; +} + + +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::mouseDoubleClickEvent(QMouseEvent * e) { + if (e->buttons().testFlag(Qt::MidButton)) + emit doubleClick(); +} + + +Camera * QGLView::camera() { + return camera_; +} + + +const Camera * QGLView::camera() const { + return camera_; +} + + +void QGLView::setCamera(Camera * camera) { + camera_ = camera; +} + + +TextureManager * QGLView::textureManager() { + return renderer_.textures_manager; +} + + +void QGLView::reloadTextures() { + renderer_.markReloadTextures(); +} + + +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); +} + + +void QGLView::setCameraLightOn(bool on) { + renderer_.setCameraLightOn(on); +} + + +bool QGLView::isCameraLightOn() const { + return renderer_.isCameraLightOn(); +} + + +QByteArray QGLView::saveCamera() { + ChunkStream cs; + const Camera * c = camera(); + cs.add(1, c->pos()).add(2, c->aim()).add(3, c->angles()).add(4, c->FOV()); + return cs.data(); +} + + +void QGLView::restoreCamera(const QByteArray & ba) { + if (ba.isEmpty()) return; + Camera * c = camera(); + QVector3D pos(c->pos()), aim(c->aim()), ang(c->angles()); + float fov(c->FOV()); + ChunkStream cs(ba); + cs.readAll(); + cs.get(1, pos).get(2, aim).get(3, ang).get(4, fov); + camera()->setPos(pos); + camera()->setAim(aim); + camera()->setAngles(ang); + camera()->setFOV(fov); +} + + +QByteArray QGLView::saveFeatures() { + QByteArray ba; + QDataStream ds(&ba, QIODevice::WriteOnly); + ds << features_; + return ba; +} + + +void QGLView::restoreFeatures(const QByteArray & ba) { + QHash f; + QDataStream ds(ba); + ds >> f; + features_ = f; +} + + +QImage QGLView::materialThumbnail(Material * m) { + return renderer_.materialThumbnail(m); +} + diff --git a/qglengine/qglview.h b/qglengine/qglview.h new file mode 100644 index 0000000..f660c6c --- /dev/null +++ b/qglengine/qglview.h @@ -0,0 +1,277 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef QGLVIEW_H +#define QGLVIEW_H + +#include "openglwindow.h" +#include "glframebuffer.h" +#include "glprimitives.h" +#include "glcamera.h" +#include "glscene.h" +#include "glrendererbase.h" +#include "renderer.h" +#include + + +class QGLView: public OpenGLWindow +{ + friend class GLRendererBase; + friend class TextureManager; + friend class ObjectBase; + friend class Scene; + friend class RendererBase; + friend class Renderer; + friend class RendererMaterial; + friend class RendererService; + Q_OBJECT + Q_PROPERTY (QColor backColor READ backColor WRITE setBackColor) + Q_PROPERTY (float lineWidth READ lineWidth WRITE setLineWidth) + Q_PROPERTY (float FOV READ FOV WRITE setFOV) + Q_PROPERTY (float depthStart READ depthStart WRITE setDepthStart) + Q_PROPERTY (float depthEnd READ depthEnd WRITE setDepthEnd) + Q_PROPERTY (QColor ambientColor READ ambientColor WRITE setAmbientColor) + Q_PROPERTY (QColor fogColor READ fogColor WRITE setFogColor) + Q_PROPERTY (bool fogEnabled READ isFogEnabled WRITE setFogEnabled) + Q_PROPERTY (float fogDensity READ fogDensity WRITE setFogDensity) + Q_PROPERTY (float fogStart READ fogStart WRITE setFogStart) + Q_PROPERTY (float fogEnd READ fogEnd WRITE setFogEnd) + 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 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 + }; + + + void stop(); + void start(float freq = 60.); + + QColor backColor() const {return backColor_;} + float lineWidth() const {return lineWidth_;} + float FOV() const {return camera()->fov_;} + float depthStart() const {return camera()->depth_start;} + float depthEnd() const {return camera()->depth_end;} + float currentFPS() const {return fps_;} + int maxAnisotropicLevel() const {return max_anisotropic;} + + QColor ambientColor() const {return ambientColor_;} + QColor fogColor() const {return fogColor_;} + float fogDensity() const {return fogDensity_;} + float fogStart() const {return fogStart_;} + float fogEnd() const {return fogEnd_;} + bool isFogEnabled() const {return fogEnabled_;} + bool isLightEnabled() const {return lightEnabled_;} + bool isGrabMouseEnabled() const {return grabMouse_;} + bool isMouseRotateEnabled() const {return mouseRotate_;} + bool isMouseSelectionEnabled() const {return mouseSelect_;} + bool isCameraOrbit() const {return cameraOrbit_;} + 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;} + +// void addObject(GLObjectBase & o) {addObject(&o);} + + Scene::SelectionMode selectionMode() const; + Qt::MouseButton selectionButton() const {return sel_button;} + Qt::KeyboardModifier selectionModifier() const {return sel_mod;} + + void setSelectionMode(Scene::SelectionMode m); + void setSelectionButton(Qt::MouseButton v) {sel_button = v;} + void setSelectionModifier(Qt::KeyboardModifier v) {sel_mod = v;} + + void selectObject(ObjectBase * o, bool add_to_selection = false); + void clearSelection(); + QList selectedObjects() const; + ObjectBase * selectedObject() const; + + TextureManager * textureManager(); + void reloadTextures(); + + Scene * scene() {return scene_;} + void focusOn(const Box3D & bb); + void setCameraLightOn(bool on); + bool isCameraLightOn() const; + + Camera * camera(); + const Camera * camera() const; + void setCamera(Camera * camera); + QByteArray saveCamera(); + void restoreCamera(const QByteArray & ba); + QByteArray saveFeatures(); + void restoreFeatures(const QByteArray & ba); + + QImage materialThumbnail(Material * m); + + + 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); + void mouseMoveEvent(QMouseEvent * e); + void mouseReleaseEvent(QMouseEvent * e); + void wheelEvent(QWheelEvent * e); + void leaveEvent(QEvent * ); + void mouseDoubleClickEvent(QMouseEvent * e); + + void keyPressEvent(QKeyEvent * e); + void keyReleaseEvent(QKeyEvent * e); + void focusOutEvent(QFocusEvent *); + + void renderSelection(); + + void checkCaps(); + +private: + void processKeys(); + bool setupViewport(); + + QPoint lastPos, downPos; + Scene * scene_; + Camera * camera_; +// uint cid; + QSet keys_; + QColor backColor_, fogColor_, ambientColor_, hoverHaloColor_, selectionHaloColor_; + QTime time, ktm_; + GLint max_anisotropic, max_texture_chanels; + ObjectBase::RenderMode rmode; + QVector hov_objects; + Qt::MouseButton sel_button; + Qt::KeyboardModifier sel_mod; + GLRendererBase::RenderingParameters start_rp; + QHash features_; + QSize prev_size; + float lineWidth_; + float fogDensity_, fogStart_, fogEnd_, fps_, fps_tm, hoverHaloFill_, selectionHaloFill_, m_motionBlurFactor; + int timer, fps_cnt, sh_id_loc, deleting_; + bool is_first_draw, is_init, fogEnabled_, lightEnabled_, grabMouse_, mouse_first, mouseRotate_, mouseSelect_, customMouseMove_, canSelect_; + bool shaders_supported, changed_, cameraOrbit_, need_init_; + bool hoverHalo_, selectionHalo_, shaders_bind, selecting_; + +private slots: + void __destroyed(); + +public slots: + void setBackColor(const QColor & arg) {backColor_ = arg;} + void setLineWidth(const float & arg) {lineWidth_ = arg;} + void setFOV(const float & arg) {camera()->fov_ = arg;} + void setDepthStart(const float & arg) {camera()->depth_start = arg;} + void setDepthEnd(const float & arg) {camera()->depth_end = arg;} + void setAmbientColor(const QColor & arg) {ambientColor_ = arg;} + void setFogColor(const QColor & arg) {fogColor_ = arg;} + void setFogDensity(const float & arg) {fogDensity_ = arg;} + void setFogStart(const float & arg) {fogStart_ = arg;} + void setFogEnd(const float & arg) {fogEnd_ = arg;} + void setFogEnabled(const bool & arg) {fogEnabled_ = arg;} + void setLightEnabled(const bool & arg) {lightEnabled_ = arg;} + 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;} + 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 glBeforePaint(); + void glBeginPaint(); + void glPainting(); + 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 materialsChanged(); + void materialThumbnailCreated(Material*); + void doubleClick(); + +}; + +#endif // QGLVIEW_H diff --git a/qglengine/qglview.qrc b/qglengine/qglview.qrc new file mode 100644 index 0000000..4cebf2b --- /dev/null +++ b/qglengine/qglview.qrc @@ -0,0 +1,46 @@ + + + icons/add-type-camera.png + icons/add-type-geo.png + icons/add-type-light.png + icons/add-type-empty.png + icons/collapse.png + icons/expand.png + icons/edit-rename.png + icons/alpha.png + icons/application-exit.png + icons/configure.png + icons/dialog-close.png + icons/document-edit.png + icons/document-import.png + icons/document-new.png + icons/document-open.png + icons/document-save.png + icons/document-save-all.png + icons/edit-clear.png + icons/edit-clear-locationbar-rtl.png + icons/edit-copy.png + icons/edit-delete.png + icons/edit-find.png + icons/edit-paste.png + icons/go-jump.png + icons/go-top.png + icons/layer-visible-on.png + icons/layer-visible-off.png + icons/light-+.png + icons/list-add.png + icons/object-flip-horizontal.png + icons/object-flip-vertical.png + icons/picker.png + icons/qglview.png + icons/transform-move.png + icons/transform-rotate.png + icons/transform-scale.png + icons/type-camera.png + icons/type-geo.png + icons/type-light.png + icons/type-empty.png + icons/view-refresh.png + shaders/bloom_0.frag + + diff --git a/qglengine/qglview_test/main.cpp b/qglengine/qglview_test/main.cpp new file mode 100644 index 0000000..3560ee7 --- /dev/null +++ b/qglengine/qglview_test/main.cpp @@ -0,0 +1,34 @@ +/* + Stanley Designer + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include "qglview_window.h" + +int main(int argc, char ** argv) { + QApplication a(argc, argv); + a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); + QGLViewWindow w; + w.show(); + QStringList al(a.arguments()); + al.pop_front(); + foreach (QString s, al) + w.loadFile(s); + return a.exec(); +} diff --git a/qglengine/qglview_test/qglview_window.cpp b/qglengine/qglview_test/qglview_window.cpp new file mode 100644 index 0000000..00239a1 --- /dev/null +++ b/qglengine/qglview_test/qglview_window.cpp @@ -0,0 +1,286 @@ +/* + Stanley Designer + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "qglview_window.h" +#include "renderer.h" +#include "glwidget.h" +#include +#include +#include + + +QGLViewWindow::QGLViewWindow(QWidget * parent): QMainWindow(parent), Ui::QGLViewWindow() { + setupUi(this); + session.setFile("session_qglview_test.conf"); + session.addEntry(this); + spinViewLineWidth->setValue(lineThickness()*2); + + view->view()->camera()->setAim(QVector3D()); + view->view()->camera()->setPos(QVector3D(2, 2, 2)); + view->view()->camera()->flyToDistance(2.); +// view->setFrameShape(QFrame::NoFrame); + view->view()->setMouseRotateEnabled(true); + view->view()->setMouseSelectionEnabled(true); + view->view()->setSelectionHaloEnabled(true); + view->view()->setHoverHaloEnabled(true); + view->view()->setBackColor(Qt::lightGray); + view->view()->setDepthStart(0.1); + view->view()->setSelectionMode(Scene::smMultiSelection); + + spinFOV->setValue(view->view()->FOV()); + spinDepthStart->setValue(view->view()->depthStart()); + spinDepthEnd->setValue(view->view()->depthEnd()); + groupHoverHalo->setChecked(view->view()->isHoverHaloEnabled()); + groupSelectionHalo->setChecked(view->view()->isSelectionHaloEnabled()); + spinHoverHaloFill->setValue(view->view()->hoverHaloFillAlpha()); + spinSelectionHaloFill->setValue(view->view()->selectionHaloFillAlpha()); + colorHoverHalo->setColor(view->view()->hoverHaloColor()); + colorSelectionHalo->setColor(view->view()->selectionHaloColor()); + checkFXAA->setChecked(view->view()->isFeatureEnabled(QGLView::qglFXAA)); + colorBack->setColor(view->view()->backColor()); + colorAmbient->setColor(view->view()->ambientColor()); + checkCameraOrbit->setChecked(view->view()->isCameraOrbit()); + + groupShadows->setChecked(view->view()->isFeatureEnabled(QGLView::qglShadowsEnabled)); + groupEyeAccomodation->setChecked(view->view()->isFeatureEnabled(QGLView::qglEyeAccomodationEnabled)); + groupBloom->setChecked(view->view()->isFeatureEnabled(QGLView::qglBloomEnabled)); + groupMotionBlur->setChecked(view->view()->isFeatureEnabled(QGLView::qglMotionBlurEnabled)); + groupReflections->setChecked(view->view()->isFeatureEnabled(QGLView::qglReflectionsEnabled)); + checkSoftShadows->setChecked(view->view()->isFeatureEnabled(QGLView::qglShadowsSoftEnabled)); + groupSSAO->setChecked(view->view()->isFeatureEnabled(QGLView::qglSSAOEnabled)); + spinAccom->setValue(view->view()->feature(QGLView::qglEyeAccomodationTime).toDouble()); + spinAccomMS->setValue(view->view()->feature(QGLView::qglEyeAccomodationMaxSpeed).toDouble()); + checkReflectionsBlur->setChecked(view->view()->isFeatureEnabled(QGLView::qglReflectionsBlur)); + spinShadowmapSize->setValue(view->view()->feature(QGLView::qglShadowsMapSize).toInt()); + spinMotionBlurFactor->setValue(view->view()->feature(QGLView::qglMotionBlurFactor).toDouble()); + spinMotionBlurSteps->setValue(view->view()->feature(QGLView::qglMotionBlurSteps).toInt()); + spinBloomFactor->setValue(view->view()->feature(QGLView::qglBloomFactor).toDouble()); + spinBloomRadius->setValue(view->view()->feature(QGLView::qglBloomRadius).toInt()); + spinBloomThreshold->setValue(view->view()->feature(QGLView::qglBloomThreshold).toDouble()); + spinSSAORadius->setValue(view->view()->feature(QGLView::qglSSAORadius).toInt()); + groupDOF->setChecked(view->view()->isFeatureEnabled(QGLView::qglDepthOfFieldEnabled)); + checkDOFAutoFocus->setChecked(view->view()->isFeatureEnabled(QGLView::qglDepthOfFieldAutoFocusEnabled)); + spinDOFFocus->setValue(view->view()->feature(QGLView::qglDepthOfFieldFocus).toDouble()); + spinDOFDiaphragm->setValue(view->view()->feature(QGLView::qglDepthOfFieldDiaphragm).toDouble()); + spinDOFSpeed->setValue(view->view()->feature(QGLView::qglDepthOfFieldAutoFocusSpeed).toDouble()); + + view->view()->start(-1); + startTimer(1000/60); + + connect(view->view(), SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + connect(view->view(), SIGNAL(keyEvent(Qt::Key, Qt::KeyboardModifiers)), this, SLOT(view_keyEvent(Qt::Key, Qt::KeyboardModifiers))); + //connect(matEditor, SIGNAL(changed()), this, SLOT(materialChanged())); + sceneTree->assignQGLView(view->view()); + matEditor->assignQGLView(view->view()); + + session.load(); + + //matEditor->setMaterial(const_cast(view->view()->scene()->rootObject()->child(0))->material()); + /*Scene * sc = loadScene("truck.obj"); + //view->view()->scene()->addScene(sc); + sc->rootObject()->moveY(-8); + for (int i = 0; i < 7; ++i) { + sc->rootObject()->moveY(2); + view->view()->scene()->addScene(sc); + } + //view->view()->scene()->dump(); + delete sc;*/ +} + + +QGLViewWindow::~QGLViewWindow() { + session.save(); + //delete ps; +} + + +void QGLViewWindow::changeEvent(QEvent * e) { + QMainWindow::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + retranslateUi(this); + return; + } +} + + +void QGLViewWindow::timerEvent(QTimerEvent * ) { + //static double t = 0.; + //((RendererSimple*)(view->view()->renderer()))->mpos = view->view()->mapFromGlobal(QCursor::pos()); + statusBar()->showMessage(QString("FPS: %1").arg(QString::number(view->view()->currentFPS(), 'f', 2))); +} + + +void QGLViewWindow::loadFile(const QString & path, bool import) { + prev_path = path; + QApplication::setOverrideCursor(Qt::WaitCursor); + QFileInfo fi(path); + Scene * s = nullptr; + if (fi.suffix().toLower() == "qgl") s = loadFromQGLFile(path); + else s = loadScene(path); + QApplication::restoreOverrideCursor(); + if (!s) { + QMessageBox::critical(this, "Import", "Can`t load " + path + "!"); + return; + } + s->setName(fi.baseName()); + if (import) view->scene()->addScene(s); + else { + view->scene()->assignFrom(s); + view->view()->focusOn(view->scene()->boundingBox()); + } + delete s; +} + + +void QGLViewWindow::selectionChanged() { + ObjectBase * sel_obj = view->view()->selectedObject(); + //qDebug() << "selected" << (sel_obj ? sel_obj->name() : "0"); + labelName->setText(sel_obj ? sel_obj->name() : ""); + /**if (cur == 0) box->hide(); + else { + box->setScale(cur->boundingBox().size()); + box->setPos(cur->boundingBox().pos()); + Box3D b = cur->boundingBox().movedTo(-cur->boundingBox().center()); + b.z = -b.z - b.height; + ps->setEmitterRect(b); + cur->addChild(box); + box->show(); + }*/ + objectEditor->setObject(sel_obj); + //qDebug() << sel_obj->boundingBox(); +} + + +void QGLViewWindow::on_actionReset_triggered() { + ///view->view()->removeObject(axis, false); + view->view()->scene()->destroy(); + ///view->view()->addObject(axis); +} + + +void QGLViewWindow::on_actionImport_triggered() { + QStringList fl = QFileDialog::getOpenFileNames(this, "Select files", prev_path, "Supported types(*.qgl *.ase *.3ds *.obj *.dae);;" + "QGLView(*.qgl);;" + "Ascii Scene Export(*.ase);;" + "3D Studio(*.3ds);;" + "Wavefront OBJ(*.obj);;" + "Collada(*.dae)"); + if (fl.isEmpty()) return; + prev_path = fl.back(); + foreach (QString f, fl) + loadFile(f, true); +} + + +void QGLViewWindow::on_actionSave_triggered() { + QString f = QFileDialog::getSaveFileName(this, "Select file", prev_path, "QGLView(*.qgl)"); + if (f.isEmpty()) return; + if (f.right(4).toLower() != ".qgl") + f += ".qgl"; + prev_path = f; + QApplication::setOverrideCursor(Qt::WaitCursor); + saveToQGLFile(f, view->scene()); + QApplication::restoreOverrideCursor(); +} + + +void QGLViewWindow::on_actionSaveSelected_triggered() { + ObjectBase * sel_obj = view->view()->selectedObject(); + if (!sel_obj) return; + QString f = QFileDialog::getSaveFileName(this, "Select file", prev_path, "QGLView(*.qgl)"); + if (f.isEmpty()) return; + if (f.right(4).toLower() != ".qgl") + f += ".qgl"; + prev_path = f; + QApplication::setOverrideCursor(Qt::WaitCursor); + ///saveToQGLFile(f, sel_obj); + QApplication::restoreOverrideCursor(); +} + + +void QGLViewWindow::on_actionOpen_triggered() { + QString f = QFileDialog::getOpenFileName(this, "Select file", prev_path, "Supported types(*.qgl *.ase *.3ds *.obj *.dae);;" + "QGLView(*.qgl);;" + "Ascii Scene Export(*.ase);;" + "3D Studio(*.3ds);;" + "Wavefront OBJ(*.obj);;" + "Collada(*.dae)"); + if (f.isEmpty()) return; + prev_path = f; + loadFile(f); +} + + +void QGLViewWindow::view_keyEvent(Qt::Key k, Qt::KeyboardModifiers m) { + //qDebug() << k; + double spd = 0.2; + if (m.testFlag(Qt::ShiftModifier)) + spd = 0.5; + switch (k) { + case Qt::Key_W: view->view()->camera()->moveForward(spd); break; + case Qt::Key_S: view->view()->camera()->moveBackward(spd); break; + case Qt::Key_A: view->view()->camera()->moveLeft(spd); break; + case Qt::Key_D: view->view()->camera()->moveRight(spd); break; + default: break; + } +} +\ + +void QGLViewWindow::on_pushButton_clicked() { + //view->view()->removeLight(view->view()->lightsCount() - 1); + //setWindowTitle(QString::number(view->view()->lightsCount())); + //QVector3D wp = view->view()->light(0)->worldPos(); + //view->view()->camera()->setPos(wp); + //view->view()->camera()->setAim(wp + (view->view()->light(0)->worldTransform() * QVector4D(view->view()->light(0)->direction)).toVector3D()*100); +} + + +void QGLViewWindow::on_pushButton_3_clicked() { + /*QList ol = view->view()->objects(true); + qDebug() << ol.size(); + foreach (ObjectBase * i, ol) { + //i->VBO().rebuffer(); + }*/ +} + + +void QGLViewWindow::on_actionAdd_plane_triggered() { + ObjectBase * o = new ObjectBase(Primitive::plane()); + o->setName("plane"); + view->addObject(o->clone()); + delete o; +} + + +void QGLViewWindow::on_actionAdd_cube_triggered() { + ObjectBase * o = new ObjectBase(Primitive::cube()); + o->setName("cube"); + view->addObject(o->clone()); + delete o; +} + + +void QGLViewWindow::on_actionAdd_sphere_triggered() { + int seg_xy = 16, seg_z = 16; + seg_xy = QInputDialog::getInt(this, "New sphere", "XY segments:", seg_xy, 1, 200); + seg_z = QInputDialog::getInt(this, "New sphere", "Z segments:", seg_z , 1, 200); + ObjectBase * o = new ObjectBase(Primitive::ellipsoid(seg_xy, seg_z)); + o->setName("sphere"); + view->addObject(o->clone()); + delete o; +} diff --git a/qglengine/qglview_test/qglview_window.h b/qglengine/qglview_test/qglview_window.h new file mode 100644 index 0000000..0fce060 --- /dev/null +++ b/qglengine/qglview_test/qglview_window.h @@ -0,0 +1,131 @@ +/* + Stanley Designer + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef QGLVIEWWINDOW_H +#define QGLVIEWWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ui_qglview_window.h" +#include "formats/loader_qgl.h" +#include "formats/loader_assimp.h" +#include "session_manager.h" +//#include "renderer_rt.h" +#include "qglview.h" +#include "ui_qglview_window.h" + + +class QGLViewWindow: public QMainWindow, public Ui::QGLViewWindow +{ + Q_OBJECT +public: + QGLViewWindow(QWidget * parent = 0); + ~QGLViewWindow(); + + void loadFile(const QString & path, bool import = false); + +private: + // Qt`s overloaded + void changeEvent(QEvent * e); + void timerEvent(QTimerEvent * ); + + QTranslator translator; + QString prev_path; + //GLPrimitiveCube * box; + Material m; + SessionManager session; + bool isChanged; + +private slots: + void on_spinFOV_valueChanged(double val) {view->view()->setFOV(val);} + void on_spinDepthStart_valueChanged(double val) {view->view()->setDepthStart(val);} + void on_spinDepthEnd_valueChanged(double val) {view->view()->setDepthEnd(val);} + void on_comboViewRenderMode_currentIndexChanged(int val) {static int modes[] = {GL_POINT, GL_LINE, GL_FILL}; view->view()->setRenderMode((ObjectBase::RenderMode)modes[val]);} + void on_groupHoverHalo_clicked(bool val) {view->view()->setHoverHaloEnabled(val);} + void on_groupSelectionHalo_clicked(bool val) {view->view()->setSelectionHaloEnabled(val);} + void on_spinHoverHaloFill_valueChanged(double val) {view->view()->setHoverHaloFillAlpha(val);} + void on_spinSelectionHaloFill_valueChanged(double val) {view->view()->setSelectionHaloFillAlpha(val);} + void on_colorHoverHalo_colorChanged(QColor color) {view->view()->setHoverHaloColor(color);} + void on_colorSelectionHalo_colorChanged(QColor color) {view->view()->setSelectionHaloColor(color);} + void on_checkFXAA_clicked(bool val) {view->view()->setFeature(QGLView::qglFXAA, val);} + void on_colorBack_colorChanged(QColor color) {view->view()->setBackColor(color);} + void on_colorAmbient_colorChanged(QColor color) {view->view()->setAmbientColor(color);} + void on_checkCameraOrbit_clicked(bool val) {view->view()->setCameraOrbit(val);} + void on_checkCameraLight_clicked(bool val) {view->view()->setCameraLightOn(val);} + void on_spinViewLineWidth_valueChanged(double val) {view->view()->setLineWidth(val);} + + void on_groupShadows_clicked(bool val) {view->view()->setFeature(QGLView::qglShadowsEnabled, val);} + void on_groupEyeAccomodation_clicked(bool val) {view->view()->setFeature(QGLView::qglEyeAccomodationEnabled, val);} + void on_groupBloom_clicked(bool val) {view->view()->setFeature(QGLView::qglBloomEnabled, val);} + void on_groupMotionBlur_clicked(bool val) {view->view()->setFeature(QGLView::qglMotionBlurEnabled, val);} + void on_groupReflections_clicked(bool val) {view->view()->setFeature(QGLView::qglReflectionsEnabled, val);} + void on_checkSoftShadows_clicked(bool val) {view->view()->setFeature(QGLView::qglShadowsSoftEnabled, val);} + void on_groupSSAO_clicked(bool val) {view->view()->setFeature(QGLView::qglSSAOEnabled, val);} + void on_groupDOF_clicked(bool val) {view->view()->setFeature(QGLView::qglDepthOfFieldEnabled, val);} + void on_checkDOFAutoFocus_clicked(bool val) {view->view()->setFeature(QGLView::qglDepthOfFieldAutoFocusEnabled, val);} + void on_spinDOFFocus_valueChanged(double val) {view->view()->setFeature(QGLView::qglDepthOfFieldFocus, val);} + void on_spinDOFDiaphragm_valueChanged(double val) {view->view()->setFeature(QGLView::qglDepthOfFieldDiaphragm, val);} + void on_spinDOFSpeed_valueChanged(double val) {view->view()->setFeature(QGLView::qglDepthOfFieldAutoFocusSpeed, val);} + void on_spinAccom_valueChanged(double val) {view->view()->setFeature(QGLView::qglEyeAccomodationTime, val);} + void on_spinAccomMS_valueChanged(double val) {view->view()->setFeature(QGLView::qglEyeAccomodationMaxSpeed, val);} + void on_checkReflectionsBlur_clicked(bool val) {view->view()->setFeature(QGLView::qglReflectionsBlur, val);} + void on_spinShadowmapSize_valueChanged(double val) {view->view()->setFeature(QGLView::qglShadowsMapSize, val);} + void on_spinMotionBlurFactor_valueChanged(double val) {view->view()->setFeature(QGLView::qglMotionBlurFactor, val);} + void on_spinMotionBlurSteps_valueChanged(int val) {view->view()->setFeature(QGLView::qglMotionBlurSteps, val);} + void on_spinBloomFactor_valueChanged(double val) {view->view()->setFeature(QGLView::qglBloomFactor, val);} + void on_spinBloomRadius_valueChanged(int val) {view->view()->setFeature(QGLView::qglBloomRadius, val);} + void on_spinBloomThreshold_valueChanged(double val) {view->view()->setFeature(QGLView::qglBloomThreshold, val);} + void on_spinSSAORadius_valueChanged(int val) {view->view()->setFeature(QGLView::qglSSAORadius, val);} + + void on_actionExit_triggered() {close();} + void on_actionReset_triggered(); + void on_actionImport_triggered(); + void on_actionSave_triggered(); + void on_actionSaveSelected_triggered(); + void on_actionOpen_triggered(); + + void selectionChanged(); + void view_keyEvent(Qt::Key k, Qt::KeyboardModifiers m); + + void on_pushButton_clicked(); + void on_pushButton_2_clicked() {view->view()->reloadShaders();} + void on_pushButton_3_clicked(); + + void on_actionAdd_plane_triggered(); + void on_actionAdd_cube_triggered(); + void on_actionAdd_sphere_triggered(); + +public slots: + +signals: + +private: + QMatrix4x4 cam_mat; + +}; + +#endif // QGLVIEWWINDOW_H diff --git a/qglengine/qglview_test/qglview_window.ui b/qglengine/qglview_test/qglview_window.ui new file mode 100644 index 0000000..79f11d9 --- /dev/null +++ b/qglengine/qglview_test/qglview_window.ui @@ -0,0 +1,1349 @@ + + + QGLViewWindow + + + + 0 + 0 + 1125 + 1032 + + + + QGLView converter + + + + + + + Qt::Horizontal + + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 10 + 75 + true + + + + + + + Qt::AlignCenter + + + + + + + 0 + + + + View + + + + + + 0 + + + + Common + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + FOV + + + + + + + 0.100000000000000 + + + 179.900000000000006 + + + 60.000000000000000 + + + + + + + Depth + + + + + + + 0 + + + + + 3 + + + 999999999.000000000000000 + + + 1.000000000000000 + + + + + + + + 0 + 0 + + + + - + + + + + + + 3 + + + 999999999.000000000000000 + + + + + + + + + Renderer + + + + + + + 0 + + + + Simple + + + + + Deferred shading + + + + + RT + + + + + + + + Draw mode + + + + + + + 2 + + + + Point + + + + + Wireframe + + + + + Solid + + + + + + + + Back color + + + + + + + + 10 + 10 + 10 + + + + + + + + Ambient + + + + + + + + 10 + 10 + 10 + + + + + + + + MSAA + + + + + + + FXAA + + + + + + + Hover halo + + + true + + + + + + + 255 + 0 + 251 + + + + true + + + + + + + Fill + + + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.300000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + + + + + + + + Selection halo + + + true + + + + + + + 0 + 143 + 239 + + + + true + + + + + + + Fill + + + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.300000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + + + + + + + + Camera + + + + + + Orbit + + + true + + + + + + + Camera Light + + + true + + + + + + + + + + 0.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Line width + + + + + + + + Features + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 960 + 737 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Shadows + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Shadowmap size + + + + + + + 16.000000000000000 + + + 2048.000000000000000 + + + 512.000000000000000 + + + 0 + + + 16.000000000000000 + + + 512.000000000000000 + + + true + + + + + + + Soft + + + true + + + + + + + + + + Bloom + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Factror + + + + + + + 0.000000000000000 + + + 100.000000000000000 + + + 1.000000000000000 + + + 1 + + + 0.100000000000000 + + + 1.000000000000000 + + + false + + + + + + + 1.000000000000000 + + + 128.000000000000000 + + + 8.000000000000000 + + + 0 + + + 1.000000000000000 + + + 4.000000000000000 + + + false + + + + + + + Radius + + + + + + + Threshold + + + + + + + 0.000000000000000 + + + 1.000000000000000 + + + 0.900000000000000 + + + 2 + + + 0.050000000000000 + + + 0.100000000000000 + + + false + + + + + + + + + + Motion blur + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Factror + + + + + + + Steps + + + + + + + 0.000000000000000 + + + 10000.000000000000000 + + + 1.000000000000000 + + + 1 + + + 0.100000000000000 + + + 1.000000000000000 + + + false + + + + + + + 1.000000000000000 + + + 128.000000000000000 + + + 8.000000000000000 + + + 0 + + + 1.000000000000000 + + + 4.000000000000000 + + + false + + + + + + + + + + Eye accomodation + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Time + + + + + + + Max speed + + + + + + + 0.000000000000000 + + + 256.000000000000000 + + + 32.000000000000000 + + + true + + + + + + + 0.010000000000000 + + + 1.000000000000000 + + + 0.100000000000000 + + + 3 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + + + + Depth of field + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Diaphragm + + + + + + + Max speed + + + + + + + 0.100000000000000 + + + 1024.000000000000000 + + + 8.000000000000000 + + + true + + + + + + + 0.010000000000000 + + + 10.000000000000000 + + + 0.100000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Focus + + + + + + + 0.100000000000000 + + + 1000.000000000000000 + + + 1.000000000000000 + + + true + + + 999999.000000000000000 + + + + + + + Auto focus + + + + + + + + + + Reflections + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Blur + + + + + + + + + + SSAO + + + true + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Time + + + + + + + 0.000000000000000 + + + 16.000000000000000 + + + 5.000000000000000 + + + 0 + + + 1.000000000000000 + + + 4.000000000000000 + + + false + + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + + + + + + + + remove light + + + + + + + reload shaders + + + + + + + rebuff all + + + + + + + + 10 + 10 + 10 + + + + false + + + + + + + Qt::Vertical + + + + 20 + 107 + + + + + + + + + Object + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + Material + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 999 + 853 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + Страница + + + + + + + 1 + + + + + + + + + + + + + + 0 + 0 + + + + + + Scene + + + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + + + + 0 + 0 + 1125 + 24 + + + + + File + + + + + + + + + + + + + Scene + + + + + + + + + + + + toolBar_2 + + + TopToolBarArea + + + false + + + + + + + + + :/icons/application-exit.png:/icons/application-exit.png + + + Exit + + + + + + :/icons/document-import.png:/icons/document-import.png + + + Import ... + + + Ctrl+I + + + + + + :/icons/document-open.png:/icons/document-open.png + + + Open ... + + + Ctrl+O + + + + + + :/icons/document-save-all.png:/icons/document-save-all.png + + + Save ... + + + Ctrl+S + + + + + + :/icons/document-new.png:/icons/document-new.png + + + Reset + + + Ctrl+N + + + + + + :/icons/document-save.png:/icons/document-save.png + + + Save selected ... + + + Save selected + + + Ctrl+Shift+S + + + + + Add cube + + + + + Add sphere + + + + + Add plane + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+ + GLWidget + QWidget +
glwidget.h
+ 1 +
+ + ObjectEditor + QWidget +
widgets/object_editor.h
+ 1 +
+ + PropertyEditor + QTreeWidget +
widgets/propertyeditor.h
+
+ + SceneTree + QWidget +
scene_tree.h
+ 1 +
+ + MaterialsEditor + QWidget +
widgets/materials_editor.h
+ 1 +
+
+ + + + + + +
diff --git a/qglengine/renderer.cpp b/qglengine/renderer.cpp new file mode 100644 index 0000000..b639440 --- /dev/null +++ b/qglengine/renderer.cpp @@ -0,0 +1,389 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "renderer.h" +#include "qglview.h" +#include "glmesh.h" +#include "gltexture_manager.h" +#include + +using namespace QGLEngineShaders; + + +Renderer::Renderer(QGLView * view_): RendererBase(view_), + fbo_selection(view_, 4), + fbo_ds (view_, 5, true , GL_RGBA16F), + fbo_out (view_, 3, false, GL_RGBA16F), + fbo_hsmall (view_, 1, false, GL_RGB16F ), + rend_mat(this), rend_service(this) { + quad = Primitive::plane(2., 2.); + sel_frame = quad->clone(); + cam_light = new Light(); + cam_light->intensity = 0.75; + cam_light->setName("Camera_Light"); + line_thick_ = 2.; + id_hover = 0; + + 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[srService] = "service.glsl"; + + shader_files[srGeometryPass] = "ds_geom.glsl"; + shader_files[srLightingPass] = "ds_light.glsl"; + shader_files[srFinalPass ] = "ds_final.glsl"; + + /*shaders << ShaderPair("FXAA", &shader_fxaa) + << ShaderPair("dsl_pass_0", &shader_ds_0) + << ShaderPair("dsl_pass_1", &shader_ds_1) + << ShaderPair("hdr", &shader_hdr) + << ShaderPair("downscale", &shader_small) + << ShaderPair("bloom_pass_0", &shader_bloom_0) + << ShaderPair("bloom_pass_1", &shader_bloom_1) + << ShaderPair("fbo_add", &shader_fbo_add) + << ShaderPair("motion_blur", &shader_motion_blur) + << ShaderPair("shadow", &shader_shadow) + << ShaderPair("ssr", &shader_ssr) + << ShaderPair("ssr_blur", &shader_ssr_blur) + << ShaderPair("ssr_merge", &shader_ssr_merge) + << ShaderPair("ssao_blur", &shader_ssao_blur) + << ShaderPair("ssao_merge", &shader_ssao_merge) + << ShaderPair("dof", &shader_dof);*/ + exposure_ = 1.; + edit_mode = need_init_shaders = is_camera_light = true; + proc_sel_pbo = false; +} + + +Renderer::~Renderer() { + delete quad; + delete sel_frame; + delete cam_light; + qDeleteAll(shaders.values()); +} + + +void Renderer::init(int width, int height) { + resize(width, height); + rend_mat.init(width, height); + rend_service.init(width, height); + initQuad(quad); + initTextureArrays(); + need_init_shaders = true; +} + + +void Renderer::resize(int width, int height) { + rend_mat.resize(width, height); + rend_service.resize(width, height); + fbo_selection.enablePixelBuffer(); + fbo_selection.resize(width, height); + fbo_ds .resize(width, height); + fbo_out .resize(width, height); + fbo_hsmall .resize(width / 16, height / 16); + line_thick_ = lineThickness() + 1.; +} + + +void Renderer::reloadShaders() { + QMapIterator it(shader_files); + while (it.hasNext()) { + it.next(); + loadShadersMulti(shaders[it.key()], "shaders/" + it.value()); + } + need_init_shaders = true; +} + + +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; +} + + +void Renderer::initShaders() { + if (!need_init_shaders) return; + need_init_shaders = false; + initUniformBuffer(shaders.value(srGeometryPass), &buffer_materials , bpMaterials , "QGLMaterialData" ); + initUniformBuffer(shaders.value(srLightingPass), &buffer_materials , bpMaterials , "QGLMaterialData" ); + initUniformBuffer(shaders.value(srLightingPass), &buffer_lights , bpLightParameters, "QGLLightParameterData"); + initUniformBuffer(shaders.value(srLightingPass), &buffer_lights_pos, bpLightPositions , "QGLLightPositionData" ); +} + + +void Renderer::releaseShader() { + view->glUseProgram(0); +} + + +void Renderer::generateObjectsID(Scene & scene) { + ids.clear(); + QMapIterator > it(scene.geometries_used); + while (it.hasNext()) { + it.next(); + foreach (ObjectBase * o, it.value()) { + uint id = qHash(o); + ids[id] = o; + o->id_ = id; + } + } +} + + +void Renderer::fillSelectionsBuffer(const QList & ol) { + cur_selections_.resize(ol.size()); + for (int i = 0; i < ol.size(); ++i) { + cur_selections_[i] = (ol[i]->isSelected(true) ? 1 : 0); + } +} + + +void Renderer::fillObjectsBuffer(const QList & ol, RenderPass pass) { + cur_objects_.resize(ol.size()); + for (int i = 0; i < ol.size(); ++i) { + Object & so(cur_objects_[i]); + ObjectBase * o = ol[i]; + if (o->material()) { + so.material = o->material()->_index; + so.color = QVector4D(1,1,1,1); + } else { + so.material = 0; + so.color = QColor2QVector(o->color_); + } + so.object_id = o->id_; + o->worldTransform().transposed().copyDataTo(so.modelmatrix); + //qDebug() << "load obj" << o->name() << o->worldTransform(); + } + //qDebug() << "fillObjectsBuffer" << ol.size(); + +} + + +void Renderer::renderObjects(Scene & scene, RenderPass pass) { + QOpenGLExtraFunctions * f = view; + QMapIterator > it(scene.geometries_used); + while (it.hasNext()) { + it.next(); + Mesh * mesh = it.key(); + if (mesh->objects_changed) { + mesh->objects_changed = false; + fillObjectsBuffer(it.value(), pass); + mesh->loadObjects(f, cur_objects_); + } + if (mesh->selected_changed && edit_mode) { + mesh->selected_changed = false; + fillSelectionsBuffer(it.value()); + mesh->loadSelections(f, cur_selections_); + } + mesh->draw(f, it.value().size()); + } +} + + +void Renderer::renderSelection(Scene & scene) { + QOpenGLShaderProgram * prog = 0; + if (bindShader(srSelectionFill, &prog)) { + view->hov_objects.clear(); + id_hover = 0; + if (fbo_selection.queriedPoints() > 0) { + if (fbo_selection.queriedPoints() == 1) { + id_hover = fbo_selection.getPoint(); + view->hov_objects.resize(1); + view->hov_objects[0] = ids.value(id_hover); + //qDebug() << id_hover; + } else { + QVector points = fbo_selection.getPoints(); + QSet ids_hover; + foreach (uint i, points) + ids_hover << i; + view->hov_objects.clear(); + foreach (uint i, ids_hover) + view->hov_objects << ids.value(i); + //qDebug() << ids_hover; + } + } + + fbo_selection.bind(); + + fbo_selection.setWriteBuffers(); + glEnableDepth(); + glClearFramebuffer(QColor(0,0,0,0)); + setUniformCamera(prog, view->camera()); + renderObjects(scene, rpSelection); + //mouse_rect = fbo_selection.rect(); + if (mouse_rect.isNull()) + fbo_selection.queryPoint(0, mouse_pos); + else + fbo_selection.queryPoints(0, mouse_rect); + + //qDebug() << id_hover; + fbo_selection.bindColorTextures(); + fbo_selection.setWriteBuffers(); + if (!view->hoverHalo_ && !view->selectionHalo_) + glClearFramebuffer(QColor(0,0,0,0), false); + else { + bindShader(srSelectionHalo, &prog); + setUniformHalo(prog, "hover" , view->hoverHaloColor() , view->hoverHaloFillAlpha()); + setUniformHalo(prog, "selection", view->selectionHaloColor(), view->selectionHaloFillAlpha()); + prog->setUniformValue("has_hover" , view->hoverHalo_ && (id_hover > 0) ? 1.f : 0.f); + prog->setUniformValue("has_selection", view->selectionHalo_ ? 1.f : 0.f); + prog->setUniformValue("fb_hover" , (int)sbrSrcHover); + prog->setUniformValue("fb_selection", (int)sbrSrcSelect); + prog->setUniformValue("hover_id", QVector4D(float( id_hover & 0xFF) / 255.f, + float((id_hover >> 8 ) & 0xFF) / 255.f, + float((id_hover >> 16) & 0xFF) / 255.f, + float((id_hover >> 24) & 0xFF) / 255.f)); + renderQuad(prog, quad, view->camera()); + } + + fbo_selection.release(); + } +} + + +void Renderer::renderSelectionFrame() { + QOpenGLShaderProgram * prog = 0; + if (bindShader(srSelectionFrame, &prog)) { + QMatrix4x4 mat; + double mrx = mouse_rect.x(), mrw = mouse_rect.width() , vw = view->width(); + double mry = mouse_rect.y(), mrh = mouse_rect.height(), vh = view->height(); + mat.translate(-1. + (mrw + mrx*2) / vw, 1. - (mrh + mry*2) / vh, 0.); + mat.scale(mrw / vw, mrh / vh, 0.); + 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); + renderQuad(prog, sel_frame); + glDisable(GL_BLEND); + } +} + + +void Renderer::renderScene() { + initShaders(); + QOpenGLExtraFunctions * f = view; + Scene & scene(*(view->scene_)); + Camera * cam = view->camera(); + QOpenGLShaderProgram * prog = 0; + bool scene_changed = scene.prepare(); + + /// reload materials on change + if (scene_changed || scene.need_reload_materials) { + if (scene.need_reload_materials) + maps_hash = 0; + generateObjectsID(scene); + reloadMaterials(scene); + if (edit_mode) + recreateMaterialThumbnails(); + emit view->materialsChanged(); + } + + /// material thumbnails + if (edit_mode && !scene_changed) { + rend_mat.procQueue(); + } + + /// lights + QList ll = scene.lights_used; + if (is_camera_light) { + ll << cam_light; + cam_light->setPos(cam->pos()); + } + if (scene.lights_changed) { + scene.lights_changed = false; + reloadLightsParameters(ll); + } + reloadLightsPositions(ll, cam); + + /// selection + if (edit_mode) { + renderSelection(scene); + } + + /// geometry pass + fbo_ds.bind(); + glEnableDepth(); + glClearFramebuffer(); + if (bindShader(srGeometryPass, &prog)) { + setUniformMaps(prog); + setUniformCamera(prog, cam); + textures_empty.bind(f, tarEmpty); + textures_maps .bind(f, tarMaps ); + renderObjects(scene, rpSolid); + } + fbo_ds.release(); + + /// lighting pass + fbo_ds.bindColorTextures(); + fbo_ds.bindDepthTexture(5); + fbo_out.bind(); + if (bindShader(srLightingPass, &prog)) { + setUniformCamera(prog, cam); + setUniformViewCorners(prog, cam); + 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_count", ll.size()); + glClearFramebuffer(view->backColor(), false); + renderQuad(prog, quad, cam); + /*QVector _fb = fbo_out.grabF(0); + if (!_fb.isEmpty()) { + double sum = 0.; + foreach (float f, _fb) sum += f; + sum /= _fb.size(); + qDebug() << "sum =" << sum; + }*/ + } + fbo_out.release(); + + /// apply hovers and selection frame + if (edit_mode) { + if (bindShader(srSelectionApply, &prog)) { + fbo_selection.bindColorTextures(); + fbo_out.bindColorTexture(0); + prog->setUniformValue("fb_out" , 0); + prog->setUniformValue("fb_hover" , (int)sbrHovered ); + prog->setUniformValue("fb_select", (int)sbrSelected); + renderQuad(prog, quad, cam); + + if (!mouse_rect.isNull()) { + renderSelectionFrame(); + } + + rend_service.renderService(); + } + } else { + fbo_out.blit(0, 0, 0, fbo_out.rect(), QRect(QPoint(), view->size())); + } + +} + + +void Renderer::setCameraLightOn(bool on) { + is_camera_light = on; + view->scene()->setLightsChanged(); +} diff --git a/qglengine/renderer.h b/qglengine/renderer.h new file mode 100644 index 0000000..1586581 --- /dev/null +++ b/qglengine/renderer.h @@ -0,0 +1,123 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef RENDERER_H +#define RENDERER_H + +#include "renderer_base.h" +#include "renderer_material.h" +#include "renderer_service.h" +#include + + +class Renderer: public RendererBase { + friend class QGLView; + friend class RendererMaterial; + friend class RendererService; + enum RenderPass { + rpSolid, + rpTransparent, + rpSelection, + rpShadow, + rpNoProc, + }; + enum ShaderRole { + // Selection + srSelectionFill, + srSelectionHalo, + srSelectionApply, + srSelectionFrame, + + // Service + srService, + + // Deferred shading + srGeometryPass, + srLightingPass, + srFinalPass, + }; + enum DeferredBufferRole { + dbrDiffuseRough, + dbrNormalReflect, + dbrSpecularHeight, + dbrEmissionBitangX, + dbrSpeedBitangXY, + }; + enum SelectionBufferRole { + sbrSrcHover, + sbrSrcSelect, + sbrHovered, + sbrSelected + }; + +public: + Renderer(QGLView * view_); + virtual ~Renderer(); + + void init(int width, int height); + void resize(int width, int height); + void reloadShaders(); + void renderScene(); + void setCameraLightOn(bool on); + bool isCameraLightOn() const {return is_camera_light;} + + QImage materialThumbnail(Material * m) {return rend_mat.materialThumbnail(m);} + void recreateMaterialThumbnails(bool force_all = false) {rend_mat.recreateMaterialThumbnails(force_all);} + +protected: + void generateObjectsID(Scene & scene); + void fillSelectionsBuffer(const QList & ol); + void fillObjectsBuffer(const QList & ol, RenderPass pass); + void reloadObjects(); + void renderObjects(Scene & scene, RenderPass pass); + void renderSelection(Scene & scene); + void renderSelectionFrame(); + + bool bindShader(ShaderRole role, QOpenGLShaderProgram ** ret = 0); + void initShaders(); + void releaseShader(); + +private: + float exposure_, line_thick_; + bool edit_mode, proc_sel_pbo, need_init_shaders, is_camera_light; + Framebuffer fbo_selection, fbo_ds, fbo_out, fbo_hsmall; + /*QOpenGLShaderProgram * shader_fxaa, * shader_ds_0, * shader_ds_1, * shader_hdr, * shader_small; + QOpenGLShaderProgram * shader_bloom_0, * shader_bloom_1, * shader_motion_blur, * shader_fbo_add; + QOpenGLShaderProgram * shader_shadow, * shader_ssr, * shader_ssr_blur, * shader_ssr_merge; + QOpenGLShaderProgram * shader_ssao_blur, * shader_ssao_merge, * shader_dof;*/ + QMap shader_files; + QMap shaders; + + RendererMaterial rend_mat; + RendererService rend_service; + + Mesh * quad, * sel_frame; + Light * cam_light; + + QHash ids; + uint id_hover; + QPoint mouse_pos; + QRect mouse_rect; + QMatrix4x4 prev_view, prev_proj; + QMatrix3x3 nm; + QVector4D corner_dirs[4]; + QVector hcontent; + +}; + +#endif // RENDERER_H diff --git a/qglengine/renderer_base.cpp b/qglengine/renderer_base.cpp new file mode 100644 index 0000000..73cdbe0 --- /dev/null +++ b/qglengine/renderer_base.cpp @@ -0,0 +1,275 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "renderer_base.h" +#include "qglview.h" +#include "glmesh.h" +#include "gltexture_manager.h" +#include "glshaders_headers.h" + +using namespace QGLEngineShaders; + + +RendererBase::RendererBase(QGLView * view_): + view(view_), + buffer_materials (GL_UNIFORM_BUFFER, GL_STREAM_DRAW), + buffer_lights (GL_UNIFORM_BUFFER, GL_STREAM_DRAW), + buffer_lights_pos(GL_UNIFORM_BUFFER, GL_STREAM_DRAW), + textures_empty(false), + textures_maps(true) +{ + textures_manager = new TextureManager(view); + maps_size = QSize(512, 512); + maps_hash = 0; + +} + + +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->viewMatrix() * cam->offsetMatrix(); + mat_proj = cam->projectionMatrix(w / h); + } + prog->setUniformValue("z_near", cam->depthStart()); + } + prog->setUniformValue("dt", QVector2D(1. / w, 1. / h)); + prog->setUniformValue("qgl_ViewMatrix" , mat_view); + prog->setUniformValue("qgl_ViewProjMatrix", mat_proj * mat_view); +} + + +void RendererBase::setUniformViewCorners(QOpenGLShaderProgram * prog, Camera * cam, QSize viewport) { + double w = view->width(), h = view->height(); + if (viewport.isValid()) { + w = viewport.width(); + h = viewport.height(); + } + QMatrix4x4 mproji = cam->projectionMatrix(w / h).inverted(); + QVector4D corner_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) + prog->setUniformValue(QString("view_corners[%1]").arg(i).toLatin1().constData(), corner_dirs[i]); +} + + +void RendererBase::reloadMaterials(Scene & scene) { + //qDebug() << "reloadMaterias"; + QList maps[2]; + QMap tex_layers[2]; + foreach (Material * m, scene.materials) { + if (m->map_diffuse .hasBitmap()) maps[0] << &(m->map_diffuse ); + if (m->map_normal .hasBitmap()) maps[1] << &(m->map_normal ); + if (m->map_specular .hasBitmap()) maps[0] << &(m->map_specular ); + 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()); + if (maps_hash != cur_maps_hash) { + maps_hash = cur_maps_hash; + textures_maps.resize(view, maps_size, layers_count); + textures_maps.bind(view); + for (int i = 0; i < 2; ++i) { + QMutableMapIterator it(tex_layers[i]); + while (it.hasNext()) { + it.next(); + QImage im = textures_manager->loadTextureImage(it.key(), i == 1); + textures_maps.load(view, im, ++cl); + it.value() = cl; + } + foreach (Map * m, maps[i]) { + m->_layer = tex_layers[i].value(m->bitmap_path); + //qDebug() << "assign" << m->bitmap_path << "layer" << m->_layer; + } + } + textures_maps.mipmaps(view); + qDebug() << "load" << (cl+1) << "bitmaps"; + } + + QGLMaterial glm; + cur_materials_.clear(); + cur_materials_ << glm; + foreach (Material * m, scene.materials) { + if (cur_materials_.size() >= max_materials) { + qDebug() << "[QGLEngine] Warning: Too many materials! Maximum" << max_materials; + break; + } + //m->load(textures_manager); + m->_index = cur_materials_.size(); + m->_changed = false; + glm.color_diffuse = QColor2QVector(m->color_diffuse ); + glm.color_specular = QColor2QVector(m->color_specular); + 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_specular .copyToQGLMap(glm.map[mtSpecular ]); + m->map_roughness.copyToQGLMap(glm.map[mtRoughness]); + m->map_emission .copyToQGLMap(glm.map[mtEmission ]); + m->map_relief .copyToQGLMap(glm.map[mtRelief ]); + cur_materials_ << glm; + } + //qDebug() << "load" << cur_materials_.size() << "materials"; + //textures_maps.resize(maps_size, ); + //cur_materials_[0].color_diffuse = QColor2QVector(Qt::red); + buffer_materials.bind(view); + buffer_materials.resize(view, cur_materials_.size() * sizeof(QGLMaterial)); + buffer_materials.load(view, cur_materials_.constData(), cur_materials_.size() * sizeof(QGLMaterial)); + scene.need_reload_materials = false; + +} + + +void RendererBase::reloadLightsParameters(const QList & lights) { + cur_lights_params_.resize(qMin(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 = 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.intensity = l->intensity; + so.startAngle = ang_start; + so.startAngleCos = cos(ang_start * deg2rad); + so.endAngle = ang_end; + so.endAngleCos = cos(ang_end * deg2rad); + so.color = QColor2QVector(l->color_); + so.constantAttenuation = l->decay_const; + so.linearAttenuation = l->decay_linear; + so.quadraticAttenuation = l->decay_quadratic; + //so.shadow = shadow; + //so.shadowColor = shadow; + } + buffer_lights.bind(view); + buffer_lights.resize(view, cur_lights_params_.size() * sizeof(QGLLightParameter)); + buffer_lights.load(view, cur_lights_params_.constData(), cur_lights_params_.size() * sizeof(QGLLightParameter)); +} + + +void RendererBase::reloadLightsPositions(const QList & lights, Camera * cam) { + cur_lights_pos_.resize(qMin(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 = lights[i]; + QMatrix4x4 m = mat * l->worldTransform(); + QVector4D pos(0, 0, 0, 1.), dir(l->direction, 1);//, dir0(light->dir0), dir1(light->dir1); + pos = m * pos; + dir = ((m * dir) - pos).normalized(); + so.position = pos; + so.direction = dir; + //so.shadowMatrix = l->shadow_matrix; + } + buffer_lights_pos.bind(view); + buffer_lights_pos.resize(view, cur_lights_pos_.size() * sizeof(QGLLightPosition)); + buffer_lights_pos.load(view, cur_lights_pos_.constData(), cur_lights_pos_.size() * sizeof(QGLLightPosition)); +} + + +void RendererBase::markReloadTextures() { + maps_hash = 0; + textures_manager->clearImageCache(); + view->scene_->need_reload_materials = true; +} + + +void RendererBase::setMapsSize(QSize sz) { + maps_size = sz; + markReloadTextures(); +} + + +void RendererBase::initQuad(Mesh * mesh, QMatrix4x4 mat) { + QGLEngineShaders::Object quab_object; + mat.transposed().copyDataTo(quab_object.modelmatrix); + mesh->init(view); + mesh->loadObject(view, quab_object); +} + + +void RendererBase::renderQuad(QOpenGLShaderProgram * prog, Mesh * mesh, Camera * cam) { + glDisableDepth(); + setUniformCamera(prog, cam, false); + mesh->draw(view, 1); +} diff --git a/qglengine/renderer_base.h b/qglengine/renderer_base.h new file mode 100644 index 0000000..1d2cbc2 --- /dev/null +++ b/qglengine/renderer_base.h @@ -0,0 +1,62 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef RENDERER_BASE_H +#define RENDERER_BASE_H + +#include "glshaders_types.h" +#include "gltexturearray.h" +#include "glbuffer.h" + + +class RendererBase { +public: + RendererBase(QGLView * view_); + ~RendererBase(); + +protected: + void initTextureArrays(); + void initUniformBuffer (QOpenGLShaderProgram * prog, Buffer * buffer, int bind_point, const char * blockName); + void setUniformHalo (QOpenGLShaderProgram * prog, const char * type, QColor color, float fill); + void setUniformMaps (QOpenGLShaderProgram * prog); + void setUniformCamera (QOpenGLShaderProgram * prog, Camera * cam, bool matrices = true, QSize viewport = QSize()); + void setUniformViewCorners(QOpenGLShaderProgram * prog, Camera * cam, QSize viewport = QSize()); + void reloadMaterials(Scene & scene); + void reloadLightsParameters(const QList & lights); + void reloadLightsPositions (const QList & lights, Camera * cam); + void markReloadTextures(); + void setMapsSize(QSize sz); + void initQuad(Mesh * mesh, QMatrix4x4 mat = QMatrix4x4()); + void renderQuad(QOpenGLShaderProgram * prog, Mesh * mesh, Camera * cam = 0); + + QGLView * view; + TextureManager * textures_manager; + QVector cur_objects_; + QVector cur_materials_; + QVector cur_lights_params_; + QVector cur_lights_pos_; + QVector cur_selections_; + Buffer buffer_materials; + Buffer buffer_lights, buffer_lights_pos; + Texture2DArray textures_empty, textures_maps; + QSize maps_size; + uint maps_hash; + +}; + +#endif // RENDERER_BASE_H diff --git a/qglengine/renderer_material.cpp b/qglengine/renderer_material.cpp new file mode 100644 index 0000000..d83a59a --- /dev/null +++ b/qglengine/renderer_material.cpp @@ -0,0 +1,131 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "renderer_material.h" +#include "renderer.h" +#include "qglview.h" +#include "glmesh.h" +#include "gltexture_manager.h" +#include + +using namespace QGLEngineShaders; + + +RendererMaterial::RendererMaterial(Renderer * r_): r(r_), + fbo_mat_thumb(r->view, 6, true , GL_RGBA16F) { + mat_sphere = Primitive::ellipsoid(16, 16); + mat_camera = new Camera(); + mat_camera->setAim(QVector3D()); + mat_camera->setPos(QVector3D(1, 1, 1)); + mat_camera->setFOV(45.); + mat_light = new Light(); + mat_light->setPos(QVector3D(50, 100, 25)); + last_thumb_material = 0; +} + + +RendererMaterial::~RendererMaterial() { + delete mat_sphere; + delete mat_camera; + delete mat_light; +} + + +void RendererMaterial::init(int width, int height) { + resize(width, height); +} + + +void RendererMaterial::resize(int width, int height) { + fbo_mat_thumb.enablePixelBuffer(); + fbo_mat_thumb.resize(256, 256); +} + + +void RendererMaterial::renderMaterial(Material * m) { + //qDebug() << "renderMaterial" << m; + last_thumb_material = m; + QOpenGLShaderProgram * prog = 0; + QOpenGLExtraFunctions * f = r->view; + fbo_mat_thumb.bind(); + glEnableDepth(); + glClearFramebuffer(); + 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::srLightingPass, &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_count", 1); + QList mat_l; + mat_l << mat_light; + r->reloadLightsParameters(mat_l); + r->reloadLightsPositions(mat_l, mat_camera); + glClearFramebuffer(r->view->backColor(), false); + r->renderQuad(prog, r->quad, mat_camera); + r->view->scene()->setLightsChanged(); + } + fbo_mat_thumb.queryImage(5); + fbo_mat_thumb.release(); +} + + +void RendererMaterial::procQueue() { + if (last_thumb_material) { + mat_thumbnails[last_thumb_material] = fbo_mat_thumb.getImage(); + emit r->view->materialThumbnailCreated(last_thumb_material); + last_thumb_material = 0; + } + if (!mat_thumb_queue.isEmpty()) + renderMaterial(mat_thumb_queue.dequeue()); +} + + +QImage RendererMaterial::materialThumbnail(Material * m) { + return mat_thumbnails.value(m); +} + + +void RendererMaterial::recreateMaterialThumbnails(bool force_all) { + if (force_all) { + mat_thumb_queue.clear(); + //qDebug() << "recreateMaterialThumbnails" << view->scene_->materials; + foreach (Material * m, r->view->scene_->materials) + mat_thumb_queue.enqueue(m); + } else { + foreach (Material * m, r->view->scene_->changed_materials) + if (!mat_thumb_queue.contains(m)) + mat_thumb_queue.enqueue(m); + } +} diff --git a/qglengine/renderer_material.h b/qglengine/renderer_material.h new file mode 100644 index 0000000..44c969d --- /dev/null +++ b/qglengine/renderer_material.h @@ -0,0 +1,54 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef RENDERER_MATERIAL_H +#define RENDERER_MATERIAL_H + +#include "glframebuffer.h" +#include + + +class RendererMaterial { + friend class QGLView; + +public: + RendererMaterial(Renderer * r_); + virtual ~RendererMaterial(); + + void init(int width, int height); + void resize(int width, int height); + + QImage materialThumbnail(Material * m); + void recreateMaterialThumbnails(bool force_all = false); + void renderMaterial(Material * m); + void procQueue(); + +private: + Renderer * r; + + Framebuffer fbo_mat_thumb; + Mesh * mat_sphere; + Camera * mat_camera; + Light * mat_light; + QMap mat_thumbnails; + Material * last_thumb_material; + QQueue mat_thumb_queue; + +}; + +#endif // RENDERER_MATERIAL_H diff --git a/qglengine/renderer_service.cpp b/qglengine/renderer_service.cpp new file mode 100644 index 0000000..0d0f471 --- /dev/null +++ b/qglengine/renderer_service.cpp @@ -0,0 +1,136 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define GL_GLEXT_PROTOTYPES +#include +#include "renderer_service.h" +#include "renderer.h" +#include "qglview.h" +#include "glmesh.h" +#include + +using namespace QGLEngineShaders; + + +RendererService::RendererService(Renderer * r_): r(r_) { + line_width = 1; + axis_camera = new Camera(); + axis_camera->setAim(QVector3D()); + axis_camera->setFOV(45.); + axis_mesh = Primitive::arrow(12); + axis_objects.resize(3); + const QVector3D _rot[3] = {QVector3D(0,1,0), QVector3D(-1,0,0), QVector3D(0,0,1)}; + for (int i = 0; i < 3; ++i) { + axis_objects[i].color = QVector4D(0,0,0,1); + axis_objects[i].color[i] = 1.; + QMatrix4x4 m; m.rotate(90., _rot[i]); + m.transposed().copyDataTo(axis_objects[i].modelmatrix); + } + box_vp_scale = 1.; + box_mesh = Primitive::cube(); +} + + +RendererService::~RendererService() { + delete box_mesh; + delete axis_camera; + delete axis_mesh; +} + + +void RendererService::init(int width, int height) { + axis_mesh->loadObjects(r->view, axis_objects); + resize(width, height); +} + + +void RendererService::resize(int width, int height) { + axis_viewport = preferredIconSize(10.); + line_width = lineThickness(); + box_vp_scale = 30. * appScale() / qMax(qMin(width, height), 1); + //qDebug() << axis_viewport; +} + + +void RendererService::fillBoxObjects() { + box_objects.clear(); + Object o; + QList ll = r->view->scene()->lights_used; + QMatrix4x4 v_mat = r->view->camera()->viewMatrix() * r->view->camera()->offsetMatrix(); + QMatrix4x4 lmat; + double vps = tan(r->view->camera()->FOV() / 2. * deg2rad) * box_vp_scale; + foreach (Light * l, ll) { + QVector4D lpos = QVector4D(l->worldPos(), 1.); + double dist = -(v_mat * lpos).z(); + lmat.translate(lpos.toVector3D()); + lmat.scale(dist * vps); + lmat.transposed().copyDataTo(o.modelmatrix); + box_objects << o; + } +} + + +void RendererService::setObjectsColor(QVector & ol, QColor col) { + QVector4D cv = QColor2QVector(col); + for (int i = 0; i < ol.size(); ++i) + ol[i].color = cv; +} + + +void RendererService::renderService() { + QOpenGLShaderProgram * prog = 0; + QOpenGLExtraFunctions * f = r->view; + if (r->bindShader(Renderer::srService, &prog)) { + f->glEnable(GL_MULTISAMPLE); + glEnableDepth(); + f->glClear(GL_DEPTH_BUFFER_BIT); + glEnableDepth(); + + + /// lights + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glDisable(GL_CULL_FACE); + r->setUniformCamera(prog, r->view->camera()); + fillBoxObjects(); + + setObjectsColor(box_objects, Qt::white); + glLineWidth(line_width*3); + box_mesh->loadObjects(f, box_objects); + box_mesh->draw(f, box_objects.size()); + + setObjectsColor(box_objects, Qt::black); + glLineWidth(line_width); + box_mesh->loadObjects(f, box_objects); + box_mesh->draw(f, box_objects.size()); + + glEnable(GL_CULL_FACE); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + + /// axis + f->glViewport(0, 0, axis_viewport.width(), axis_viewport.height()); + axis_camera->setPos(-r->view->camera()->direction() * 3.); + r->setUniformCamera(prog, axis_camera, true, axis_viewport); + //axis_mesh->loadObjects(f, axis_objects); + axis_mesh->draw(f, axis_objects.size()); + f->glViewport(0, 0, r->view->width(), r->view->height()); + + + f->glDisable(GL_MULTISAMPLE); + } +} diff --git a/qglengine/renderer_service.h b/qglengine/renderer_service.h new file mode 100644 index 0000000..f96f41f --- /dev/null +++ b/qglengine/renderer_service.h @@ -0,0 +1,53 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef RENDERER_SERVICE_H +#define RENDERER_SERVICE_H + +#include "glframebuffer.h" +#include "glshaders_types.h" +#include + + +class RendererService { + friend class QGLView; + +public: + RendererService(Renderer * r_); + virtual ~RendererService(); + + void init(int width, int height); + void resize(int width, int height); + + void fillBoxObjects(); + void setObjectsColor(QVector & ol, QColor col); + void renderService(); + +private: + Renderer * r; + + Mesh * axis_mesh, * box_mesh; + QVector axis_objects, box_objects; + Camera * axis_camera; + QSize axis_viewport; + int line_width; + double box_vp_scale; + +}; + +#endif // RENDERER_SERVICE_H diff --git a/qglengine/shaders/ds_geom.glsl b/qglengine/shaders/ds_geom.glsl new file mode 100644 index 0000000..26a122b --- /dev/null +++ b/qglengine/shaders/ds_geom.glsl @@ -0,0 +1,64 @@ +// vert // + +out vec3 geom_normal; +out mat3 TBN; +out vec4 object_color; + +void main(void) { + qgl_MaterialIndex = qgl_Material; + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); + + mat3 nmat = qgl_getNormalMatrix(); + geom_normal = normalize(nmat * qgl_Normal); + TBN = nmat * mat3(qgl_Tangent, qgl_Bitangent, qgl_Normal); + object_color = qgl_ObjectColor; +} + + +// frag // + +in vec3 geom_normal; +in mat3 TBN; +in vec4 object_color; + +uniform vec2 dt; +uniform float z_near; + +const vec3 luma = vec3(0.299, 0.587, 0.114); + +void main(void) { + vec2 tc = qgl_FragTexture.xy; + + vec4 diffuse = qgl_materialTexture(QGL_MAP_DIFFUSE, tc, vec4(0)) * qgl_material[qgl_MaterialIndex].color_diffuse * object_color; + + vec3 normal, dn; + dn = qgl_materialTexture(QGL_MAP_NORMAL, tc, -vec4(0.5, 0.5, 1., 0.)).xyz; + //dn.y = -dn.y; + float dn_sl = length(dn); + dn = TBN * dn; + dn *= dn_sl / (length(dn) + 1E-6); + normal = normalize(geom_normal + dn); + + vec4 specular = qgl_materialTexture(QGL_MAP_SPECULAR, tc, vec4(0)) * qgl_material[qgl_MaterialIndex].color_specular; + + float roughness = dot(qgl_materialTexture(QGL_MAP_ROUGHNESS, tc, vec4(0)).rgb, luma); + roughness = clamp(roughness, 0.0001, 0.9999); + + float reflectivity = clamp(qgl_material[qgl_MaterialIndex].reflectivity, 0., 1.); + + vec4 emission = qgl_materialTexture(QGL_MAP_EMISSION, tc, vec4(0)); + emission *= qgl_material[qgl_MaterialIndex].color_emission; + + float height = dot(qgl_materialTexture(QGL_MAP_RELIEF, tc, vec4(0)).rgb, luma); + + qgl_FragData[0] = vec4(diffuse .rgb, roughness ); + qgl_FragData[1] = vec4(normal .xyz, reflectivity); + qgl_FragData[2] = vec4(specular.rgb, height ); + qgl_FragData[3] = vec4(emission.rgb, 0/*bn.x*/); + //qgl_FragData[4] = vec4(speed.xy, bn.yz); + + //ivec2 itc = ivec2(gl_FragCoord.xy); + //qgl_FragData[0].rgb = vec3(dot(n,vec3(0,0,1))); + //qgl_FragData[0].rgb = diffuse.rgb * dot(n,vec3(0,0,1)); +} diff --git a/qglengine/shaders/ds_light.glsl b/qglengine/shaders/ds_light.glsl new file mode 100644 index 0000000..8b3e29f --- /dev/null +++ b/qglengine/shaders/ds_light.glsl @@ -0,0 +1,144 @@ +// vert // + +out vec3 view_dir; + +uniform vec4 view_corners[4]; + +void main(void) { + gl_Position = qgl_ftransform(); + view_dir = view_corners[gl_VertexID].xyz; +} + + +// frag // + +in vec3 view_dir; + +uniform vec2 dt; +uniform float z_near; +uniform sampler2D tex_0, tex_1, tex_2, tex_3, tex_4; +uniform sampler2D tex_d; +uniform int lights_count; + +const vec3 luma = vec3(0.299, 0.587, 0.114); +const float _pe = 2.4e-7; + +vec4 pos, lpos, shp; +vec3 li, si, ldir, halfV, bn, bn2, lwdir; +//vec3 vds, vds2; +float shm_diff, shm_spec, dist, NdotL, NdotH, spot, ldist, diff, sdist, shadow; + +void calcLight(in int index, in vec3 n, in vec3 v) { + lpos = qgl_light_position[index].position; + ldir = lpos.xyz - (pos.xyz * lpos.w); + ldist = length(ldir); + ldir = normalize(ldir); + halfV = normalize(ldir + v); + NdotL = max(dot(n, ldir), 1E-6); + NdotH = max(dot(n, halfV), 1E-6); + spot = step(1.01E-6, NdotL) * qgl_light_parameter[index].intensity; + /*if (qgl_Light[index].endAngle <= 90.) { + float scos = max(dot(-ldir, qgl_Light[index].direction.xyz), 0.); + spot *= scos * step(qgl_Light[index].endAngleCos, scos); + spot *= smoothstep(qgl_Light[index].endAngleCos, qgl_Light[index].startAngleCos, scos); + //lwdir = mat3(mat_viewi) * qgl_Light[index].direction.xyz; + //bn = normalize(cross(lwdir, vec3(1, 0, 0))); + //bn2 = normalize(cross(lwdir, bn)); + float ds = ldist/200.;//max(abs(sdist) / 5000, 0.02); + //spot *= clamp(1. - sdist, 0, 1); + vds = ds * bn.xyz; + vds2 = ds * bn2.xyz; + float shadow = getShadow(index, pos.xyz, vec3(0)) * 3.; + shadow += getShadow(index, pos.xyz, vds ) * 2.; + shadow += getShadow(index, pos.xyz, - vds ) * 2.; + shadow += getShadow(index, pos.xyz, - vds2 ) * 2.; + shadow += getShadow(index, pos.xyz, + vds2 ) * 2.; + //shadow += getShadow(index, pos.xyz, vds - vds2 ) * 1.5; + //shadow += getShadow(index, pos.xyz, vds + vds2 ) * 1.5; + //shadow += getShadow(index, pos.xyz, - vds - vds2 ) * 1.5; + //shadow += getShadow(index, pos.xyz, - vds + vds2 ) * 1.5; + //shadow += getShadow(index, pos.xyz, vds + vds ); + //shadow += getShadow(index, pos.xyz, - vds - vds ); + //shadow += getShadow(index, pos.xyz, - vds2 - vds2); + //shadow += getShadow(index, pos.xyz, + vds2 + vds2); + //shadow += getShadow(index, pos.xyz, vds + vds - vds2 ); + //shadow += getShadow(index, pos.xyz, - vds - vds - vds2 ); + //shadow += getShadow(index, pos.xyz, vds + vds + vds2 ); + //shadow += getShadow(index, pos.xyz, - vds - vds + vds2 ); + //shadow += getShadow(index, pos.xyz, vds - vds2 - vds2); + //shadow += getShadow(index, pos.xyz, vds + vds2 + vds2); + //shadow += getShadow(index, pos.xyz, - vds - vds2 - vds2); + //shadow += getShadow(index, pos.xyz, - vds + vds2 + vds2); + //shadow += shadow += getShadow(index, pos.xyz, vds+vds2)*10; + spot *= mix(1., shadow / 11., shadow_on); + }*/ + //spot /= (qgl_Light[index].constantAttenuation + ldist * (qgl_Light[index].linearAttenuation + ldist * qgl_Light[index].quadraticAttenuation)); + //li += spot * gl_LightSource[index].diffuse.rgb * light_diffuse(0, ldir, n); + //si += spot * qgl_Light[index].color.rgb * shm_diff * light_specular(0, ldir, n, halfV, v, sh_pow); + float NdotLs = NdotL*NdotL; + float NdotHs = NdotH*NdotH; + + float ndlc = (1. - NdotLs) / NdotLs; + float der = NdotLs * (shm_diff + ndlc); + diff = 2. / (1. + sqrt(1. + (1. - shm_diff) * ndlc)); + li += spot * diff * qgl_light_parameter[index].color.rgb;// * light_diffuse(0, ldir, n); + + ndlc = (1. - NdotHs) / NdotHs; + der = NdotHs * (shm_spec + ndlc); + si += spot * (shm_spec / (der*der) / 3.1416) * qgl_light_parameter[index].color.rgb; +} + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + float z = texelFetch(tex_d, tc, 0).r; + if (z == 1.) { + qgl_FragColor = vec4(0); + return; + } + vec4 v0 = texelFetch(tex_0, tc, 0), + v1 = texelFetch(tex_1, tc, 0), + v2 = texelFetch(tex_2, tc, 0), + v3 = texelFetch(tex_3, tc, 0), + v4 = texelFetch(tex_4, tc, 0); + + z = z + z - 1; + z = ((_pe - 2.) * z_near) / (z + _pe - 1.); // infinite depth + + pos.w = 1; + pos.xyz = view_dir * z; + //pos.z = -pos.z; + vec3 v = normalize(-pos.xyz); + + //vec2 sp = gl_FragCoord.xy * dt * 2 - vec2(1, 1); + vec3 diffuse = v0.rgb; + vec3 normal = v1.xyz; + vec3 specular = v2.rgb; + vec3 emission = v3.rgb; + float roughness = v0.w; + float reflectivity = v1.w; + float height = v2.w; + //bn = normalize(vec3(v3.w, v4.zw)); + //bn2 = normalize(cross(n, bn)); + + shm_diff = max(roughness, 0.00001); + roughness = roughness*roughness*roughness; + shm_spec = max(roughness, 0.00001); + //sh_pow = 1. / max(roughness, 0.00001); + li = vec3(0.);//qgl_AmbientLight.color.rgb * qgl_AmbientLight.intensity; + si = vec3(0.); + for (int i = 0; i < lights_count; ++i) + calcLight(i, normal, v); + // calcLight(0, n, v, v2); + + //calcLight(0, normal, v); + + qgl_FragColor.rgb = max(vec3(0), li * diffuse + si * specular + emission); + qgl_FragColor.a = 1; + + + //qgl_FragData[4] = vec4(speed.xy, bn.yz); + + //ivec2 itc = ivec2(gl_FragCoord.xy); + //qgl_FragData[0].rgb = vec3(dot(n,vec3(0,0,1))); + //qgl_FragData[0].rgb = diffuse.rgb * dot(n,vec3(0,0,1)); +} diff --git a/qglengine/shaders/selection.glsl b/qglengine/shaders/selection.glsl new file mode 100644 index 0000000..23909ca --- /dev/null +++ b/qglengine/shaders/selection.glsl @@ -0,0 +1,19 @@ +// vert // + +flat out uint id, select; + +void main(void) { + id = qgl_ObjectID; + select = qgl_ObjectSelected; + gl_Position = qgl_ftransform(); +} + + +// frag // + +flat in uint id, select; + +void main(void) { + qgl_FragData[0] = unpackUnorm4x8(id); + qgl_FragData[1] = vec4(select); +} diff --git a/qglengine/shaders/selection_apply.glsl b/qglengine/shaders/selection_apply.glsl new file mode 100644 index 0000000..13e46b2 --- /dev/null +++ b/qglengine/shaders/selection_apply.glsl @@ -0,0 +1,22 @@ +// vert // + +void main(void) { + gl_Position = qgl_ftransform(); +} + + +// frag // + +uniform sampler2D fb_out, fb_hover, fb_select; + +void main(void) { + ivec2 tc = ivec2(gl_FragCoord.xy); + vec4 src = texelFetch(fb_out , tc, 0); + vec4 hov = texelFetch(fb_hover , tc, 0); + vec4 sel = texelFetch(fb_select, tc, 0); + src.rgb = mix(src.rgb, sel.rgb, sel.a); + src.rgb = mix(src.rgb, hov.rgb, hov.a * 0.667f); + //src.rgb = src.rgb + (sel.rgb*sel.a); + qgl_FragData[0] = src; + //qgl_FragData[0] = vec4(sel.a); +} diff --git a/qglengine/shaders/selection_frame.glsl b/qglengine/shaders/selection_frame.glsl new file mode 100644 index 0000000..e811faa --- /dev/null +++ b/qglengine/shaders/selection_frame.glsl @@ -0,0 +1,24 @@ +// vert // + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); +} + + +// frag // + +uniform vec2 dt, size; +uniform float thickness; + +void main(void) { + vec2 frame_size = size / dt; + vec2 pix_pos = qgl_FragTexture * frame_size; + vec2 frame = vec2(1) + step(pix_pos, vec2(thickness)) - step(pix_pos, frame_size - vec2(thickness)); + vec2 line = frame * step(0.5, fract(pix_pos.yx / thickness / 4.)); + float fc = max(frame.x, frame.y); + float fl = max(line.x , line.y ); + fl = mix(1, fl, fc); + qgl_FragColor = vec4(vec3(fl), fc * 0.8 + 0.2); + //qgl_FragColor = vec4(vec3(fl,0),1); +} diff --git a/qglengine/shaders/selection_halo.glsl b/qglengine/shaders/selection_halo.glsl new file mode 100644 index 0000000..6d367c6 --- /dev/null +++ b/qglengine/shaders/selection_halo.glsl @@ -0,0 +1,44 @@ +// vert // + +void main(void) { + gl_Position = qgl_ftransform(); +} + + +// frag // + +uniform sampler2D fb_hover, fb_selection; +uniform vec4 hover_id, hover_color, selection_color; +uniform float hover_fill, selection_fill, has_hover, has_selection; +ivec2 tc; + +vec4 diffVector(in sampler2D map, in vec4 id) { + vec4 ds0 = abs(texelFetchOffset(map, tc, 0, ivec2(-1, 0)) - id); + vec4 ds1 = abs(texelFetchOffset(map, tc, 0, ivec2( 1, 0)) - id); + vec4 ds2 = abs(texelFetchOffset(map, tc, 0, ivec2(0, -1)) - id); + vec4 ds3 = abs(texelFetchOffset(map, tc, 0, ivec2(0, 1)) - id); + return vec4(dot(ds0, vec4(1)), dot(ds1, vec4(1)), dot(ds2, vec4(1)), dot(ds3, vec4(1))); +} +vec2 diffPair(in vec4 dv) { + const float _c = 64; + return vec2(step(1e-6, dot(dv, vec4(1))), + step(1e-3, (dv.r * _c) * (dv.g * _c) * (dv.b * _c) * (dv.a * _c))); +} +float diffAlpha(in vec2 dp, in float fill) { + return mix(dp.x - dp.y, dp.x - dp.y - dp.y + 1, fill); +} + +void main(void) { + tc = ivec2(gl_FragCoord.xy); + float hh = clamp(dot(hover_id, vec4(1)), 0, 1); + + vec2 dp = diffPair(diffVector(fb_hover, hover_id)); + float v = diffAlpha(dp, hover_fill); + qgl_FragData[2] = has_hover*hh*v*vec4(hover_color.bgr, hover_color.a); + + dp = diffPair(diffVector(fb_selection, vec4(1))); + v = diffAlpha(dp, selection_fill); + qgl_FragData[3] = has_selection*v*vec4(selection_color.bgr, selection_color.a); + + //qgl_FragData[3] = vec4(has_selection); +} diff --git a/qglengine/shaders/service.glsl b/qglengine/shaders/service.glsl new file mode 100644 index 0000000..8905031 --- /dev/null +++ b/qglengine/shaders/service.glsl @@ -0,0 +1,25 @@ +// vert // + +out vec3 geom_normal; +out vec4 object_color; + +void main(void) { + qgl_FragTexture = qgl_Texture; + gl_Position = qgl_ftransform(); + + mat3 nmat = qgl_getNormalMatrix(); + geom_normal = normalize(nmat * qgl_Normal); + object_color = qgl_ObjectColor; +} + + +// frag // + +in vec3 geom_normal; +in vec4 object_color; + +void main(void) { + vec3 normal = normalize(geom_normal); + float l = max(0, dot(normal, vec3(0,0,1))); + qgl_FragColor = object_color * (l * 0.6 + 0.4); +} diff --git a/qglengine/widgets/CMakeLists.txt b/qglengine/widgets/CMakeLists.txt new file mode 100644 index 0000000..2a84983 --- /dev/null +++ b/qglengine/widgets/CMakeLists.txt @@ -0,0 +1,34 @@ +project(qglengine_widgets) +cmake_minimum_required(VERSION 2.6) +if (POLICY CMP0017) + cmake_policy(SET CMP0017 NEW) +endif() +find_qt(Qt5 Core Gui Widgets) +qt_sources(SRC) +qt_wrap(${SRC} HDRS out_HDR CPPS out_CPP QMS out_QM) +file(GLOB PHS "*_p.h") +list(REMOVE_ITEM out_HDR "${PHS}") +qt_add_library(${PROJECT_NAME} SHARED out_CPP) +qt_target_link_libraries(${PROJECT_NAME} qad_utils qad_widgets qglengine_core) +list(APPEND QT_MULTILIB_LIST ${PROJECT_NAME}) +set(QT_MULTILIB_LIST ${QT_MULTILIB_LIST} PARENT_SCOPE) +message(STATUS "Building ${PROJECT_NAME}") +if (LIBPROJECT) + sdk_install("qglengine" "${PROJECT_NAME}" "${out_HDR}" "${out_QM}") +else() + if (LIB) + if (WIN32) + qt_install(FILES ${out_HDR} DESTINATION ${MINGW_INCLUDE}/qglengine) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_LIB}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${MINGW_BIN}) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION QtBin) + else() + qt_install(FILES ${H} DESTINATION ${CMAKE_INSTALL_PREFIX}/include/qglengine) + qt_install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) + endif() + message(STATUS "Install ${PROJECT_NAME} to system \"${CMAKE_INSTALL_PREFIX}\"") + else() + qt_install(TARGETS ${PROJECT_NAME} DESTINATION bin) + message(STATUS "Install ${PROJECT_NAME} to local \"bin\"") + endif() +endif() diff --git a/qglengine/widgets/material_editor.cpp b/qglengine/widgets/material_editor.cpp new file mode 100644 index 0000000..1456296 --- /dev/null +++ b/qglengine/widgets/material_editor.cpp @@ -0,0 +1,227 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "material_editor.h" +#include "ui_material_editor.h" +#include "glmaterial.h" + + +MaterialEditor::MaterialEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::MaterialEditor(); + ui->setupUi(this); + ui->checkGlass->hide(); + ui->frameReflection->hide(); + ui->label_13->hide(); + mat = 0; + active = true; +} + + +void MaterialEditor::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void MaterialEditor::materialChanged() { + if (!active || !mat) return; + mat->_changed = true; + mat->color_diffuse = ui->colorDiffuse ->color(); + mat->color_specular = ui->colorSpecular->color(); + mat->color_emission = ui->colorEmission->color(); + mat->transparency = ui->spinTransparent->value(); + mat->reflectivity = ui->spinReflect->value(); + mat->iof = ui->spinIOF->value(); + mat->dispersion = ui->spinDispersion->value(); + mat->glass = ui->checkGlass->isChecked(); + emit changed(); +} + + +void MaterialEditor::setMaterial(Material * m) { + active = false; + mat = m; + setEnabled(m); + if (!mat) return; + ui->colorDiffuse ->setColor(mat->color_diffuse ); + ui->colorSpecular->setColor(mat->color_specular); + ui->colorEmission->setColor(mat->color_emission); + ui->spinTransparent->setValue(mat->transparency); + ui->spinReflect->setValue(mat->reflectivity); + ui->spinIOF->setValue(mat->iof); + ui->spinDispersion->setValue(mat->dispersion); + ui->checkGlass->setChecked(mat->glass); + ui->mapDiffuse ->setMap(&(mat->map_diffuse )); + ui->mapNormal ->setMap(&(mat->map_normal )); + ui->mapSpecular ->setMap(&(mat->map_specular )); + ui->mapRoughness->setMap(&(mat->map_roughness)); + ui->mapEmission ->setMap(&(mat->map_emission )); + ui->mapRelief ->setMap(&(mat->map_relief )); + /*ui->lineReflFront->setProperty("GLpath", mat->map_reflection.path(0)); ui->lineReflFront->setText(QFileInfo(mat->map_reflection.path(0)).fileName()); + ui->lineReflBack->setProperty("GLpath", mat->map_reflection.path(1)); ui->lineReflBack->setText(QFileInfo(mat->map_reflection.path(1)).fileName()); + ui->lineReflLeft->setProperty("GLpath", mat->map_reflection.path(2)); ui->lineReflLeft->setText(QFileInfo(mat->map_reflection.path(2)).fileName()); + ui->lineReflRight->setProperty("GLpath", mat->map_reflection.path(3)); ui->lineReflRight->setText(QFileInfo(mat->map_reflection.path(3)).fileName()); + ui->lineReflTop->setProperty("GLpath", mat->map_reflection.path(4)); ui->lineReflTop->setText(QFileInfo(mat->map_reflection.path(4)).fileName()); + ui->lineReflBottom->setProperty("GLpath", mat->map_reflection.path(5)); ui->lineReflBottom->setText(QFileInfo(mat->map_reflection.path(5)).fileName()); + */active = true; +} + +/* +Material MaterialEditor::material() { + Material m; + mat->color_diffuse = ui->colorDiffuse ->color(); + mat->color_specular = ui->colorSpecular->color(); + mat->color_emission = ui->colorEmission->color(); + mat->transparency = ui->spinTransparent->value(); + mat->reflectivity = ui->spinReflect->value(); + mat->iof = ui->spinIOF->value(); + mat->dispersion = ui->spinDispersion->value(); + mat->glass = ui->checkGlass->isChecked(); + mat->map_diffuse = ui->mapDiffuse->map(); + mat->map_normal = ui->mapNormal->map(); + mat->map_specular = ui->mapSpecular->map(); + mat->map_roughness = ui->mapRoughness->map(); + mat->map_emission = ui->mapEmission->map(); + mat->map_relief = ui->mapRelief->map(); + mat->map_reflection.setPath(0, ui->lineReflFront->property("GLpath").toString()); + mat->map_reflection.setPath(1, ui->lineReflBack->property("GLpath").toString()); + mat->map_reflection.setPath(2, ui->lineReflLeft->property("GLpath").toString()); + mat->map_reflection.setPath(3, ui->lineReflRight->property("GLpath").toString()); + mat->map_reflection.setPath(4, ui->lineReflTop->property("GLpath").toString()); + mat->map_reflection.setPath(5, ui->lineReflBottom->property("GLpath").toString()); + return m; +} +*/ + +void MaterialEditor::on_buttonReflFrontSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflFront->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflFront->setProperty("GLpath", str); + ui->lineReflFront->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBackSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflBack->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflBack->setProperty("GLpath", str); + ui->lineReflBack->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflLeftSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflLeft->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflLeft->setProperty("GLpath", str); + ui->lineReflLeft->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflRightSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflRight->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflRight->setProperty("GLpath", str); + ui->lineReflRight->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflTopSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflTop->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflTop->setProperty("GLpath", str); + ui->lineReflTop->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBottomSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->lineReflBottom->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->lineReflBottom->setProperty("GLpath", str); + ui->lineReflBottom->setText(QFileInfo(str).fileName()); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflFrontClear_clicked() { + ui->lineReflFront->setText(""); + ui->lineReflFront->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBackClear_clicked() { + ui->lineReflBack->setText(""); + ui->lineReflBack->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflLeftClear_clicked() { + ui->lineReflLeft->setText(""); + ui->lineReflLeft->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflRightClear_clicked() { + ui->lineReflRight->setText(""); + ui->lineReflRight->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflTopClear_clicked() { + ui->lineReflTop->setText(""); + ui->lineReflTop->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonReflBottomClear_clicked() { + ui->lineReflBottom->setText(""); + ui->lineReflBottom->setProperty("GLpath", ""); + materialChanged(); +} + + +void MaterialEditor::on_buttonLoadCubeDir_clicked() { + QString dir = QFileDialog::getExistingDirectory(this, "Select directory", ui->lineReflFront->property("GLpath").toString()); + if (dir.isEmpty()) return; + /*CubeTexture cb(0); + cb.loadPathesFromDirectory(dir); + active = false; + ui->lineReflFront->setProperty("GLpath", cb.path(0)); ui->lineReflFront->setText(QFileInfo(cb.path(0)).fileName()); + ui->lineReflBack->setProperty("GLpath", cb.path(1)); ui->lineReflBack->setText(QFileInfo(cb.path(1)).fileName()); + ui->lineReflLeft->setProperty("GLpath", cb.path(2)); ui->lineReflLeft->setText(QFileInfo(cb.path(2)).fileName()); + ui->lineReflRight->setProperty("GLpath", cb.path(3)); ui->lineReflRight->setText(QFileInfo(cb.path(3)).fileName()); + ui->lineReflTop->setProperty("GLpath", cb.path(4)); ui->lineReflTop->setText(QFileInfo(cb.path(4)).fileName()); + ui->lineReflBottom->setProperty("GLpath", cb.path(5)); ui->lineReflBottom->setText(QFileInfo(cb.path(5)).fileName());*/ + active = true; + materialChanged(); +} diff --git a/qglengine/widgets/material_editor.h b/qglengine/widgets/material_editor.h new file mode 100644 index 0000000..315f050 --- /dev/null +++ b/qglengine/widgets/material_editor.h @@ -0,0 +1,65 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef MATERIAL_EDITOR_H +#define MATERIAL_EDITOR_H + +#include +#include "gltypes.h" + +namespace Ui { + class MaterialEditor; +} + +class MaterialEditor: public QWidget +{ + Q_OBJECT +public: + explicit MaterialEditor(QWidget * parent = 0); + + void setMaterial(Material * m); + +protected: + void changeEvent(QEvent * e); + + bool active; + Ui::MaterialEditor * ui; + Material * mat; + +private slots: + void materialChanged(); + void on_buttonReflFrontSelect_clicked(); + void on_buttonReflFrontClear_clicked(); + void on_buttonReflBackSelect_clicked(); + void on_buttonReflBackClear_clicked(); + void on_buttonReflLeftSelect_clicked(); + void on_buttonReflLeftClear_clicked(); + void on_buttonReflRightSelect_clicked(); + void on_buttonReflRightClear_clicked(); + void on_buttonReflTopSelect_clicked(); + void on_buttonReflTopClear_clicked(); + void on_buttonReflBottomSelect_clicked(); + void on_buttonReflBottomClear_clicked(); + void on_buttonLoadCubeDir_clicked(); + +signals: + void changed(); + +}; + +#endif // MATERIAL_EDITOR_H diff --git a/qglengine/widgets/material_editor.ui b/qglengine/widgets/material_editor.ui new file mode 100644 index 0000000..8912e7c --- /dev/null +++ b/qglengine/widgets/material_editor.ui @@ -0,0 +1,975 @@ + + + MaterialEditor + + + + 0 + 0 + 435 + 817 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + font:bold; + + + Diffuse + + + true + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + font:normal; + + + Color: + + + + + + + Qt::NoFocus + + + font:normal; + + + true + + + + + + + font:normal; + + + + + + + + + + + + + font:bold; + + + Normal + + + true + + + + + + font:normal; + + + + + + + + + + font:bold; + + + Specular + + + true + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + font:normal; + + + Color: + + + + + + + Qt::NoFocus + + + font:normal; + + + true + + + + + + + font:normal; + + + + + + + + + + + + + font:bold; + + + Roughness + + + true + + + + + + font:normal; + + + + + + + + + + font:bold; + + + Emisson + + + true + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + font:normal; + + + Color: + + + + + + + Qt::NoFocus + + + font:normal; + + + true + + + + + + + font:normal; + + + + + + + + + + + + + font:bold; + + + Relief + + + true + + + + + + font:normal; + + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Glass + + + + + + + Transparency + + + + + + + 1.000000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + Reflectivity + + + + + + + 1.000000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + IOF + + + + + + + 2.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + Dispersion + + + + + + + 1.000000000000000 + + + 0.100000000000000 + + + 2 + + + 0.010000000000000 + + + 0.100000000000000 + + + + + + + Reflection map + + + + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Front: + + + + + + + Back: + + + + + + + Left: + + + + + + + Right: + + + + + + + Top: + + + + + + + Bottom: + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + 2 + + + + + + + + X + + + + + + + ^ + + + + + + + + + load from directory + + + + + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+ + MaterialMapEditor + QWidget +
material_map_editor.h
+ 1 + + changed() + +
+
+ + + + colorDiffuse + colorChanged(QColor) + MaterialEditor + materialChanged() + + + 326 + 49 + + + 282 + 17 + + + + + colorSpecular + colorChanged(QColor) + MaterialEditor + materialChanged() + + + 376 + 202 + + + 284 + 45 + + + + + spinTransparent + valueChanged(double) + MaterialEditor + materialChanged() + + + 433 + 497 + + + 283 + 149 + + + + + spinReflect + valueChanged(double) + MaterialEditor + materialChanged() + + + 433 + 526 + + + 284 + 174 + + + + + spinIOF + valueChanged(double) + MaterialEditor + materialChanged() + + + 433 + 555 + + + 284 + 236 + + + + + checkGlass + toggled(bool) + MaterialEditor + materialChanged() + + + 433 + 468 + + + 284 + 84 + + + + + colorEmission + colorChanged(QColor) + MaterialEditor + materialChanged() + + + 421 + 351 + + + 326 + 63 + + + + + spinDispersion + valueChanged(double) + MaterialEditor + materialChanged() + + + 433 + 584 + + + 326 + 288 + + + + + mapDiffuse + changed() + MaterialEditor + materialChanged() + + + 421 + 69 + + + 434 + 63 + + + + + mapSpecular + changed() + MaterialEditor + materialChanged() + + + 421 + 218 + + + 434 + 143 + + + + + mapEmission + changed() + MaterialEditor + materialChanged() + + + 421 + 367 + + + 434 + 216 + + + + + mapNormal + changed() + MaterialEditor + materialChanged() + + + 421 + 129 + + + 434 + 260 + + + + + mapRelief + changed() + MaterialEditor + materialChanged() + + + 421 + 427 + + + 434 + 304 + + + + + mapRoughness + changed() + MaterialEditor + materialChanged() + + + 421 + 278 + + + 434 + 250 + + + + + groupDiffuse + toggled(bool) + widgetDiffuse + setVisible(bool) + + + 40 + 10 + + + 47 + 56 + + + + + groupNormal + toggled(bool) + mapNormal + setVisible(bool) + + + 54 + 103 + + + 55 + 123 + + + + + groupSpecular + toggled(bool) + widgetSpecular + setVisible(bool) + + + 72 + 162 + + + 71 + 205 + + + + + groupRoughness + toggled(bool) + mapRoughness + setVisible(bool) + + + 56 + 246 + + + 55 + 273 + + + + + groupEmission + toggled(bool) + widgetEmission + setVisible(bool) + + + 67 + 314 + + + 71 + 353 + + + + + groupRelief + toggled(bool) + mapRelief + setVisible(bool) + + + 42 + 397 + + + 44 + 422 + + + + + + materialChanged() + +
diff --git a/qglengine/widgets/material_map_editor.cpp b/qglengine/widgets/material_map_editor.cpp new file mode 100644 index 0000000..70baf77 --- /dev/null +++ b/qglengine/widgets/material_map_editor.cpp @@ -0,0 +1,107 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this programap-> If not, see . +*/ + +#include "material_map_editor.h" +#include "ui_material_map_editor.h" +#include "glmaterial.h" + + +MaterialMapEditor::MaterialMapEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::MaterialMapEditor(); + ui->setupUi(this); + active = true; +} + + +void MaterialMapEditor::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void MaterialMapEditor::resizeEvent(QResizeEvent * e) { + ui->iconedLabel->setFixedWidth(ui->iconedLabel->height()); + ui->iconedLabel->setIconSize(ui->iconedLabel->size()); +} + + +void MaterialMapEditor::updateIcon() { + ui->iconedLabel->setIcon(QIcon(ui->linePath->property("GLpath").toString())); +} + + +void MaterialMapEditor::mapChanged() { + if (!active || !map) return; + map->color_amount = ui->sliderAmount->value(); + map->color_offset = ui->sliderOffset->value(); + map->bitmap_scale.setX(ui->spinScaleX->value()); + map->bitmap_scale.setY(ui->spinScaleY->value()); + emit changed(); +} + + +void MaterialMapEditor::setMap(Map * m) { + active = false; + map = m; + setEnabled(m); + if (!map) return; + ui->sliderAmount->setValue(map->color_amount); + ui->sliderOffset->setValue(map->color_offset); + ui->spinScaleX->setValue(map->bitmap_scale.x()); + ui->spinScaleY->setValue(map->bitmap_scale.y()); + ui->linePath->setProperty("GLpath", map->bitmap_path); ui->linePath->setText(QFileInfo(map->bitmap_path).fileName()); + updateIcon(); + active = true; +} + +/* +Map MaterialMapEditor::map() { + Map m; + map->color_amount = ui->sliderAmount->value(); + map->color_offset = ui->sliderOffset->value(); + map->bitmap_scale.setX(ui->spinScaleX->value()); + map->bitmap_scale.setY(ui->spinScaleY->value()); + map->bitmap_path = ui->linePath->property("GLpath").toString(); + return m; +} +*/ + +void MaterialMapEditor::on_buttonSelect_clicked() { + QString str = QFileDialog::getOpenFileName(this, "Select image", ui->linePath->property("GLpath").toString(), "Images(*.bmp *.jpg *.jpeg *.png *.tif *.tiff *.tga);;All files(*)"); + if (str.isEmpty()) return; + ui->linePath->setProperty("GLpath", str); + ui->linePath->setText(QFileInfo(str).fileName()); + map->setBitmapPath(str); + updateIcon(); + mapChanged(); +} + + +void MaterialMapEditor::on_buttonClear_clicked() { + ui->linePath->setText(""); + ui->linePath->setProperty("GLpath", ""); + map->clearBitmap(); + updateIcon(); + mapChanged(); +} diff --git a/qglengine/widgets/material_map_editor.h b/qglengine/widgets/material_map_editor.h new file mode 100644 index 0000000..65e2a5f --- /dev/null +++ b/qglengine/widgets/material_map_editor.h @@ -0,0 +1,56 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef MATERIAL_MAP_EDITOR_H +#define MATERIAL_MAP_EDITOR_H + +#include +#include "gltypes.h" + +namespace Ui { + class MaterialMapEditor; +} + +class MaterialMapEditor: public QWidget +{ + Q_OBJECT +public: + explicit MaterialMapEditor(QWidget * parent = 0); + + void setMap(Map * m); + +protected: + void changeEvent(QEvent * e); + void resizeEvent(QResizeEvent * e); + void updateIcon(); + + bool active; + Ui::MaterialMapEditor * ui; + Map * map; + +private slots: + void mapChanged(); + void on_buttonSelect_clicked(); + void on_buttonClear_clicked(); + +signals: + void changed(); + +}; + +#endif // MATERIAL_MAP_EDITOR_H diff --git a/qglengine/widgets/material_map_editor.ui b/qglengine/widgets/material_map_editor.ui new file mode 100644 index 0000000..668e97d --- /dev/null +++ b/qglengine/widgets/material_map_editor.ui @@ -0,0 +1,321 @@ + + + MaterialMapEditor + + + + 0 + 0 + 587 + 138 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + IconedLabel::RightToLeft + + + + + + + + + 2 + + + + + + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + + :/icons/document-open.png:/icons/document-open.png + + + + + + + + + Amount: + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.050000000000000 + + + 0.200000000000000 + + + -99.000000000000000 + + + 99.000000000000000 + + + + + + + Offset: + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.050000000000000 + + + 0.200000000000000 + + + -99.000000000000000 + + + 99.000000000000000 + + + + + + + + + + 0 + 0 + + + + Scale X: + + + + + + + 9999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + Scale Y: + + + + + + + 9999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 1 + 20 + + + + + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + IconedLabel + QFrame +
iconedlabel.h
+
+
+ + + + + + + linePath + textChanged(QString) + MaterialMapEditor + mapChanged() + + + 471 + 22 + + + 99 + 73 + + + + + sliderAmount + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 440 + 38 + + + 512 + 37 + + + + + sliderOffset + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 497 + 66 + + + 511 + 65 + + + + + spinScaleX + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 377 + 104 + + + 332 + 164 + + + + + spinScaleY + valueChanged(double) + MaterialMapEditor + mapChanged() + + + 519 + 110 + + + 493 + 164 + + + + + + mapChanged() + +
diff --git a/qglengine/widgets/materials_editor.cpp b/qglengine/widgets/materials_editor.cpp new file mode 100644 index 0000000..0b96b98 --- /dev/null +++ b/qglengine/widgets/materials_editor.cpp @@ -0,0 +1,177 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "materials_editor.h" +#include "ui_materials_editor.h" +#include "material_editor.h" +#include "qglview.h" +#include "glmaterial.h" +#include +#include +#include + + +MaterialsEditor::MaterialsEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::MaterialsEditor(); + ui->setupUi(this); + ui->widgetMaterial->setMaterial(0); + view = 0; +} + + +void MaterialsEditor::assignQGLView(QGLView * v) { + view = v; + connect(view, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + connect(view, SIGNAL(materialsChanged()), this, SLOT(materialsChanged())); + connect(view, SIGNAL(materialThumbnailCreated(Material*)), this, SLOT(materialThumbnailCreated(Material*))); + materialsChanged(); +} + + +bool MaterialsEditor::event(QEvent * e) { + if (e->type() == QEvent::FontChange || e->type() == QEvent::Polish) { + QSize sz = preferredIconSize(1.5, this); + ui->comboMaterial->setIconSize(sz * 3); + ui->buttonRename->setIconSize(sz); + ui->buttonAdd ->setIconSize(sz); + ui->buttonDelete->setIconSize(sz); + ui->buttonAssign->setIconSize(sz); + } + return QWidget::event(e); +} + + +void MaterialsEditor::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +Material * MaterialsEditor::currentMaterial() { + if (!view) return 0; + int ind = ui->comboMaterial->currentIndex(); + QVector mats = view->scene()->getMaterials(); + if (ind < 0 || ind >= mats.size()) return 0; + return mats[ind]; +} + + +void MaterialsEditor::selectMaterial(Material * m) { + if (!m) { + ui->comboMaterial->setCurrentIndex(-1); + return; + } + for (int i = 0; i < ui->comboMaterial->count(); ++i) { + if ((Material*)(ui->comboMaterial->itemData(i, Qt::UserRole + 1).value()) == m) { + ui->comboMaterial->setCurrentIndex(i); + break; + } + } +} + + +int MaterialsEditor::indexByMaterial(Material * m) { + if (!m) return -1; + for (int i = 0; i < ui->comboMaterial->count(); ++i) { + if ((Material*)(ui->comboMaterial->itemData(i, Qt::UserRole + 1).value()) == m) + return i; + } + return -1; +} + + +void MaterialsEditor::selectionChanged() { + if (!view) return; + //qDebug() << "selectionChanged"; + ObjectBase * o = view->selectedObject(); + if (o) selectMaterial(o->material()); +} + + +void MaterialsEditor::materialsChanged() { + Material * cm = currentMaterial(); + ui->comboMaterial->clear(); + if (!view) return; + QVector mats = view->scene()->getMaterials(); + for (int i = 0; i < mats.size(); ++i) { + Material * m = mats[i]; + ui->comboMaterial->addItem(QString("[%1] " + m->name).arg(i + 1), QVariant(m->name)); + ui->comboMaterial->setItemData(i, QVariant(quintptr(m)), Qt::UserRole + 1); + ui->comboMaterial->setItemIcon(i, QIcon(QPixmap::fromImage(view->materialThumbnail(m)))); + if (cm == m) ui->comboMaterial->setCurrentIndex(i); + } + +} + + +void MaterialsEditor::materialThumbnailCreated(Material * m) { + //qDebug() << "materialThumbnailCreated" << m; + int i = indexByMaterial(m); + if (i < 0 || !view) return; + //qDebug() << "materialThumbnailCreated set to" << i; + ui->comboMaterial->setItemIcon(i, QIcon(QPixmap::fromImage(view->materialThumbnail(m)))); +} + + +void MaterialsEditor::on_comboMaterial_currentIndexChanged(int index) { + ui->widgetMaterial->setMaterial(currentMaterial()); +} + + +void MaterialsEditor::on_buttonRename_clicked() { + Material * m = currentMaterial(); + if (!m) return; + QString nn = QInputDialog::getText(this, "Materials editor", "Input new name:", QLineEdit::Normal, m->name); + if (nn.isEmpty()) return; + m->name = nn; + int ind = ui->comboMaterial->currentIndex(); + ui->comboMaterial->setItemText(ind, QString("[%1] " + nn).arg(ind + 1)); + ui->comboMaterial->setItemData(ind, nn); +} + + +void MaterialsEditor::on_buttonAdd_clicked() { + if (!view) return; + Material * m = view->scene()->newMaterial(); + materialsChanged(); + selectMaterial(m); +} + + +void MaterialsEditor::on_buttonDelete_clicked() { + if (!view) return; + Material * m = currentMaterial(); + if (!m) return; + view->scene()->removeMaterial(m); + materialsChanged(); +} + + +void MaterialsEditor::on_buttonAssign_clicked() { + if (!view) return; + Material * m = currentMaterial(); + QList ol = view->selectedObjects(); + foreach (ObjectBase * o, ol) + o->setMaterial(m, true); +} diff --git a/qglengine/widgets/materials_editor.h b/qglengine/widgets/materials_editor.h new file mode 100644 index 0000000..60b9ad3 --- /dev/null +++ b/qglengine/widgets/materials_editor.h @@ -0,0 +1,64 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef MATERIALS_EDITOR_H +#define MATERIALS_EDITOR_H + +#include +#include "gltypes.h" + +namespace Ui { + class MaterialsEditor; +} + +class MaterialsEditor: public QWidget +{ + Q_OBJECT +public: + explicit MaterialsEditor(QWidget * parent = 0); + + void assignQGLView(QGLView * v); + +protected: + bool event(QEvent * e); + void changeEvent(QEvent * e); + Material * currentMaterial(); + void selectMaterial(Material * m); + int indexByMaterial(Material * m); + + Ui::MaterialsEditor * ui; + QGLView * view; + Material * mat; + +private slots: + void selectionChanged(); + void materialsChanged(); + void materialThumbnailCreated(Material * m); + + void on_comboMaterial_currentIndexChanged(int index); + void on_buttonRename_clicked(); + void on_buttonAdd_clicked(); + void on_buttonDelete_clicked(); + void on_buttonAssign_clicked(); + +signals: + void changed(); + +}; + +#endif // MATERIALS_EDITOR_H diff --git a/qglengine/widgets/materials_editor.ui b/qglengine/widgets/materials_editor.ui new file mode 100644 index 0000000..7e1dbc1 --- /dev/null +++ b/qglengine/widgets/materials_editor.ui @@ -0,0 +1,170 @@ + + + MaterialsEditor + + + + 0 + 0 + 542 + 492 + + + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + + + + + Rename ... + + + + :/icons/edit-rename.png:/icons/edit-rename.png + + + + + + + Add + + + + :/icons/list-add.png:/icons/list-add.png + + + + + + + Delete + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + Assign + + + + :/icons/go-jump.png:/icons/go-jump.png + + + + + + + + + QFrame::NoFrame + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 522 + 440 + + + + false + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Vertical + + + + 20 + 421 + + + + + + + + + + + + + EComboBox + QComboBox +
ecombobox.h
+
+ + MaterialEditor + QWidget +
material_editor.h
+ 1 +
+
+ + + + + + + materialChanged() + +
diff --git a/qglengine/widgets/object_editor.cpp b/qglengine/widgets/object_editor.cpp new file mode 100644 index 0000000..ded02f5 --- /dev/null +++ b/qglengine/widgets/object_editor.cpp @@ -0,0 +1,177 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "object_editor.h" +#include "ui_object_editor.h" +#include "glcamera.h" + + +ObjectEditor::ObjectEditor(QWidget * parent): QWidget(parent) { + ui = new Ui::ObjectEditor(); + ui->setupUi(this); + active = true; + object = 0; + rmodes << ObjectBase::View << ObjectBase::Point << ObjectBase::Line << ObjectBase::Fill; + ui->groupLight->setEnabled(false); + ui->groupLight->setVisible(false); + ui->groupCamera->setEnabled(false); + ui->groupCamera->setVisible(false); +} + + +void ObjectEditor::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + + +void ObjectEditor::setObject(ObjectBase * o) { + object = o; + if (object == 0) { + ui->groupLight->setEnabled(false); + ui->groupLight->setVisible(false); + ui->groupCamera->setEnabled(false); + ui->groupCamera->setVisible(false); + return; + } + ui->buttonDiscardRawMatrix->setEnabled(o->isRawMatrix()); + active = false; + ui->spinPosX->setValue(object->posX()); + ui->spinPosY->setValue(object->posY()); + ui->spinPosZ->setValue(object->posZ()); + ui->spinRotationX->setValue(object->rotationX()); + ui->spinRotationY->setValue(object->rotationY()); + ui->spinRotationZ->setValue(object->rotationZ()); + ui->spinScaleX->setValue(object->scaleX()); + ui->spinScaleY->setValue(object->scaleY()); + ui->spinScaleZ->setValue(object->scaleZ()); + ui->spinLineWidth->setValue(object->lineWidth()); + ui->checkVisible->setChecked(object->isVisible()); + ui->checkAcceptLight->setChecked(object->isAcceptLight()); + ui->checkAcceptFog->setChecked(object->isAcceptFog()); + ui->checkCastShadows->setChecked(object->isCastShadows()); + ui->checkReceiveShadows->setChecked(object->isReceiveShadows()); + ui->comboRenderMode->setCurrentIndex(rmodes.indexOf(object->renderMode())); + ui->groupLight->setEnabled(object->type() == ObjectBase::glLight); + ui->groupLight->setVisible(object->type() == ObjectBase::glLight); + ui->buttonColor->setColor(object->color()); + if (object->type() == ObjectBase::glLight) { + Light * l = globject_cast(object); + //bool is_dir = l->light_type == Light::Directional, is_cone = l->light_type == Light::Cone; + ui->comboLightType->setCurrentIndex(l->light_type); + ui->spinLightIntensity->setValue(l->intensity); + ui->spinLightDecayConst->setValue(l->decay_const); + ui->spinLightDecayLinear->setValue(l->decay_linear); + ui->spinLightDecayQuadratic->setValue(l->decay_quadratic); + ui->spinLightAngleStart->setValue(l->angle_start); + ui->spinLightAngleEnd->setValue(l->angle_end); + ui->spinLightDirectionX->setValue(l->direction.x()); + ui->spinLightDirectionY->setValue(l->direction.y()); + ui->spinLightDirectionZ->setValue(l->direction.z()); + } + ui->groupCamera->setEnabled(object->type() == ObjectBase::glCamera); + ui->groupCamera->setVisible(object->type() == ObjectBase::glCamera); + if (object->type() == ObjectBase::glCamera) { + Camera * c = globject_cast(object); + ui->checkCameraMirrorX->setChecked(c->isMirrorX()); + ui->checkCameraMirrorY->setChecked(c->isMirrorY()); + ui->spinCameraFOV->setValue(c->FOV()); + ui->spinCameraDepthStart->setValue(c->depthStart()); + ui->spinCameraDepthEnd->setValue(c->depthEnd()); + } + active = true; +} + + +void ObjectEditor::objectChanged() { + if (!active || object == 0) return; + if (!object->isRawMatrix()) { + object->setPosX(ui->spinPosX->value()); + object->setPosY(ui->spinPosY->value()); + object->setPosZ(ui->spinPosZ->value()); + object->setRotationX(ui->spinRotationX->value()); + object->setRotationY(ui->spinRotationY->value()); + object->setRotationZ(ui->spinRotationZ->value()); + object->setScaleX(ui->spinScaleX->value()); + object->setScaleY(ui->spinScaleY->value()); + object->setScaleZ(ui->spinScaleZ->value()); + } + /*object->setLineWidth(ui->spinLineWidth->value()); + object->setVisible(ui->checkVisible->isChecked()); + object->setAcceptLight(ui->checkAcceptLight->isChecked()); + object->setAcceptFog(ui->checkAcceptFog->isChecked()); + object->setCastShadows(ui->checkCastShadows->isChecked()); + object->setReceiveShadows(ui->checkReceiveShadows->isChecked()); + object->setRenderMode((ObjectBase::RenderMode)rmodes[ui->comboRenderMode->currentIndex()]);*/ + object->setColor(ui->buttonColor->color()); + if (object->type() == ObjectBase::glLight) { + Light * l = globject_cast(object); + //bool is_dir = l->light_type == Light::Directional, is_cone = l->light_type == Light::Cone; + l->light_type = (Light::Type)ui->comboLightType->currentIndex(); + l->intensity = ui->spinLightIntensity->value(); + l->decay_const = ui->spinLightDecayConst->value(); + l->decay_linear = ui->spinLightDecayLinear->value(); + l->decay_quadratic = ui->spinLightDecayQuadratic->value(); + l->angle_start = ui->spinLightAngleStart->value(); + l->angle_end = ui->spinLightAngleEnd->value(); + l->direction = QVector3D(ui->spinLightDirectionX->value(), ui->spinLightDirectionY->value(), ui->spinLightDirectionZ->value()).normalized(); + l->apply(); + } + if (object->type() == ObjectBase::glCamera) { + Camera * c = globject_cast(object); + c->setMirrorX(ui->checkCameraMirrorX->isChecked()); + c->setMirrorY(ui->checkCameraMirrorY->isChecked()); + c->setFOV(ui->spinCameraFOV->value()); + c->setDepthStart(ui->spinCameraDepthStart->value()); + c->setDepthEnd(ui->spinCameraDepthEnd->value()); + } + emit changed(); +} + + +void ObjectEditor::on_spinLightAngleStart_valueChanged(double v) { + if (ui->spinLightAngleEnd->value() < v) + ui->spinLightAngleEnd->setValue(v); +} + + +void ObjectEditor::on_spinLightAngleEnd_valueChanged(double v) { + if (ui->spinLightAngleStart->value() > v) + ui->spinLightAngleStart->setValue(v); +} + + +void ObjectEditor::on_buttonDiscardRawMatrix_clicked() { + if (!active || !object) return; + object->setPosX(ui->spinPosX->value()); + object->setPosY(ui->spinPosY->value()); + object->setPosZ(ui->spinPosZ->value()); + object->setRotationX(ui->spinRotationX->value()); + object->setRotationY(ui->spinRotationY->value()); + object->setRotationZ(ui->spinRotationZ->value()); + object->setScaleX(ui->spinScaleX->value()); + object->setScaleY(ui->spinScaleY->value()); + object->setScaleZ(ui->spinScaleZ->value()); + ui->buttonDiscardRawMatrix->setEnabled(false); +} diff --git a/qglengine/widgets/object_editor.h b/qglengine/widgets/object_editor.h new file mode 100644 index 0000000..615fbcf --- /dev/null +++ b/qglengine/widgets/object_editor.h @@ -0,0 +1,57 @@ +/* + QGLView + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef OBJECT_EDITOR_H +#define OBJECT_EDITOR_H + +#include +#include "globject.h" + +namespace Ui { + class ObjectEditor; +} + +class ObjectEditor: public QWidget +{ + Q_OBJECT +public: + explicit ObjectEditor(QWidget * parent = 0); + + void setObject(ObjectBase * o); + ObjectBase * getObject() {return object;} + +protected: + void changeEvent(QEvent * e); + + Ui::ObjectEditor * ui; + bool active; + ObjectBase * object; + QList rmodes; + +private slots: + void objectChanged(); + void on_spinLightAngleStart_valueChanged(double v); + void on_spinLightAngleEnd_valueChanged(double v); + void on_buttonDiscardRawMatrix_clicked(); + +signals: + void changed(); + +}; + +#endif // OBJECT_EDITOR_H diff --git a/qglengine/widgets/object_editor.ui b/qglengine/widgets/object_editor.ui new file mode 100644 index 0000000..800ce96 --- /dev/null +++ b/qglengine/widgets/object_editor.ui @@ -0,0 +1,1294 @@ + + + ObjectEditor + + + + 0 + 0 + 352 + 891 + + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 2 + + + 2 + + + + + false + + + Discard raw transform + + + + + + + Position X + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + + + + + Position Y + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + + + + + Position Z + + + + + + + -99999.000000000000000 + + + 99999.000000000000000 + + + + + + + Rotation X + + + + + + + -360.000000000000000 + + + 360.000000000000000 + + + ° + + + + + + + Rotation Y + + + + + + + -360.000000000000000 + + + 360.000000000000000 + + + ° + + + + + + + Rotation Z + + + + + + + -360.000000000000000 + + + 360.000000000000000 + + + ° + + + + + + + Scale X + + + + + + + 4 + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Scale Y + + + + + + + 4 + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Scale Z + + + + + + + 4 + + + -99999.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Render mode + + + + + + + + View + + + + + Point + + + + + Line + + + + + Fill + + + + + + + + Visible + + + + + + + Accept light + + + + + + + Accept fog + + + + + + + Cast shadows + + + + + + + Receive shadows + + + + + + + Line width + + + + + + + 0.000000000000000 + + + 99999.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Light + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + 2 + + + 0 + + + + + Type + + + + + + + + Omni + + + + + Directional + + + + + Cone + + + + + + + + Intensity + + + + + + + 0.000000000000000 + + + 128.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Decay const + + + + + + + 0.000000000000000 + + + 64.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Decay linear + + + + + + + 0.000000000000000 + + + 64.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Decay quadratic + + + + + + + 0.000000000000000 + + + 64.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + true + + + + + + + Angle Start + + + + + + + 0.000000000000000 + + + 180.000000000000000 + + + 1 + + + 5.000000000000000 + + + 30.000000000000000 + + + ° + + + + + + + Angle End + + + + + + + 0.000000000000000 + + + 180.000000000000000 + + + 1 + + + 5.000000000000000 + + + 30.000000000000000 + + + ° + + + + + + + Direcion X + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 0.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Direcion Y + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 0.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + Direcion Z + + + + + + + -1.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 + + + 2 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + + + + Camera + + + + + + Depth + + + + + + + 0 + + + + + 5 + + + 999999999.000000000000000 + + + 0.100000000000000 + + + + + + + + 0 + 0 + + + + - + + + + + + + 5 + + + 999999999.000000000000000 + + + 1000.000000000000000 + + + + + + + + + FOV + + + + + + + 1.000000000000000 + + + 179.000000000000000 + + + 60.000000000000000 + + + 2 + + + 5.000000000000000 + + + 30.000000000000000 + + + ° + + + + + + + + + Mirror Y + + + + + + + Mirror X + + + + + + + + + + + + Color + + + + + + + + + + + SpinSlider + QWidget +
spinslider.h
+
+ + ColorButton + QPushButton +
colorbutton.h
+
+
+ + + + spinPosX + valueChanged(double) + ObjectEditor + objectChanged() + + + 319 + 43 + + + 321 + 5 + + + + + spinPosY + valueChanged(double) + ObjectEditor + objectChanged() + + + 319 + 75 + + + 320 + 36 + + + + + spinPosZ + valueChanged(double) + ObjectEditor + objectChanged() + + + 319 + 97 + + + 321 + 61 + + + + + spinRotationX + valueChanged(double) + ObjectEditor + objectChanged() + + + 172 + 121 + + + 82 + 73 + + + + + spinRotationY + valueChanged(double) + ObjectEditor + objectChanged() + + + 319 + 145 + + + 320 + 96 + + + + + spinRotationZ + valueChanged(double) + ObjectEditor + objectChanged() + + + 319 + 169 + + + 321 + 125 + + + + + spinScaleX + valueChanged(double) + ObjectEditor + objectChanged() + + + 319 + 191 + + + 321 + 150 + + + + + spinScaleY + valueChanged(double) + ObjectEditor + objectChanged() + + + 319 + 213 + + + 321 + 175 + + + + + spinScaleZ + valueChanged(double) + ObjectEditor + objectChanged() + + + 319 + 235 + + + 321 + 200 + + + + + checkVisible + toggled(bool) + ObjectEditor + objectChanged() + + + 185 + 268 + + + 76 + 242 + + + + + checkAcceptLight + toggled(bool) + ObjectEditor + objectChanged() + + + 179 + 295 + + + 61 + 261 + + + + + checkAcceptFog + toggled(bool) + ObjectEditor + objectChanged() + + + 210 + 314 + + + 79 + 288 + + + + + checkCastShadows + toggled(bool) + ObjectEditor + objectChanged() + + + 283 + 333 + + + 55 + 310 + + + + + checkReceiveShadows + toggled(bool) + ObjectEditor + objectChanged() + + + 287 + 352 + + + 78 + 334 + + + + + spinLineWidth + valueChanged(double) + ObjectEditor + objectChanged() + + + 303 + 366 + + + 321 + 359 + + + + + comboRenderMode + currentIndexChanged(int) + ObjectEditor + objectChanged() + + + 319 + 257 + + + 321 + 228 + + + + + buttonColor + colorChanged(QColor) + ObjectEditor + objectChanged() + + + 309 + 422 + + + 320 + 393 + + + + + comboLightType + currentIndexChanged(int) + ObjectEditor + objectChanged() + + + 309 + 442 + + + 320 + 429 + + + + + spinLightIntensity + valueChanged(double) + ObjectEditor + objectChanged() + + + 309 + 464 + + + 318 + 463 + + + + + spinLightDecayConst + valueChanged(double) + ObjectEditor + objectChanged() + + + 309 + 486 + + + 320 + 489 + + + + + spinLightDecayLinear + valueChanged(double) + ObjectEditor + objectChanged() + + + 309 + 508 + + + 318 + 517 + + + + + spinLightDecayQuadratic + valueChanged(double) + ObjectEditor + objectChanged() + + + 309 + 530 + + + 325 + 543 + + + + + spinLightAngleStart + valueChanged(double) + ObjectEditor + objectChanged() + + + 309 + 552 + + + 321 + 569 + + + + + spinLightAngleEnd + valueChanged(double) + ObjectEditor + objectChanged() + + + 309 + 574 + + + 320 + 595 + + + + + spinLightDirectionX + valueChanged(double) + ObjectEditor + objectChanged() + + + 309 + 596 + + + 332 + 607 + + + + + spinLightDirectionY + valueChanged(double) + ObjectEditor + objectChanged() + + + 309 + 618 + + + 330 + 633 + + + + + spinLightDirectionZ + valueChanged(double) + ObjectEditor + objectChanged() + + + 309 + 640 + + + 326 + 659 + + + + + spinCameraDepthStart + valueChanged(double) + ObjectEditor + objectChanged() + + + 85 + 691 + + + 171 + 652 + + + + + spinCameraDepthEnd + valueChanged(double) + ObjectEditor + objectChanged() + + + 259 + 693 + + + 277 + 652 + + + + + spinCameraFOV + valueChanged(double) + ObjectEditor + objectChanged() + + + 145 + 719 + + + 324 + 696 + + + + + checkCameraMirrorX + clicked(bool) + ObjectEditor + objectChanged() + + + 129 + 753 + + + 323 + 758 + + + + + checkCameraMirrorY + clicked(bool) + ObjectEditor + objectChanged() + + + 194 + 782 + + + 328 + 785 + + + + + + objectChanged() + +
diff --git a/qglengine/widgets/propertyeditor.cpp b/qglengine/widgets/propertyeditor.cpp new file mode 100644 index 0000000..cce7e95 --- /dev/null +++ b/qglengine/widgets/propertyeditor.cpp @@ -0,0 +1,408 @@ +#include "propertyeditor.h" + + +QWidget * Delegate::widgetForProperty(QWidget * parent, const QModelIndex & index) const { + QWidget * w = 0; + int type = 0; + QVariant value = index.data(Qt::UserRole); + if (index.data(Qt::UserRole + 2).toString() == "__flags") return 0; + if (index.data(Qt::UserRole + 1).toString() == "__flag") { + qulonglong key = index.data(Qt::UserRole).toULongLong(); + value = index.parent().data(Qt::UserRole); + //QMetaProperty prop = index.parent().data(Qt::UserRole + 1).value(); + w = new QCheckBox(parent); type = 14; ((QCheckBox*)w)->setChecked(((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0)); + ((QCheckBox*)w)->setText("0x" + QString::number(key, 16).toUpper()); + connect((QCheckBox*)w, SIGNAL(clicked(bool)), this, SLOT(changedFlag())); + //qDebug() << prop.enumerator().name(); + } else { + if (value.canConvert()) { + PropertyValuePair prop = value.value(); + if (prop.first.isEnumType()) { + w = new QComboBox(parent); type = 13; ((QComboBox*)w)->setCurrentIndex(value.toInt()); + w->setProperty("__prop", QVariant::fromValue(prop.first)); + QMetaEnum menum = prop.first.enumerator(); + for (int i = 0; i < menum.keyCount(); ++i) { + ((QComboBox*)w)->addItem(QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")", menum.value(i)); + if (menum.value(i) == prop.second.toInt()) + ((QComboBox*)w)->setCurrentIndex(i); + } + connect((QComboBox*)w, SIGNAL(currentIndexChanged(int)), this, SLOT(changed())); + } + } else { + switch (value.type()) { + case QVariant::Int: w = new QSpinBox(parent); type = 2; ((QSpinBox*)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::UInt: w = new QSpinBox(parent); type = 3; ((QSpinBox*)w)->setRange(0, 0xFFFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::LongLong: w = new QSpinBox(parent); type = 4; ((QSpinBox*)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::ULongLong: w = new QSpinBox(parent); type = 5; ((QSpinBox*)w)->setRange(0, 0xFFFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break; + case QVariant::Double: w = new QDoubleSpinBox(parent); type = 6; ((QDoubleSpinBox*)w)->setRange(-999999999, 999999999); ((QDoubleSpinBox*)w)->setDecimals(3); connect((QDoubleSpinBox*)w, SIGNAL(valueChanged(double)), this, SLOT(changed())); break; + case QVariant::Bool: w = new QCheckBox(parent); type = 7; ((QCheckBox*)w)->setChecked(value.toBool()); connect((QCheckBox*)w, SIGNAL(toggled(bool)), this, SLOT(changed())); break; + case QVariant::Color: w = new ColorButton(parent); type = 8; ((ColorButton*)w)->setUseAlphaChannel(true); ((ColorButton*)w)->setColor(value.value()); connect((ColorButton*)w, SIGNAL(colorChanged(QColor)), this, SLOT(changed())); break; + case QVariant::Point: w = new QPointEdit(parent); type = 9; ((QPointEdit*)w)->setDecimals(0); ((QPointEdit*)w)->setValue(QPointF(value.toPoint())); connect((QPointEdit*)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed())); break; + case QVariant::PointF: w = new QPointEdit(parent); type = 10; ((QPointEdit*)w)->setDecimals(3); ((QPointEdit*)w)->setValue(value.toPointF()); connect((QPointEdit*)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed())); break; + case QVariant::Rect: w = new QRectEdit(parent); type = 11; ((QRectEdit*)w)->setDecimals(0); ((QRectEdit*)w)->setValue(QRectF(value.toRect())); connect((QRectEdit*)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed())); break; + case QVariant::RectF: w = new QRectEdit(parent); type = 12; ((QRectEdit*)w)->setDecimals(3); ((QRectEdit*)w)->setValue(value.toRectF()); connect((QRectEdit*)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed())); break; + case QVariant::String: default: w = new CLineEdit(parent); type = 1; ((CLineEdit*)w)->setDefaultText(value.toString()); connect((CLineEdit*)w, SIGNAL(textChanged(QString)), this, SLOT(changed())); break; + } + } + } + if (w == 0) return 0; + /*QPalette pal = w->palette(); + pal.setColor(QPalette::Window, Qt::white); + w->setPalette(pal);*/ + w->setAutoFillBackground(true); + w->setProperty("__type", type); + return w; +} + + +void Delegate::setWidgetProperty(QWidget * w, const QVariant & value) const { + if (w == 0) return; + switch (w->property("__type").toInt()) { + case 1: ((CLineEdit*)w)->setText(value.toString()); break; + case 2: case 3: case 4: case 5: ((QSpinBox*)w)->setValue(value.toInt()); break; + case 6: ((QDoubleSpinBox*)w)->setValue(value.toDouble()); break; + case 7: ((QCheckBox*)w)->setChecked(value.toBool()); break; + case 8: ((ColorButton*)w)->setColor(value.value()); break; + case 9: ((QPointEdit*)w)->setValue(value.value()); break; + case 10: ((QPointEdit*)w)->setValue(value.value()); break; + case 11: ((QRectEdit*)w)->setValue(value.value()); break; + case 12: ((QRectEdit*)w)->setValue(value.value()); break; + } +} + + +const QVariant Delegate::widgetProperty(QWidget * w) const { + if (w == 0) return QVariant(); + switch (w->property("__type").toInt()) { + case 1: return QVariant::fromValue(((CLineEdit*)w)->text()); break; + case 2: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 3: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 4: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 5: return QVariant::fromValue(((QSpinBox*)w)->value()); break; + case 6: return QVariant::fromValue(((QDoubleSpinBox*)w)->value()); break; + case 7: return QVariant::fromValue(((QCheckBox*)w)->isChecked()); break; + case 8: return QVariant::fromValue(((ColorButton*)w)->color()); break; + case 9: return QVariant::fromValue(((QPointEdit*)w)->value().toPoint()); break; + case 10: return QVariant::fromValue(((QPointEdit*)w)->value()); break; + case 11: return QVariant::fromValue(((QRectEdit*)w)->value().toRect()); break; + case 12: return QVariant::fromValue(((QRectEdit*)w)->value()); break; + case 13: return QVariant::fromValue(PropertyValuePair(w->property("__prop").value(), ((QComboBox*)w)->itemData(((QComboBox*)w)->currentIndex()))); break; + default: return QVariant(); break; + } + return QVariant(); +} + + +void Delegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { + if (index.data(Qt::UserRole + 1).toString() != "__flag") + model->setData(index, widgetProperty(editor), Qt::UserRole); +} + + +void Delegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { + QStyledItemDelegate::paint(painter, option, index); + QVariant value = index.data(Qt::UserRole); + QStyle * style = QApplication::style(); + QStyleOption * so = 0; + QStyleOptionComplex * soc = 0; + QString text; + QRect rect; + QPalette::ColorRole role = (option.state.testFlag(QStyle::State_Selected) && option.state.testFlag(QStyle::State_Active) ? QPalette::HighlightedText : QPalette::WindowText); + if (index.data(Qt::UserRole + 2).toString() == "__flags") { + text = "0x" + QString::number(value.toInt(), 16).toUpper(); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + return; + } + if (index.data(Qt::UserRole + 1) == "__flag") { + qulonglong key = index.data(Qt::UserRole).toULongLong(); + value = index.parent().data(Qt::UserRole); + so = new QStyleOptionButton(); + so->rect = option.rect; + so->palette = option.palette; + so->fontMetrics = option.fontMetrics; + ((QStyleOptionButton*)so)->state = (((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0) ? QStyle::State_On : QStyle::State_Off) | option.state; + ((QStyleOptionButton*)so)->text = "0x" + QString::number(key, 16).toUpper(); + if (option.state.testFlag(QStyle::State_Selected)) + so->palette.setColor(QPalette::WindowText, so->palette.color(QPalette::HighlightedText)); + style->drawControl(QStyle::CE_CheckBox, so, painter); + } else { + if (value.canConvert()) { + PropertyValuePair prop = value.value(); + if (prop.first.isEnumType()) { + QMetaEnum menum = prop.first.enumerator(); + for (int i = 0; i < menum.keyCount(); ++i) { + if (menum.value(i) == prop.second.toInt()) { + text = QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")"; + break; + } + } + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + } + } else { + switch (value.type()) { + case QVariant::Int: + text.setNum(value.toInt()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::UInt: + text.setNum(value.toUInt()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::LongLong: + text.setNum(value.toLongLong()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::ULongLong: + text.setNum(value.toULongLong()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::Double: + text.setNum(value.toDouble(), 'f', 3); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::Bool: + so = new QStyleOptionButton(); + so->rect = option.rect; + so->state = option.state; + so->palette = option.palette; + so->fontMetrics = option.fontMetrics; + ((QStyleOptionButton*)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off) | option.state; + style->drawControl(QStyle::CE_CheckBox, so, painter); + break; + case QVariant::Color: + rect = option.rect;//style->subElementRect(QStyle::QStyle::SE_FrameContents, so); + rect.setRect(rect.x() + 3, rect.y() + 3, rect.width() - 6, rect.height() - 6); + painter->fillRect(rect, ab); + painter->fillRect(rect, value.value()); + break; + case QVariant::Point: + text = pointString(value.toPoint()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::PointF: + text = pointString(value.toPointF()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::Rect: + text = rectString(value.toRect()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::RectF: + text = rectString(value.toRectF()); + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role); + break; + case QVariant::String: default: + style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, value.toString()), + Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, value.toString(), role); + break; + } + } + } + /*so = new QStyleOptionFrame(); + so->rect = option.rect; + so->state = option.state; + so->palette = option.palette; + so->fontMetrics = option.fontMetrics; + ((QStyleOptionFrame*)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off); + style->drawPrimitive(QStyle::PE_PanelLineEdit, so, painter); + style->drawPrimitive(QStyle::PE_FrameLineEdit, so, painter); + break;*/ + if (so != 0) delete so; + if (soc != 0) delete soc; + +} + + +void Delegate::changedFlag() { + QAbstractItemModel * model = const_cast(cmi.model()); + model->setData(cmi, qobject_cast(sender())->isChecked(), Qt::UserRole + 3); + QModelIndex p = cmi.parent(), mi; + int row = 0; + qulonglong val = 0; + QList chldr; + mi = p.child(row, 1); + while (mi.isValid()) { + chldr << mi; + model->setData(mi, !mi.data(Qt::UserRole + 4).toBool(), Qt::UserRole + 4); + mi = p.child(++row, 1); + } + bool cc = cmi.data(Qt::UserRole + 3).toBool(); + qulonglong cv = cmi.data(Qt::UserRole).toULongLong(); + //qDebug() << "*****"; + if (cc && cv == 0) { + val = 0; + //qDebug() << "null" << cv; + } else { + if (!cc && cv != 0) { + //qDebug() << "uncheck" << cv; + for (int i = 0; i < chldr.size(); ++i) { + if (chldr[i] == cmi) continue; + //qDebug() << (chldr[i].data(Qt::UserRole).toULongLong() & cv); + if (chldr[i].data(Qt::UserRole).toULongLong() & cv) + model->setData(chldr[i], false, Qt::UserRole + 3); + } + } + for (int i = 0; i < chldr.size(); ++i) { + //qDebug() << chldr[i].data(Qt::UserRole + 3).toBool(); + if (chldr[i].data(Qt::UserRole + 3).toBool()) + val |= chldr[i].data(Qt::UserRole).toULongLong(); + } + } + for (int i = 0; i < chldr.size(); ++i) { + if (chldr[i] == cmi) continue; + cv = chldr[i].data(Qt::UserRole).toULongLong(); + model->setData(chldr[i], ((val & cv) == cv && cv != 0) || (val == 0 && cv == 0), Qt::UserRole + 3); + } + //qDebug() << val; + model->setData(p, val, Qt::UserRole); + model->setData(p.sibling(p.row(), 1), val, Qt::UserRole); +} + + + +PropertyEditor::PropertyEditor(QWidget * parent): QTreeWidget(parent) { + object = 0; + active_ = false; + configTree(); + connect(this, SIGNAL(itemClicked(QTreeWidgetItem * , int)), this, SLOT(itemClicked(QTreeWidgetItem * , int))); + connect(this, SIGNAL(itemChanged(QTreeWidgetItem * , int)), this, SLOT(itemChanged(QTreeWidgetItem * , int))); +} + + +PropertyEditor::~PropertyEditor() { + +} + + +void PropertyEditor::changeEvent(QEvent * e) { + QTreeWidget::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + configTree(); + return; + } +} + + +void PropertyEditor::configTree() { + setColumnCount(2); + setRootIsDecorated(false); + setColumnWidth(0, 170); + setColumnWidth(1, 10); + header()->setStretchLastSection(true); + QStringList lbls; + lbls << tr("Property") << tr("Value"); + setHeaderLabels(lbls); + setAlternatingRowColors(true); + setItemDelegateForColumn(1, new Delegate()); + +} + + +void PropertyEditor::itemClicked(QTreeWidgetItem * item, int column) { + if (column == 0) + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + else { + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); + editItem(item, 1); + } +} + + +void PropertyEditor::itemChanged(QTreeWidgetItem * item, int column) { + if (!active_) return; + if (column != 1) return; + QVariant value = item->data(1, Qt::UserRole); + if (value.canConvert()) { + value = value.value().second; + } + object->setProperty(item->text(0).toLatin1(), value); +} + + +void PropertyEditor::rebuild() { + clear(); + configTree(); + if (object == 0) return; + active_ = false; + const QMetaObject * mo = object->metaObject(); + QList mol; + while (mo != 0) { + mol.push_front(mo); + mo = mo->superClass(); + } + int ps, pe; + QTreeWidgetItem * ti, * tli, * tfi; + QVariant value; +// QWidget * pw = 0; + int chue = 0; + QColor bc; + font_b = font(); + font_b.setBold(true); + foreach (const QMetaObject * o, mol) { + ps = o->propertyOffset(); + pe = o->propertyCount();// - ps; + //qDebug() << i->className() << ps << pe; + tli = new QTreeWidgetItem(); + tli->setText(0, o->className()); + tli->setFont(0, font_b); + setItemBackColor(tli, Qt::darkGray); + setItemForeColor(tli, Qt::white); + addTopLevelItem(tli); + setFirstItemColumnSpanned(tli, true); + tli->setExpanded(true); + for (int i = ps; i < pe; ++i) { + props << o->property(i); + value = o->property(i).read(object); + ti = new QTreeWidgetItem(); + ti->setSizeHint(1, QSize(20, 20)); + bc.setHsv(chue, 60, 245 + (i % 2) * 20 - 10); + setItemBackColor(ti, bc); + ti->setText(0, o->property(i).name()); + if (props.back().isFlagType()) { + QMetaEnum menum = props.back().enumerator(); + for (int j = 0; j < menum.keyCount(); ++j) { + tfi = new QTreeWidgetItem(); + tfi->setText(0, menum.key(j)); + tfi->setData(1, Qt::UserRole, menum.value(j)); + tfi->setData(1, Qt::UserRole + 1, "__flag"); + tfi->setData(1, Qt::UserRole + 2, value.toULongLong()); + tfi->setData(1, Qt::UserRole + 3, (value.toULongLong() & menum.value(j)) > 0); + tfi->setSizeHint(1, QSize(20, 20)); + bc.setHsv(chue, 60, 245 + ((i + j + 1) % 2) * 20 - 10); + setItemBackColor(tfi, bc); + ti->addChild(tfi); + } + ti->setData(0, Qt::UserRole, value); + ti->setData(1, Qt::UserRole, value); + ti->setData(1, Qt::UserRole + 2, "__flags"); + ti->setData(0, Qt::UserRole + 1, QVariant::fromValue(props.back())); + } + else if (props.back().isEnumType()) + value.setValue(PropertyValuePair(props.back(), value)); + //ti->setText(1, value.toString()); + ti->setData(1, Qt::UserRole, value); + tli->addChild(ti); + //const_cast(indexFromItem(ti, 1)).; + //if (pw != 0) setItemWidget(ti, 1, pw); + } + chue += 60; + chue %= 360; + } + active_ = true; +} + + +void PropertyEditor::refresh() { + +} diff --git a/qglengine/widgets/propertyeditor.h b/qglengine/widgets/propertyeditor.h new file mode 100644 index 0000000..6b26b2c --- /dev/null +++ b/qglengine/widgets/propertyeditor.h @@ -0,0 +1,77 @@ +#ifndef PROPERTYEDITOR_H +#define PROPERTYEDITOR_H + +#include +#include +#include +#include +#include +#include +#include + + +class Delegate: public QStyledItemDelegate { + Q_OBJECT +public: + Delegate(QObject * parent = 0): QStyledItemDelegate() {ab = QBrush(QImage(":/icons/alpha.png"));} + + QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const {cmi = const_cast(index); return widgetForProperty(parent, index);} + void setEditorData(QWidget * editor, const QModelIndex & index) const {setWidgetProperty(editor, index.data(Qt::UserRole));} + void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const; + void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const {editor->setGeometry(option.rect);} + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; + +private: + QWidget * widgetForProperty(QWidget * parent, const QModelIndex & index) const; + void setWidgetProperty(QWidget * w, const QVariant & value) const; + const QVariant widgetProperty(QWidget * w) const; + QString pointString(const QPoint & p) const {return QString::number(p.x()) + " x " + QString::number(p.y());} + QString pointString(const QPointF & p) const {return QString::number(p.x()) + " x " + QString::number(p.y());} + QString rectString(const QRect & r) const {return QString::number(r.x()) + " x " + QString::number(r.y()) + " : " + + QString::number(r.width()) + " x " + QString::number(r.height());} + QString rectString(const QRectF & r) const {return QString::number(r.x()) + " x " + QString::number(r.y()) + " : " + + QString::number(r.width()) + " x " + QString::number(r.height());} + + QBrush ab; + mutable QModelIndex cmi; + +private slots: + void changed() {setModelData((QWidget * )sender(), const_cast(cmi.model()), cmi);} + void changedFlag(); + +}; + +typedef QPair PropertyValuePair; +Q_DECLARE_METATYPE (PropertyValuePair) +Q_DECLARE_METATYPE (QMetaProperty) + +class PropertyEditor: public QTreeWidget { + Q_OBJECT +public: + explicit PropertyEditor(QWidget * parent = 0); + virtual ~PropertyEditor(); + + void assignObject(QObject * o) {object = o; rebuild();} + +protected: + void changeEvent(QEvent * e); + +private: + void configTree(); + void setItemBackColor(QTreeWidgetItem * i, const QColor & c) {i->setBackgroundColor(0, c); i->setBackgroundColor(1, c);} + void setItemForeColor(QTreeWidgetItem * i, const QColor & c) {i->setForeground(0, c); i->setForeground(1, c);} + void rebuild(); + void refresh(); + + QObject * object; + QFont font_b; + QList props; + bool active_; + +private slots: + void itemClicked(QTreeWidgetItem * item, int column); + void itemChanged(QTreeWidgetItem * item, int column); + +}; + +#endif // PROPERTYEDITOR_H diff --git a/qglengine/widgets/scene_tree.cpp b/qglengine/widgets/scene_tree.cpp new file mode 100644 index 0000000..f64790b --- /dev/null +++ b/qglengine/widgets/scene_tree.cpp @@ -0,0 +1,405 @@ +/* + Stanley Designer + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "scene_tree.h" +#include "ui_scene_tree.h" +#include "glcamera.h" +#include "qglview.h" +#include +#include +#include +#include + +enum Column { + cName, + cVis, + cMaterial +}; +enum ItemRole { + irObject = Qt::UserRole, + irType = Qt::UserRole + 1, +}; + +enum ObjectType { + otNode = 1, + otMesh = 2, + otLight = 4, + otCamera = 8, +}; + + +SceneTree::SceneTree(QWidget * parent): QWidget(parent) { + ui = new Ui::SceneTree(); + ui->setupUi(this); + ui->treeObjects->header()->setSectionResizeMode(cVis, QHeaderView::ResizeToContents); + ui->treeObjects->header()->setSectionsMovable(false); + ui->treeObjects->header()->swapSections(cName, cVis); + icon_empty = QIcon(":/icons/type-empty.png"); + icon_geo = QIcon(":/icons/type-geo.png"); + icon_camera = QIcon(":/icons/type-camera.png"); + icon_light = QIcon(":/icons/type-light.png"); + icon_vis[0] = QIcon(":/icons/layer-visible-off.png"); + icon_vis[1] = QIcon(":/icons/layer-visible-on.png"); + QAction * a = 0; + a = new QAction(QIcon(":/icons/type-camera.png"), "Focus"); + connect(a, SIGNAL(triggered()), this, SLOT(focusObjects())); + ui->treeObjects->addAction(a); + a = new QAction(QIcon(":/icons/edit-delete.png"), "Remove"); + connect(a, SIGNAL(triggered()), this, SLOT(removeObjects())); + ui->treeObjects->addAction(a); + view = 0; + block_tree = false; + connect(ui->treeObjects->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(treeObjects_selectionCnahged())); +} + + +SceneTree::~SceneTree() { + delete ui; +} + + +void SceneTree::assignQGLView(QGLView * v) { + view = v; + connect(view, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + connect(view, SIGNAL(materialsChanged()), this, SLOT(materialsChanged())); + connect(view->scene(), SIGNAL(treeChanged()), this, SLOT(objectsTreeChanged())); + connect(view->scene(), SIGNAL(__objectDeleted(ObjectBase*)), this, SLOT(__objectDeleted(ObjectBase*))); + objectsTreeChanged(); +} + + +void SceneTree::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + if (e->type() == QEvent::LanguageChange) { + ui->retranslateUi(this); + return; + } +} + + +void SceneTree::rememberExpanded(QTreeWidgetItem * ti) { + for (int i = 0; i < ti->childCount(); ++i) { + QTreeWidgetItem * ci = ti->child(i); + if (ci->isExpanded()) + expanded_ << itemObject(ci); + rememberExpanded(ci); + } +} + + +void SceneTree::restoreExpanded(QTreeWidgetItem * ti) { + for (int i = 0; i < ti->childCount(); ++i) { + QTreeWidgetItem * ci = ti->child(i); + ci->setExpanded(expanded_.contains(itemObject(ci))); + restoreExpanded(ci); + } +} + + +void SceneTree::makeObjetTree(ObjectBase * o, QTreeWidgetItem * ti) { + for (int i = 0; i < o->childCount(); ++i) { + ObjectBase * co = o->child(i); + QTreeWidgetItem * ci = new QTreeWidgetItem(ti); + ci->setText(cName, co->name()); + ci->setCheckState(cVis, co->isVisible() ? Qt::Checked : Qt::Unchecked); + ci->setIcon(cVis, icon_vis[co->isVisible(true)]); + if (co->material()) + ci->setText(cMaterial, co->material()->name); + ci->setFlags(ci->flags() | Qt::ItemIsEditable); + ObjectType t = otNode; + switch (co->type()) { + case ObjectBase::glMesh: + if (co->mesh()) { + t = otMesh; + ci->setIcon(cName, icon_geo); + geo_items << ci; + } else { + ci->setIcon(cName, icon_empty); + } + break; + case ObjectBase::glLight: + t = otLight; + ci->setIcon(cName, icon_light); + break; + case ObjectBase::glCamera: + t = otCamera; + ci->setIcon(cName, icon_camera); + break; + default: break; + } + ci->setData(cName, irObject, quintptr(co)); + ci->setData(cName, irType, int(t)); + ci->setSelected(co->isSelected()); + makeObjetTree(co, ci); + } +} + + +ObjectBase * SceneTree::itemObject(QTreeWidgetItem * item) const { + if (!item) return 0; + return (ObjectBase*)(item->data(cName, irObject).toULongLong()); +} + + +int SceneTree::itemType(QTreeWidgetItem * item) const { + if (!item) return otNode; + return item->data(cName, irType).toInt(); +} + + +void SceneTree::selectionChanged() { + if (block_tree) return; + block_tree = true; + QList il = ui->treeObjects->findItems("", Qt::MatchContains | Qt::MatchRecursive); + const ObjectBase * fo = 0; + if (view->selectedObjects().size() == 1) + fo = view->selectedObject(); + foreach (QTreeWidgetItem * i, il) { + ObjectBase * o = itemObject(i); + i->setSelected(o->isSelected()); + if (fo && (fo == o)) { + ui->treeObjects->setCurrentItem(i); + } + } + block_tree = false; + checkButtons(); +} + + +void SceneTree::materialsChanged() { + foreach (QTreeWidgetItem * i, geo_items) { + ObjectBase * o = itemObject(i); + if (!o) continue; + if (o->material()) + i->setText(cMaterial, o->material()->name); + } +} + + +bool SceneTree::filterTree(QTreeWidgetItem * ti, const QString & filter, int types) { + bool ret = false; + for (int i = 0; i < ti->childCount(); ++i) { + QTreeWidgetItem * ci = ti->child(i); + QString cit = ci->text(cName); + int t = itemType(ci); + if (ci->childCount() > 0) { + if (!filterTree(ci, filter, types)) { + ci->setHidden(true); + continue; + } + ci->setHidden(false); + ret = true; + } else { + bool f = false; + if (filter.isEmpty()) { + f = true; + } else { + f = f || cit.contains(filter); + } + if ((types & t) != t) + f = false; + ci->setHidden(!f); + if (f) ret = true; + } + } + return ret; +} + + +void SceneTree::checkButtons() { + bool has_1 = false, has_m = false; + if (view) { + has_1 = !view->selectedObjects().isEmpty(); + has_m = view->selectedObjects().size() > 1; + } + ui->buttonFocus ->setEnabled(has_1); + ui->buttonRemove->setEnabled(has_1); + ui->buttonClone ->setEnabled(has_1); + ui->buttonSelectParent->setEnabled(has_1); + ui->buttonGroup->setEnabled(has_m); +} + + +void SceneTree::treeObjects_selectionCnahged() { + if (block_tree || !view) return; + block_tree = true; + view->scene()->clearSelection(); + QList sol; + QList til = ui->treeObjects->selectedItems(); + foreach (QTreeWidgetItem * i, til) + sol << itemObject(i); + view->scene()->selectObjects(sol); + block_tree = false; + checkButtons(); +} + + +void SceneTree::filter() { + int types = 0; + if (ui->buttonFilterNode ->isChecked()) types |= otNode ; + if (ui->buttonFilterMesh ->isChecked()) types |= otMesh ; + if (ui->buttonFilterLight ->isChecked()) types |= otLight ; + if (ui->buttonFilterCamera->isChecked()) types |= otCamera; + if (types == 0) types = 0xFF; + filterTree(ui->treeObjects->invisibleRootItem(), ui->lineFilter->text(), types); + ui->treeObjects->invisibleRootItem()->setHidden(false); +} + + +void SceneTree::__objectDeleted(ObjectBase * o) { + for (int i = 0; i < geo_items.size(); ++i) + if (itemObject(geo_items[i]) == o) { + geo_items.removeAt(i); + --i; + } +} + + +void SceneTree::on_treeObjects_itemChanged(QTreeWidgetItem * item, int column) { + if (block_tree) return; + if (column == cName) { + ObjectBase * o = itemObject(item); + if (o) o->setName(item->text(cName)); + } + if (column == cVis) { + bool vis = item->checkState(cVis) == Qt::Checked; + QList til = ui->treeObjects->selectedItems(); + if (!til.contains(item)) { + til.clear(); + til << item; + } + foreach (QTreeWidgetItem * ti, til) { + //itemObject(ti)->setVisible(vis); + itemObject(ti)->setVisible(vis); + } + } +} + + +void SceneTree::on_treeObjects_itemMoved(QTreeWidgetItem * item, QTreeWidgetItem * new_parent) { + ObjectBase * co = itemObject(item); + if (!co->hasParent()) return; + //co->parent()->removeChild(co); + if (new_parent == ui->treeObjects->invisibleRootItem()) { + view->scene()->rootObject()->addChild(co); + } else { + ObjectBase * po = itemObject(new_parent); + if (po) + po->addChild(co); + } +} + + +void SceneTree::on_buttonAddNode_clicked() { + if (!view) return; + ObjectBase * no = new ObjectBase(); + view->scene()->addObject(no); + view->scene()->selectObject(no); +} + + +void SceneTree::on_buttonAddLight_clicked() { + if (!view) return; + ObjectBase * no = new Light(); + view->scene()->addObject(no); + view->scene()->selectObject(no); +} + + +void SceneTree::on_buttonClone_clicked() { + if (!view) return; + QList sil = ui->treeObjects->selectedItems(); + QList col; + foreach (QTreeWidgetItem * i, sil) { + ObjectBase * o = itemObject(i); + if (!o) continue; + ObjectBase * no = o->clone(); + o->parent()->addChild(no); + col << no; + } + view->scene()->selectObjects(col); +} + + +void SceneTree::on_buttonSelectParent_clicked() { + if (!view) return; + QList sol = view->scene()->selectedObjects(true); + QSet nsl; + foreach (ObjectBase * o, sol) { + ObjectBase * po = o->parent(); + if (po != view->scene()->rootObject()) + o = po; + nsl << o; + } + view->scene()->selectObjects(nsl.toList()); +} + + +void SceneTree::on_buttonGroup_clicked() { + if (!view) return; + QList sol = view->scene()->selectedObjects(true); + ObjectBase * cp = sol[0]->parent(); + ObjectBase * nr = new ObjectBase(); + cp->addChild(nr); + foreach (ObjectBase * o, sol) + nr->addChild(o); + view->scene()->selectObject(nr); +} + + +void SceneTree::removeObjects() { + if (!view) return; + QList sil = ui->treeObjects->selectedItems(); + foreach (QTreeWidgetItem * i, sil) { + ObjectBase * o = itemObject(i); + if (o) delete o; + } + qDeleteAll(sil); +} + + +void SceneTree::focusObjects() { + if (!view) return; + if (!view->camera()) return; + Box3D bb; + QList ol = view->selectedObjects(); + foreach (ObjectBase * o, ol) { + o->calculateBoundingBox(); + bb |= o->boundingBox(); + } + view->focusOn(bb); +} + + +void SceneTree::objectsTreeChanged() { + int vpos = ui->treeObjects->verticalScrollBar()->value(); + expanded_.clear(); + geo_items.clear(); + rememberExpanded(ui->treeObjects->invisibleRootItem()); + block_tree = true; + ui->treeObjects->clear(); + block_tree = false; + if (!view) return; + block_tree = true; + makeObjetTree(view->scene()->rootObject(), ui->treeObjects->invisibleRootItem()); + restoreExpanded(ui->treeObjects->invisibleRootItem()); + block_tree = false; + filter(); + QApplication::processEvents(); + ui->treeObjects->verticalScrollBar()->setValue(vpos); +} diff --git a/qglengine/widgets/scene_tree.h b/qglengine/widgets/scene_tree.h new file mode 100644 index 0000000..8c13215 --- /dev/null +++ b/qglengine/widgets/scene_tree.h @@ -0,0 +1,85 @@ +/* + Stanley Designer + Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef SCENE_TREE_H +#define SCENE_TREE_H + +#include +#include +#include "glscene.h" + +class QTreeWidgetItem; + +namespace Ui { + class SceneTree; +} + +class SceneTree: public QWidget +{ + Q_OBJECT +public: + SceneTree(QWidget * parent = 0); + ~SceneTree(); + + void assignQGLView(QGLView * v); + +private: + void changeEvent(QEvent * e); + void rememberExpanded(QTreeWidgetItem * ti); + void restoreExpanded(QTreeWidgetItem * ti); + void makeObjetTree(ObjectBase * o, QTreeWidgetItem * ti); + ObjectBase * itemObject(QTreeWidgetItem * item) const; + int itemType(QTreeWidgetItem * item) const; + bool filterTree(QTreeWidgetItem * ti, const QString & filter, int types); + void checkButtons(); + + Ui::SceneTree * ui; + bool block_tree; + QIcon icon_empty, icon_geo, icon_camera, icon_light, icon_vis[2]; + QSet expanded_; + QList geo_items; + QGLView * view; + +private slots: + void treeObjects_selectionCnahged(); + void on_treeObjects_itemChanged(QTreeWidgetItem * item, int column); + void on_treeObjects_itemMoved (QTreeWidgetItem * item, QTreeWidgetItem * new_parent); + void on_buttonAddNode_clicked(); + void on_buttonAddLight_clicked(); + void on_buttonClone_clicked(); + void on_buttonUpdate_clicked() {objectsTreeChanged();} + void on_buttonSelectParent_clicked(); + void on_buttonGroup_clicked(); + + void removeObjects(); + void focusObjects(); + void objectsTreeChanged(); + void selectionChanged(); + void materialsChanged(); + void filter(); + void __objectDeleted(ObjectBase * o); + +public slots: + +signals: + +private: + +}; + +#endif // SCENE_TREE_H diff --git a/qglengine/widgets/scene_tree.ui b/qglengine/widgets/scene_tree.ui new file mode 100644 index 0000000..be71efe --- /dev/null +++ b/qglengine/widgets/scene_tree.ui @@ -0,0 +1,550 @@ + + + SceneTree + + + + 0 + 0 + 345 + 494 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Selection + + + + + + false + + + Focus on selected + + + + :/icons/type-camera.png:/icons/type-camera.png + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + false + + + Delete selected + + + + :/icons/edit-delete.png:/icons/edit-delete.png + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + false + + + Clone selected + + + + :/icons/edit-copy.png:/icons/edit-copy.png + + + + + + + false + + + Group selected + + + G + + + + + + + false + + + Select parent + + + + :/icons/go-top.png:/icons/go-top.png + + + + + + + + + + Add + + + + + + Add empty object + + + + :/icons/add-type-empty.png:/icons/add-type-empty.png + + + + + + + Add light + + + + :/icons/add-type-light.png:/icons/add-type-light.png + + + + + + + Add camera + + + + :/icons/add-type-camera.png:/icons/add-type-camera.png + + + + + + + + + + + + + + Filter + + + + QFormLayout::AllNonFixedFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Name: + + + + + + + + + + Types: + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Nodes + + + + :/icons/type-empty.png:/icons/type-empty.png + + + true + + + + + + + Meshes + + + + :/icons/type-geo.png:/icons/type-geo.png + + + true + + + + + + + Lights + + + + :/icons/type-light.png:/icons/type-light.png + + + true + + + + + + + Cameras + + + + :/icons/type-camera.png:/icons/type-camera.png + + + true + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + + + + + + + Tree + + + + + + Expand tree + + + + :/icons/expand.png:/icons/expand.png + + + + + + + Force update + + + + :/icons/view-refresh.png:/icons/view-refresh.png + + + + + + + Collapse tree + + + + :/icons/collapse.png:/icons/collapse.png + + + + + + + + + + + + Qt::ActionsContextMenu + + + QAbstractItemView::InternalMove + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::ScrollPerPixel + + + true + + + false + + + + Name + + + + + Vis + + + + + Material + + + + + + + + + CLineEdit + QLineEdit +
clineedit.h
+
+ + InternalMoveTreeWidget + QTreeWidget +
treewidget_p.h
+
+
+ + + + + + + buttonExpand + clicked() + treeObjects + expandAll() + + + 288 + 143 + + + 299 + 174 + + + + + buttonCollapse + clicked() + treeObjects + collapseAll() + + + 319 + 137 + + + 332 + 184 + + + + + buttonFocus + clicked() + SceneTree + focusObjects() + + + 26 + 37 + + + 17 + 27 + + + + + buttonRemove + clicked() + SceneTree + removeObjects() + + + 122 + 39 + + + 114 + 26 + + + + + lineFilter + textChanged(QString) + SceneTree + filter() + + + 239 + 100 + + + 355 + 37 + + + + + buttonFilterNode + toggled(bool) + SceneTree + filter() + + + 61 + 137 + + + 353 + 109 + + + + + buttonFilterMesh + toggled(bool) + SceneTree + filter() + + + 95 + 140 + + + 357 + 146 + + + + + buttonFilterLight + toggled(bool) + SceneTree + filter() + + + 135 + 135 + + + 353 + 173 + + + + + buttonFilterCamera + toggled(bool) + SceneTree + filter() + + + 161 + 135 + + + 351 + 226 + + + + + + removeObjects() + focusObjects() + filter() + +
diff --git a/qglengine/widgets/treewidget_p.h b/qglengine/widgets/treewidget_p.h new file mode 100644 index 0000000..7c80f95 --- /dev/null +++ b/qglengine/widgets/treewidget_p.h @@ -0,0 +1,33 @@ +#ifndef TREEWIDGET_H +#define TREEWIDGET_H + +#include +#include +#include +#include + +class InternalMoveTreeWidget: public QTreeWidget +{ + Q_OBJECT +public: + InternalMoveTreeWidget(QWidget * parent = 0): QTreeWidget(parent) {} + +protected: + virtual void dropEvent(QDropEvent * e) { + QList sil = selectedItems(); + if (sil.isEmpty()) return; + QTreeWidget::dropEvent(e); + foreach (QTreeWidgetItem * ti, sil) { + QTreeWidgetItem * ti_p = ti->parent(); + if (!ti_p) ti_p = invisibleRootItem(); + int ti_ppos = ti_p->indexOfChild(ti); + emit itemMoved(ti, ti_p, ti_ppos); + } + } + +signals: + void itemMoved(QTreeWidgetItem * item, QTreeWidgetItem * new_parent, int new_index); + +}; + +#endif // TREEWIDGET_H diff --git a/qglengine/widgets/widgets.qrc b/qglengine/widgets/widgets.qrc new file mode 100644 index 0000000..6345623 --- /dev/null +++ b/qglengine/widgets/widgets.qrc @@ -0,0 +1,15 @@ + + + ../icons/go-jump.png + ../icons/dialog-close.png + ../icons/edit-clear.png + ../icons/configure.png + ../icons/document-save.png + ../icons/edit-find.png + ../icons/list-add.png + ../icons/edit-delete.png + ../icons/edit-copy.png + ../icons/edit-paste.png + ../icons/document-edit.png + + diff --git a/qglview/qglview.cpp b/qglview/qglview.cpp index 81be514..6f73dd6 100644 --- a/qglview/qglview.cpp +++ b/qglview/qglview.cpp @@ -113,10 +113,12 @@ QGLView::~QGLView() { void QGLView::stop() { if (timer) killTimer(timer); + timer = 0; } void QGLView::start(float freq) { + stop(); timer = startTimer(freq <= 0.f ? 0 : int(1000.f / freq)); } @@ -253,6 +255,7 @@ void QGLView::timerEvent(QTimerEvent *) { void QGLView::render() { + if (!isVisible()) return; resizeGL(width(), height()); QRect g_rect(QPoint(), size()); emit glBeforePaint();