This repository has been archived on 2020-09-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
libs/qglengine/globject.cpp
2020-08-25 22:24:02 +03:00

718 lines
17 KiB
C++

/*
QGL ObjectBase & 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 <http://www.gnu.org/licenses/>.
*/
#include "globject.h"
#include "glcamera.h"
#include "glscene.h"
#include "glmesh.h"
#include <chunkstream.h>
//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<QVector3D> 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<int>();
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<bool>(); break;
case 3: if (p) p->accept_fog = cs.getData<bool>(); break;
case 4: if (p) p->visible_ = cs.getData<bool>(); break;
case 5: if (p) p->cast_shadow = cs.getData<bool>(); break;
case 6: if (p) p->rec_shadow = cs.getData<bool>(); break;
case 7: if (p) p->raw_matrix = cs.getData<bool>(); break;
case 8: if (p) p->line_width = cs.getData<float>(); break;
case 9: if (p) p->render_mode = (ObjectBase::RenderMode)cs.getData<int>(); break;
case 14: if (p) p->mat_ = cs.getData<QMatrix4x4>(); break;
case 16: if (p) ccnt = cs.getData<int>(); break;
case 17: if (p) p->name_ = cs.getData<QString>(); break;
case 18: if (p) p->meta = cs.getData<QVariantMap>(); break;
case 19: if (p) p->color_ = cs.getData<QColor>(); break;
case 20: if (p) p->trans = cs.getData<Transform>(); break;
case 100: if (l) l->setAim(cs.getData<QVector3D>()); break;
case 101: if (l) l->angle_start = cs.getData<GLfloat>(); break;
case 102: if (l) l->angle_end = cs.getData<GLfloat>(); break;
case 103: if (l) l->intensity = cs.getData<GLfloat>(); break;
case 104: if (l) l->decay_const = cs.getData<GLfloat>(); break;
case 105: if (l) l->decay_linear = cs.getData<GLfloat>(); break;
case 106: if (l) l->decay_quadratic = cs.getData<GLfloat>(); break;
case 107: if (l) l->decay_start = cs.getData<GLfloat>(); break;
case 108: if (l) l->decay_end = cs.getData<GLfloat>(); break;
case 109: if (l) l->light_type = (Light::Type)cs.getData<int>(); break;
case 111: if (l) l->setDistance(cs.getData<double>()); break;
case 200: if (c) c->setAim(cs.getData<QVector3D>()); break;
case 201: if (c) c->setFOV(cs.getData<GLfloat>()); break;
case 202: if (c) c->setDepthStart(cs.getData<GLfloat>()); break;
case 206: if (c) c->mirror_x = cs.getData<bool>(); break;
case 207: if (c) c->mirror_y = cs.getData<bool>(); break;
case 208: if (c) c->setDistance(cs.getData<double>()); break;
case 209: if (c) c->roll_ = cs.getData<GLfloat>(); 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;
}