/* 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->buttonSelectByMesh->setEnabled(has_1); ui->buttonSelectByMaterial->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_buttonSelectByMesh_clicked() { view->scene()->selectObjectsByMesh(); } void SceneTree::on_buttonSelectByMaterial_clicked() { view->scene()->selectObjectsByMaterial(); } 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); }