git-svn-id: svn://db.shs.com.ru/libs@626 a8b55f48-bf90-11e4-a774-851b48703e85
This commit is contained in:
471
qglengine/glscene.cpp
Normal file
471
qglengine/glscene.cpp
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "glscene.h"
|
||||
#include "glcamera.h"
|
||||
#include "glmesh.h"
|
||||
#include "qglview.h"
|
||||
#include <chunkstream.h>
|
||||
|
||||
|
||||
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<ObjectBase*> 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<QString> & 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<ObjectBase *> 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<ObjectBase * > ol = root_->children(true);
|
||||
foreach (ObjectBase * o, ol) {
|
||||
o->selected_ = false;
|
||||
}
|
||||
emitSelectionChanged();
|
||||
}
|
||||
|
||||
|
||||
QList<ObjectBase * > 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<ObjectBase * > 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<QString> names;
|
||||
foreach (Material * m, materials) {
|
||||
if (m->name.isEmpty()) m->name = "default_000";
|
||||
m->name = uniqueName(m->name, names);
|
||||
names << m->name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QList<ObjectBase *> 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<ObjectBase * > 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<Light * >(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<ObjectBase*> 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<short> geom_ind, mat_ind;
|
||||
QList<ObjectBase*> 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<short> 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<ObjectBase*> 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;
|
||||
}
|
||||
Reference in New Issue
Block a user