/* 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); }