/* QGL SceneTree Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "scene_tree.h" #include "ui_scene_tree.h" #include "glcamera.h" #include "qglview.h" #include #include #include #include enum Column { cName, cVis, cMaterial }; enum ItemRole { irObject = Qt::UserRole, irType = Qt::UserRole + 1, }; enum ObjectType { otNode = 1, otMesh = 2, otLight = 4, otCamera = 8, }; QAction * newSeparator() { QAction * s = new QAction(); s->setSeparator(true); return s; } SceneTree::SceneTree(QWidget * parent): QWidget(parent) { ui = new Ui::SceneTree(); ui->setupUi(this); ui->treeObjects->header()->setSectionResizeMode(cVis, QHeaderView::ResizeToContents); ui->treeObjects->header()->setSectionsMovable(false); ui->treeObjects->header()->swapSections(cName, cVis); icon_empty = QIcon(":/icons/type-empty.png"); icon_geo = QIcon(":/icons/type-geo.png"); icon_camera = QIcon(":/icons/type-camera.png"); icon_light = QIcon(":/icons/type-light.png"); icon_vis[0] = QIcon(":/icons/layer-visible-off.png"); icon_vis[1] = QIcon(":/icons/layer-visible-on.png"); ui->treeObjects->addActions(actionsSelection()); ui->treeObjects->addAction (newSeparator()); ui->treeObjects->addActions(actionsAdd()); ui->buttonFilter->addActions(QList() << ui->actionFilter_node << ui->actionFilter_mesh << ui->actionFilter_light << ui->actionFilter_camera); view = 0; hidden_by_filter = obj_count = 0; block_tree = false; connect(ui->treeObjects->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(treeObjects_selectionCnahged())); } SceneTree::~SceneTree() { delete ui; } void SceneTree::assignQGLView(QGLView * v) { view = v; objectsTreeChanged(); if (!view) return; connect(view, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); connect(view, SIGNAL(materialsChanged()), this, SLOT(materialsChanged())); connect(view->scene(), SIGNAL(treeChanged()), this, SLOT(objectsTreeChanged())); connect(view->scene(), SIGNAL(__objectDeleted(ObjectBase*)), this, SLOT(__objectDeleted(ObjectBase*))); view->setContextActions(actionsSelection()); checkActions(); } QList SceneTree::actionsAdd() { QList ret; ret << ui->actionAdd_node << ui->actionAdd_light << ui->actionAdd_camera; return ret; } QList SceneTree::actionsSelection() { QList ret; ret << ui->actionFocus << newSeparator() << ui->actionGroup << ui->actionClone << newSeparator() << ui->actionSelect_parent << ui->actionSelect_by_mesh << ui->actionSelect_by_material << newSeparator() << ui->actionTransfer_transform_to_children << newSeparator() << ui->actionActive_camera << ui->actionDefault_camera << newSeparator() << ui->actionRemove; return ret; } void SceneTree::changeEvent(QEvent * e) { QWidget::changeEvent(e); if (e->type() == QEvent::LanguageChange) { ui->retranslateUi(this); return; } } void SceneTree::rememberExpanded(QTreeWidgetItem * ti) { for (int i = 0; i < ti->childCount(); ++i) { QTreeWidgetItem * ci = ti->child(i); if (ci->isExpanded()) expanded_ << itemObject(ci); rememberExpanded(ci); } } void SceneTree::restoreExpanded(QTreeWidgetItem * ti) { for (int i = 0; i < ti->childCount(); ++i) { QTreeWidgetItem * ci = ti->child(i); ci->setExpanded(expanded_.contains(itemObject(ci))); restoreExpanded(ci); } } void SceneTree::makeObjetTree(ObjectBase * o, QTreeWidgetItem * ti) { ++obj_count; for (int i = 0; i < o->childCount(); ++i) { ObjectBase * co = o->child(i); QTreeWidgetItem * ci = new QTreeWidgetItem(ti); ci->setText(cName, co->name()); ci->setCheckState(cVis, co->isVisible() ? Qt::Checked : Qt::Unchecked); ci->setIcon(cVis, icon_vis[co->isVisible(true)]); if (co->material()) ci->setText(cMaterial, co->material()->name); ci->setFlags(ci->flags() | Qt::ItemIsEditable); ObjectType t = otNode; switch (co->type()) { case ObjectBase::glMesh: if (co->mesh()) { t = otMesh; ci->setIcon(cName, icon_geo); geo_items << ci; } else { ci->setIcon(cName, icon_empty); } break; case ObjectBase::glLight: t = otLight; ci->setIcon(cName, icon_light); break; case ObjectBase::glCamera: t = otCamera; ci->setIcon(cName, icon_camera); ci->setText(cVis, (co == view->camera()) ? "*" : ""); cam_items << ci; break; default: break; } ci->setData(cName, irObject, quintptr(co)); ci->setData(cName, irType, int(t)); ci->setSelected(co->isSelected()); makeObjetTree(co, ci); } } ObjectBase * SceneTree::itemObject(QTreeWidgetItem * item) const { if (!item) return 0; return (ObjectBase*)(item->data(cName, irObject).toULongLong()); } int SceneTree::itemType(QTreeWidgetItem * item) const { if (!item) return otNode; return item->data(cName, irType).toInt(); } void SceneTree::selectionChanged() { if (block_tree) return; block_tree = true; QList il = ui->treeObjects->findItems("", Qt::MatchContains | Qt::MatchRecursive); const ObjectBase * fo = 0; if (view->selectedObjects().size() == 1) fo = view->selectedObject(); foreach (QTreeWidgetItem * i, il) { ObjectBase * o = itemObject(i); i->setSelected(o->isSelected()); if (fo && (fo == o)) { ui->treeObjects->setCurrentItem(i); } } block_tree = false; checkActions(); } void SceneTree::materialsChanged() { foreach (QTreeWidgetItem * i, geo_items) { ObjectBase * o = itemObject(i); if (!o) continue; if (o->material()) i->setText(cMaterial, o->material()->name); } } void SceneTree::cameraChanged() { if (!view) return; foreach (QTreeWidgetItem * i, cam_items) { ObjectBase * o = itemObject(i); if (!o) continue; i->setText(cVis, (o == view->camera()) ? "*" : ""); } } bool SceneTree::filterTree(QTreeWidgetItem * ti, const QString & filter, int types) { bool ret = false; for (int i = 0; i < ti->childCount(); ++i) { QTreeWidgetItem * ci = ti->child(i); QString cit = ci->text(cName); int t = itemType(ci); if (ci->childCount() > 0) { if (!filterTree(ci, filter, types)) { ci->setHidden(true); ++hidden_by_filter; continue; } ci->setHidden(false); ret = true; } else { bool f = false; if (filter.isEmpty()) { f = true; } else { f = f || cit.contains(filter, Qt::CaseInsensitive); } if ((types & t) != t) f = false; ci->setHidden(!f); if (f) ret = true; else ++hidden_by_filter; } } return ret; } void SceneTree::checkActions() { bool has_1 = false, has_m = false; if (view) { has_1 = !view->selectedObjects().isEmpty(); has_m = view->selectedObjects().size() > 1; } ui->actionFocus ->setEnabled(has_1); ui->actionRemove->setEnabled(has_1); ui->actionClone ->setEnabled(has_1); ui->actionGroup->setEnabled(has_m); ui->actionTransfer_transform_to_children->setEnabled(has_1); ui->actionSelect_parent->setEnabled(has_1); ui->actionSelect_by_mesh->setEnabled(has_1); ui->actionSelect_by_material->setEnabled(has_1); ui->actionActive_camera->setEnabled(has_1); } void SceneTree::treeObjects_selectionCnahged() { if (block_tree || !view) return; block_tree = true; view->scene()->clearSelection(); ObjectBaseList sol; QList til = ui->treeObjects->selectedItems(); foreach (QTreeWidgetItem * i, til) sol << itemObject(i); view->scene()->selectObjects(sol); block_tree = false; checkActions(); } void SceneTree::filter() { int types = 0; if (ui->actionFilter_node ->isChecked()) types |= otNode ; if (ui->actionFilter_mesh ->isChecked()) types |= otMesh ; if (ui->actionFilter_light ->isChecked()) types |= otLight ; if (ui->actionFilter_camera->isChecked()) types |= otCamera; if (types == 0) types = 0xFF; hidden_by_filter = 0; filterTree(ui->treeObjects->invisibleRootItem(), ui->lineFilter->text(), types); ui->treeObjects->invisibleRootItem()->setHidden(false); ui->labelCounts->setText(tr("%1 objects, %2 hide by filter").arg(obj_count).arg(hidden_by_filter)); } void SceneTree::__objectDeleted(ObjectBase * o) { for (int i = 0; i < geo_items.size(); ++i) if (itemObject(geo_items[i]) == o) { geo_items.removeAt(i); --i; } for (int i = 0; i < cam_items.size(); ++i) if (itemObject(cam_items[i]) == o) { cam_items.removeAt(i); --i; } } void SceneTree::on_treeObjects_itemChanged(QTreeWidgetItem * item, int column) { if (block_tree) return; if (column == cName) { ObjectBase * o = itemObject(item); if (o) o->setName(item->text(cName)); } if (column == cVis) { bool vis = item->checkState(cVis) == Qt::Checked; QList til = ui->treeObjects->selectedItems(); if (!til.contains(item)) { til.clear(); til << item; } foreach (QTreeWidgetItem * ti, til) { //itemObject(ti)->setVisible(vis); itemObject(ti)->setVisible(vis); } } } void SceneTree::on_treeObjects_itemMoved(QTreeWidgetItem * item, QTreeWidgetItem * new_parent) { ObjectBase * co = itemObject(item); if (!co->hasParent()) return; //co->parent()->removeChild(co); if (new_parent == ui->treeObjects->invisibleRootItem()) { view->scene()->rootObject()->addChild(co); } else { ObjectBase * po = itemObject(new_parent); if (po) po->addChild(co); } } void SceneTree::on_actionAdd_node_triggered() { if (!view) return; ObjectBase * no = new ObjectBase(); view->scene()->addObject(no); view->scene()->selectObject(no); } void SceneTree::on_actionAdd_light_triggered() { if (!view) return; ObjectBase * no = new Light(); view->scene()->addObject(no); view->scene()->selectObject(no); } void SceneTree::on_actionAdd_camera_triggered() { if (!view) return; ObjectBase * no = new Camera(); view->scene()->addObject(no); view->scene()->selectObject(no); } void SceneTree::on_actionClone_triggered() { if (!view) return; QList sil = ui->treeObjects->selectedItems(); ObjectBaseList col; foreach (QTreeWidgetItem * i, sil) { ObjectBase * o = itemObject(i); if (!o) continue; ObjectBase * no = o->clone(); o->parent()->addChild(no); col << no; } view->scene()->selectObjects(col); } void SceneTree::on_actionGroup_triggered() { if (!view) return; ObjectBaseList sol = view->scene()->selectedObjects(true); ObjectBase * cp = sol[0]->parent(); ObjectBase * nr = new ObjectBase(); cp->addChild(nr); foreach (ObjectBase * o, sol) nr->addChild(o); view->scene()->selectObject(nr); } void SceneTree::on_actionTransfer_transform_to_children_triggered() { if (!view) return; ObjectBaseList sol = view->scene()->selectedObjects(true); foreach (ObjectBase * o, sol) o->transferTransformToChildren(); } void SceneTree::on_actionActive_camera_triggered() { if (!view) return; ObjectBase * o = view->scene()->selectedObject(); if (!o) return; if (o->type() != ObjectBase::glCamera) return; view->setCamera((Camera *)o); cameraChanged(); } void SceneTree::on_actionDefault_camera_triggered() { if (!view) return; view->setDefaultCamera(); cameraChanged(); } void SceneTree::on_actionSelect_parent_triggered() { if (!view) return; ObjectBaseList sol = view->scene()->selectedObjects(true); QSet nsl; foreach (ObjectBase * o, sol) { ObjectBase * po = o->parent(); if (po != view->scene()->rootObject()) o = po; nsl << o; } view->scene()->selectObjects(nsl.toList()); } void SceneTree::on_actionSelect_by_mesh_triggered() { view->scene()->selectObjectsByMesh(); } void SceneTree::on_actionSelect_by_material_triggered() { view->scene()->selectObjectsByMaterial(); } void SceneTree::removeObjects() { if (!view) return; QList sil = ui->treeObjects->selectedItems(); foreach (QTreeWidgetItem * i, sil) { ObjectBase * o = itemObject(i); if (o) delete o; } qDeleteAll(sil); } void SceneTree::focusObjects() { if (!view) return; if (!view->camera()) return; Box3D bb; ObjectBaseList ol = view->selectedObjects(); foreach (ObjectBase * o, ol) { o->calculateBoundingBox(); bb |= o->boundingBox(); } view->focusOn(bb); } void SceneTree::objectsTreeChanged() { int vpos = ui->treeObjects->verticalScrollBar()->value(); expanded_.clear(); geo_items.clear(); cam_items.clear(); rememberExpanded(ui->treeObjects->invisibleRootItem()); block_tree = true; ui->treeObjects->clear(); block_tree = false; if (!view) return; block_tree = true; obj_count = 0; makeObjetTree(view->scene()->rootObject(), ui->treeObjects->invisibleRootItem()); --obj_count; restoreExpanded(ui->treeObjects->invisibleRootItem()); block_tree = false; filter(); QApplication::processEvents(); ui->treeObjects->verticalScrollBar()->setValue(vpos); }