/* GLObjectBase & Light 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 "globject.h" #include "glcamera.h" #include "glscene.h" #include "glmesh.h" #include static int _count = 0; ObjectBase::ObjectBase(Mesh * geom, Material * mat) { type_ = glMesh; render_mode = View; prev_pass = rpSolid; parent_ = nullptr; color_ = Qt::white; is_root = is_init = selected_ = false; visible_ = accept_fog = accept_light = cast_shadow = rec_shadow = select_ = true; line_width = -1.; id_ = 0; blend_src = GL_SRC_ALPHA; blend_dest = GL_ONE_MINUS_SRC_ALPHA; type_ = glMesh; raw_matrix = selected_aim = false; mat_.setToIdentity(); scene_ = nullptr; mesh_ = geom; material_ = mat; //qDebug() << "ObjectBase, now" << ++_count; } ObjectBase::~ObjectBase() { //qDebug() << "~ObjectBase, now" << --_count; if (parent_) parent_->children_.removeAll(this); if (scene_) { scene_->__objectDeleted(this); scene_->setTreeChanged(); scene_->setTreeStructChanged(); } foreach (ObjectBase * c, children_) { c->parent_ = nullptr; delete c; } } ObjectBase * ObjectBase::clone(bool withChildren) { ObjectBase * o = new ObjectBase(); o->prev_pass = prev_pass; o->is_init = false; o->accept_light = accept_light; o->accept_fog = accept_fog; o->visible_ = visible_; o->color_ = color_; o->type_ = type_; o->raw_matrix = raw_matrix; o->mat_ = mat_; o->trans = trans; o->itransform_ = itransform_; o->bound = bound; o->name_ = name_;// + "_copy"; o->blend_src = blend_src; o->blend_dest = blend_dest; o->pos_h = pos_h; o->material_ = material_; o->mesh_ = mesh_; o->meta = meta; o->scene_ = nullptr; if (withChildren) { for (int i = 0; i < children_.size(); ++i) o->addChild(children_[i]->clone(withChildren)); } return o; } void ObjectBase::destroy() { if (mesh_) delete mesh_; } void ObjectBase::init() { calculateBoundingBox(); //material_.reflection.create(); //qDebug() << "init" << vbo.buffer_; is_init = true; } RenderPass ObjectBase::pass() const { RenderPass ret = rpSolid; if (material_) if (material_->hasTransparency()) ret = rpTransparent; return ret; } void ObjectBase::setScene(Scene * v) { scene_ = v; foreach (ObjectBase * c, children_) c->setScene(v); } void ObjectBase::addChild(ObjectBase * o) { if (o == this) return; if (o->parent_) o->parent_->children_.removeAll(o); children_ << o; o->parent_ = this; o->setScene(scene_); o->buildTransform(); /*if (scene_) { ObjectBaseList cl = o->children(true); cl << o; //foreach (ObjectBase * i, cl) { // emit view_->objectAdded(i); //} }*/ setSceneTreeChanged(); } void ObjectBase::removeChild(ObjectBase * o) { if (o == this) return; children_.removeAll(o); o->parent_ = nullptr; o->buildTransform(); setSceneTreeChanged(); } void ObjectBase::removeChild(int index) { children_[index]->parent_ = nullptr; children_[index]->buildTransform(); children_.removeAt(index); setSceneTreeChanged(); } void ObjectBase::clearChildren(bool deleteAll) { foreach (ObjectBase * i, children_) { i->scene_ = nullptr; i->parent_ = nullptr; i->clearChildren(deleteAll); if (deleteAll) { delete i; } else { i->buildTransform(); } } children_.clear(); setSceneTreeChanged(); } ObjectBase * ObjectBase::child(int index) { if (index < 0 || index >= children_.size()) return nullptr; return children_[index]; } ObjectBase * ObjectBase::child(const QString & name) { foreach (ObjectBase * i, children_) if (i->name_ == name) return i; return nullptr; } const ObjectBase * ObjectBase::child(int index) const { if (index < 0 || index >= children_.size()) return nullptr; return children_[index]; } const ObjectBase * ObjectBase::child(const QString & name) const { foreach (ObjectBase * i, children_) if (i->name_ == name) return i; return nullptr; } ObjectBaseList ObjectBase::children(bool all_) { if (!all_) return children_; ObjectBaseList cl; addChildren(cl, this); return cl; } bool ObjectBase::isVisible(bool check_parents) const { if (!check_parents) return visible_; if (!visible_) return false; ObjectBase * p = parent_; while (p) { if (!p->visible_) return false; p = p->parent_; } return true; } void ObjectBase::setVisible(bool v) { visible_ = v; setSceneTreeChanged(); } void ObjectBase::rotateZ(GLfloat a) { raw_matrix = false; trans.setRotationZ(trans.rotationZ() + a); //angles_.setZ(angles_.z() + a); //while (angles_.z() < -360.f) angles_.setZ(angles_.z() + 360.f); //while (angles_.z() > 360.f) angles_.setZ(angles_.z() - 360.f); buildTransform(); } void ObjectBase::setRotationZ(GLfloat a) { raw_matrix = false; trans.setRotationZ(a); //angles_.setZ(a); //while (angles_.z() < -360.f) angles_.setZ(angles_.z() + 360.f); //while (angles_.z() > 360.f) angles_.setZ(angles_.z() - 360.f); buildTransform(); } void ObjectBase::setTransform(const Transform & t) { trans = t; buildTransform(); } void ObjectBase::addChildren(ObjectBaseList & list, ObjectBase * where) { foreach (ObjectBase * i, where->children_) { list << i; addChildren(list, i); } } void ObjectBase::calculateBoundingBox() { bound = Box3D(); if (mesh_) { bound = mesh_->boundingBox(); QVector c = bound.corners(), tc; foreach (QVector3D p, c) tc << (itransform_ * QVector4D(p, 1)).toVector3D(); bound = Box3D(tc); } foreach (ObjectBase * i, children_) { i->calculateBoundingBox(); bound |= i->boundingBox(); } } void ObjectBase::updateTransform() { buildTransform(true); } void ObjectBase::setProperty(const QString & pn, const QVariant & v) { meta[pn] = v; } QVariant ObjectBase::property(const QString & pn, bool * exists) const { if (exists) *exists = meta.contains(pn); return meta.value(pn); } bool ObjectBase::hasProperty(const QString & pn) const { return meta.contains(pn); } void ObjectBase::removeProperty(const QString & pn) { meta.remove(pn); } void ObjectBase::setMatrix(const QMatrix4x4 & t) { //raw_matrix = true; //mat_ = t; //pos_ = mat_.column(3).toVector3D(); //mat_.setColumn(3, QVector4D(0., 0., 0., 1.)); raw_matrix = false; trans.setMatrix(t); buildTransform(); } QMatrix4x4 ObjectBase::matrix() const { return trans.matrix(); } QVector3D ObjectBase::inParentSpace(const QVector3D & v) const { if (!parent_) return v; return (parent_->matrix() * QVector4D(v, 1)).toVector3D(); } void ObjectBase::transferTransformToChildren(bool only_scale) { QMatrix4x4 m = trans.matrix(); if (only_scale) m = trans.matrixScale(); foreach (ObjectBase * i, children_) i->trans.setMatrix(m * i->trans.matrix()); if (only_scale) resetScale(); else setMatrix(QMatrix4x4()); } void ObjectBase::cleanTree() { for (int i = 0; i < children_.size(); ++i) { ObjectBase * o = children_[i]; if (!o->hasChildren() && !o->mesh() && (o->type() == glMesh)) { delete o; --i; } o->cleanTree(); } } bool ObjectBase::isSelected(bool check_parents) const { if (!check_parents) return selected_; if (selected_) return true; ObjectBase * p = parent_; while (p) { if (p->selected_) return true; p = p->parent_; } return false; } void ObjectBase::setSelected(bool yes) { //qDebug() << "select" << name() << view_; if (select_) selected_ = yes; if (!selected_) selected_aim = false; } ObjectBase * ObjectBase::selectedParent() const { ObjectBase * p = parent_; while (p) { if (p->selected_) return p; p = p->parent_; } return 0; } void ObjectBase::setMaterial(Material * m, bool with_children) { material_ = m; if (with_children) foreach (ObjectBase * i, children_) i->setMaterial(m, true); setObjectsChanged(); if (scene_) scene_->mat_changed = scene_->tree_changed = true; } void ObjectBase::setColor(QColor c, bool with_children) { color_ = c; if (with_children) foreach (ObjectBase * i, children_) i->setColor(c, true); setObjectsChanged(); } void ObjectBase::setMesh(Mesh * v) { if (scene_) v = scene_->attachMesh(v); mesh_ = v; setSceneTreeChanged(); setObjectsChanged(); } void ObjectBase::buildTransform(bool force) { if (force) trans.setDirty(); itransform_.setToIdentity(); ObjectBase * p = parent_; if (p) itransform_ = p->itransform_; //if (raw_matrix) { // itransform_.translate(pos_); // itransform_ *= mat_; // //qDebug() << "raw_matrix" << itransform_; //} else localTransform(itransform_); //qDebug() << name_ << itransform_; foreach (ObjectBase * i, children_) i->buildTransform(force); setObjectsChanged(); } void ObjectBase::initInternal() { init(); foreach (ObjectBase * i, children_) i->initInternal(); } void ObjectBase::localTransform(QMatrix4x4 & m) { m *= trans.matrix(); } void ObjectBase::setSceneTreeChanged() { if (scene_) { scene_->setTreeChanged(); scene_->setTreeStructChanged(); } setObjectsChanged(); } void ObjectBase::setObjectsChanged() { int p = pass(); if (mesh_) { mesh_->setObjectsChanged (p, true); mesh_->setSelectionChanged(p, true); if (prev_pass != p) { mesh_->setObjectsChanged (prev_pass, true); mesh_->setSelectionChanged(prev_pass, true); } } prev_pass = p; } QMatrix4x4 ObjectBase::worldMatrix(QMatrix4x4 parent) const { QMatrix4x4 mat; //mat.translate(pos_); //if (raw_matrix) { // mat *= mat_; //} else { // if (angles_.z() != 0.f) mat.rotate(angles_.z(), 0., 0., 1.); // if (angles_.y() != 0.f) mat.rotate(angles_.y(), 0., 1., 0.); // if (angles_.x() != 0.f) mat.rotate(angles_.x(), 1., 0., 0.); // mat.scale(scale_); //} mat = trans.matrix(); return parent * mat; } AimedObject::AimedObject() { aim_dist = 1.; } QVector3D AimedObject::worldAim() const { QVector3D ret = worldPos() + worldDirection() * aim_dist; return ret; } void AimedObject::setAim(const QVector3D & p) { QVector3D dir = p - pos(); trans.setRotation(Transform::fromDirection(dir, trans.rotationY())); aim_dist = dir.length(); buildTransform(); //if (!p.isNull()) //qDebug() << "setAim" << p << aim() << worldAim(); } QVector3D AimedObject::direction() const { return trans.direction(); } void AimedObject::setDirection(const QVector3D & d) { //double len = qMax(aim_.length(), 0.001f); //aim_ = d.normalized() * len; buildTransform(); } void AimedObject::flyCloser(double s) { double tl = 1. / (1. + s); move(direction() * aim_dist * (1. - tl)); aim_dist *= tl; } void AimedObject::flyFarer(double s) { double tl = 1. * (1. + s); move(direction() * aim_dist * (1. - tl)); aim_dist *= tl; } void AimedObject::flyToDistance(double d) { move(direction() * (aim_dist - d)); aim_dist = d; //qDebug() << d << (aim() - pos()).length() << aim(); } void AimedObject::moveForward(const float & x, bool withZ) { QVector3D dv = itransform_.mapVector(QVector3D(0, 0, -x)); if (!withZ) dv[2] = 0.; move(dv); } void AimedObject::moveLeft(const float & x, bool withZ) { QVector3D dv = itransform_.mapVector(QVector3D(-x, 0, 0)); if (!withZ) dv[2] = 0.; move(dv); } void AimedObject::moveUp(const float & x, bool onlyZ) { QVector3D dv = itransform_.mapVector(QVector3D(0, x, 0)); if (onlyZ) dv[0] = dv[1] = 0.; move(dv); } void AimedObject::orbitZ(const float & a) { QVector3D pa = aim(); rotateZ(-a); move(pa - aim()); } void AimedObject::orbitXY(const float & a) { QVector3D pa = aim(); rotateX(-a); move(pa - aim()); } void AimedObject::transformChanged() { } Light::Light(): AimedObject(), shadow_map(0, true, GL_R16F) { type_ = glLight; light_type = Omni; intensity = 1.; angle_start = angle_end = 180.; decay_linear = decay_quadratic = decay_start = 0.; decay_const = decay_end = 1.; setDirection(0, 0, -1.); } Light::Light(const QVector3D & p, const QColor & c, float i): AimedObject(), shadow_map(0, true, GL_R16F) { type_ = glLight; light_type = Omni; intensity = i; color_ = c; angle_start = angle_end = 180.; decay_linear = decay_quadratic = decay_start = 0.; decay_const = decay_end = 1.; setPos(p); setDirection(0, 0, -1.); } ObjectBase * Light::clone(bool withChildren) { Light * o = new Light(*this); //GLObjectBase::clone(withChildren); o->is_init = false; o->name_ = name_;// + "_copy"; o->scene_ = nullptr; o->children_.clear(); if (withChildren) { for (int i = 0; i < children_.size(); ++i) o->addChild(children_[i]->clone(withChildren)); } o->color_ = color_; o->light_type = light_type; o->trans = trans; o->aim_dist = aim_dist; o->angle_start = angle_start; o->angle_end = angle_end; o->intensity = intensity; o->decay_const = decay_const; o->decay_linear = decay_linear; o->decay_quadratic = decay_quadratic; o->meta = meta; return o; } void Light::apply() { if (scene_) scene_->setLightsChanged(); } QDataStream & operator <<(QDataStream & s, const ObjectBase * p) { ChunkStream cs; //qDebug() << "place" << p->name() << "..."; cs.add(1, int(p->type_)).add(2, p->accept_light).add(3, p->accept_fog).add(4, p->visible_) .add(5, p->cast_shadow).add(6, p->rec_shadow).add(7, p->raw_matrix).add(8, p->line_width) .add(9, int(p->render_mode)).add(14, p->mat_).add(16, p->children_.size()) .add(17, p->name_).add(18, p->meta).add(19, p->color_).add(20, p->trans); //qDebug() << "place self done"; if (p->type_ == ObjectBase::glLight) { //qDebug() << "place light ..."; const Light * l = (const Light*)p; cs.add(101, l->angle_start).add(102, l->angle_end).add(103, l->intensity) .add(104, l->decay_const).add(105, l->decay_linear).add(106, l->decay_quadratic) .add(107, l->decay_start).add(108, l->decay_end).add(109, int(l->light_type)) .add(111, l->distance()); } if (p->type_ == ObjectBase::glCamera) { //qDebug() << "place camera ..."; const Camera * c = (const Camera*)p; cs.add(200, c->aim()).add(201, c->fov_).add(202, c->depth_start) .add(206, c->mirror_x).add(207, c->mirror_y).add(208, c->distance()) .add(209, c->roll_); } //qDebug() << "place" << p->name() << cs.data().size() << s.device()->size(); s << cs.data(); foreach (const ObjectBase * c, p->children_) s << c; return s; } QDataStream & operator >>(QDataStream & s, ObjectBase *& p) { ChunkStream cs(s); p = nullptr; int ccnt = 0; Light * l = nullptr; Camera * c = nullptr; //qDebug() << "read obj ..."; while (!cs.atEnd()) { switch (cs.read()) { case 1: { ObjectBase::Type type = (ObjectBase::Type)cs.getData(); switch (type) { case ObjectBase::glMesh: p = new ObjectBase(); break; case ObjectBase::glLight: p = new Light(); l = (Light*)p; break; case ObjectBase::glCamera: p = new Camera(); c = (Camera*)p; break; default : break; } if (p) p->type_ = type; } break; case 2: if (p) p->accept_light = cs.getData(); break; case 3: if (p) p->accept_fog = cs.getData(); break; case 4: if (p) p->visible_ = cs.getData(); break; case 5: if (p) p->cast_shadow = cs.getData(); break; case 6: if (p) p->rec_shadow = cs.getData(); break; case 7: if (p) p->raw_matrix = cs.getData(); break; case 8: if (p) p->line_width = cs.getData(); break; case 9: if (p) p->render_mode = (ObjectBase::RenderMode)cs.getData(); break; case 14: if (p) p->mat_ = cs.getData(); break; case 16: if (p) ccnt = cs.getData(); break; case 17: if (p) p->name_ = cs.getData(); break; case 18: if (p) p->meta = cs.getData(); break; case 19: if (p) p->color_ = cs.getData(); break; case 20: if (p) p->trans = cs.getData(); break; case 100: if (l) l->setAim(cs.getData()); break; case 101: if (l) l->angle_start = cs.getData(); break; case 102: if (l) l->angle_end = cs.getData(); break; case 103: if (l) l->intensity = cs.getData(); break; case 104: if (l) l->decay_const = cs.getData(); break; case 105: if (l) l->decay_linear = cs.getData(); break; case 106: if (l) l->decay_quadratic = cs.getData(); break; case 107: if (l) l->decay_start = cs.getData(); break; case 108: if (l) l->decay_end = cs.getData(); break; case 109: if (l) l->light_type = (Light::Type)cs.getData(); break; case 111: if (l) l->setDistance(cs.getData()); break; case 200: if (c) c->setAim(cs.getData()); break; case 201: if (c) c->setFOV(cs.getData()); break; case 202: if (c) c->setDepthStart(cs.getData()); break; case 206: if (c) c->mirror_x = cs.getData(); break; case 207: if (c) c->mirror_y = cs.getData(); break; case 208: if (c) c->setDistance(cs.getData()); break; case 209: if (c) c->roll_ = cs.getData(); break; } } //qDebug() << p->name() << ccnt; for (int i = 0; i < ccnt; ++i) { ObjectBase * c = nullptr; s >> c; if (!c) continue; c->parent_ = p; p->children_ << c; } p->buildTransform(); return s; }