git-svn-id: svn://db.shs.com.ru/libs@626 a8b55f48-bf90-11e4-a774-851b48703e85

This commit is contained in:
2019-11-22 15:20:52 +00:00
parent 5e2b563d57
commit 09248aae34
128 changed files with 16038 additions and 0 deletions

471
qglengine/glscene.cpp Normal file
View 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;
}