initial commit
This commit is contained in:
504
widgets/scene_tree.cpp
Normal file
504
widgets/scene_tree.cpp
Normal file
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "scene_tree.h"
|
||||
#include "ui_scene_tree.h"
|
||||
#include "glcamera.h"
|
||||
#include "qglview.h"
|
||||
#include <QTreeWidget>
|
||||
#include <QScrollBar>
|
||||
#include <QAction>
|
||||
#include <QEvent>
|
||||
|
||||
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<QAction*>() << 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<QAction *> SceneTree::actionsAdd() {
|
||||
QList<QAction *> ret;
|
||||
ret << ui->actionAdd_node << ui->actionAdd_light << ui->actionAdd_camera;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QList<QAction *> SceneTree::actionsSelection() {
|
||||
QList<QAction *> 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<QTreeWidgetItem*> 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<QTreeWidgetItem*> 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<QTreeWidgetItem*> 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<QTreeWidgetItem*> 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<ObjectBase*> 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<QTreeWidgetItem*> 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);
|
||||
}
|
||||
Reference in New Issue
Block a user