/***************************************************************************
* Copyright (C) 2005 by Raphael Langerhorst *
* raphael-langerhorst@gmx.at *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
***************************************************************************/
#include "GweController.h"
#include "GDataController.h"
#include <GElement.h>
#include <GObject.h>
#include <GForm.h>
#include <GEnergy.h>
#include <GVector3.h>
#include <GMatrix44.h>
#include <GEnergy.h>
#include <GElementID.h>
#include <GElementInfluence.h>
#include <qptrlist.h>
#include <qtimer.h>
using namespace GCS;
namespace GWE
{
//see GweController.h
class QPtrListGElement : public QPtrList<GCS::GElement>
{
public:
virtual ~QPtrListGElement() {};
};
GweController::GweController(GDataController* data, QObject *parent, const char *name)
: QObject(parent,name),
Data(data)
{
}
GweController::~GweController()
{
}
GDataController* GweController::getDataController()
{
return this->Data;
}
const GDataController* GweController::getDataController() const
{
return this->Data;
}
void GweController::connectBasicElementSignals(const GCS::GElementID& id)
{
GElement* element = NULL;
element = Data->getOpenElement(id);
bool close_afterwards = false;
if (element == NULL)
{
element = Data->open(id);
close_afterwards = true;
}
if (element)
{
qDebug(QString("connecting signals and slots for element %1 !").arg(id.getID()));
connect(element,SIGNAL(childElementCreated(GCS::GElement*)),
Data,SLOT(add(GCS::GElement*)));
connect(element,SIGNAL(radiateInfluence(const GCS::GElementInfluence&)),
this,SLOT(radiateInfluence(const GCS::GElementInfluence& )));
connect(element,SIGNAL(sendInfluence(const GCS::GElementID&, const GCS::GElementInfluence& )),
this,SLOT(routeInfluence(const GCS::GElementID&, const GCS::GElementInfluence& )));
connect(element,SIGNAL(formChanged(const GCS::GForm& )),
this,SLOT(handleReparenting()));
connect(element,SIGNAL(energyChanged(const GCS::GEnergy& )),
this,SLOT(removeElementWithNoEnergyLeft(const GCS::GEnergy& )));
if (close_afterwards)
Data->close(id);
}
else
{
qWarning(QString("could not connect element %1 !").arg(id.getID()));
}
}
//@todo max_traverse_children and max_traverse_parents parameters not yet used!!
QPtrListGElement GweController::findInRange(GElement* source, unsigned max_traverse_children, unsigned max_traverse_parents)
{
QPtrListGElement list;
GDataController* data = this->getDataController();
Q_CHECK_PTR(source);
if (source==NULL)
{
qWarning("source is NULL!!!");
return list;
}
const GObject* source_o = source->getObject();
bool close_parent_afterwards = false;
const GElementID& parentID = source->getObject()->getParent();
GElement* parent = NULL;
parent = data->getOpenElement(parentID);
if (parent == NULL)
{
close_parent_afterwards = true;
parent = data->open(parentID);
}
Q_CHECK_PTR(parent);
//now, getParent()->children() to get a list of elements with the
//same parent as source
QValueList<GElementID> children;
if (parent)
{
//parent is affected as well
list.append(parent);
//if the element is it's own parent we do not need to go through
//it's children here, they will all be added later anyway
if (parentID != source->getElementID().getID())
{
children = parent->getObject()->getChildren();
bool has_form = source_o->hasForm() ? true : false;
if (has_form) //if the source element has a form, we need to check if they touch each other
{
const GForm* source_f = source_o->getForm();
const GVector3& position = source_f->Position;
const double range = source_f->getRadiusMax();
for (QValueListIterator<GElementID> childrenID = children.begin(); childrenID != children.end(); ++childrenID)
{
GElement* el = data->open(*childrenID);
Q_CHECK_PTR(el);
if (el)
{
const GObject* el_o= el->getObject();
if (el_o->hasForm())
{
const GForm* f = el_o->getForm();
if ((f->Position - position).length() < (range + f->getRadiusMax()))
list.append(el);
else
data->close(el_o->getID());
}
else
{
//if something has no form it is everywhere
list.append(el);
}
}
}
}
else //if the source element has no form, it reaches everything anyway
{
for (QValueListIterator<GElementID> childrenID = children.begin(); childrenID != children.end(); ++childrenID)
{
GElement* el = data->open(*childrenID);
Q_CHECK_PTR(el);
if (el)
{
list.append(el);
}
}
}
}
if (close_parent_afterwards)
data->close(parentID);
}
else
{
qDebug("element with ID " + QString::number(source->getElementID().getID()) + " has no parent!");
//the element at least influences itself
list.append(source);
}
//add own children - the element that radiates a certain influence DOES influence "itself" by this
children = source->getObject()->getChildren();
for (QValueListIterator<GElementID> childrenID = children.begin(); childrenID != children.end(); ++childrenID)
{
GElement* el = data->open(*childrenID);
Q_CHECK_PTR(el);
if (el)
{
list.append(el);
}
}
//this list contains:
// * the parent
// * all children of it's parent that touch the source's form (including itself)
// * all own children
// ALL elements are opened and should be closed when usage finished!
return list;
}
void GweController::executeOpenElement(const GCS::GElementID& id)
{
GCS::GElement* element = this->Data->getOpenElement(id);
if (element)
{
element->executeElement();
}
else
{
qWarning(QString("Couldn't execute element with ID %1 because it couldn't be found").arg(id.getID()));
}
}
void GweController::radiateInfluence(const GElementInfluence& influence)
{
GDataController* data = this->getDataController();
Q_CHECK_PTR(data);
GElement* source_element = NULL;
source_element = data->getOpenElement(influence.source());
if (source_element==NULL)
source_element = data->open(influence.source());
if (source_element==NULL)
{
qWarning("Received influence to radiate from a source that does not exist!");
return;
}
Q_CHECK_PTR(source_element);
// qDebug("got influence to radiate from " + QString::number(source_element->getElementID().getID()));
QPtrList<GCS::GElement> influenced_elements = this->findInRange(source_element);
//for now provide a very simple mechanism for influence distribution:
// energy amount of the influence is evenly distributed among its receivers
unsigned dest_count = influenced_elements.count();
if (dest_count > 0)
{
double energy_level_original = influence.Energy.level();
double energy_amount_original = influence.Energy.amount();
double energy_sigma_original = influence.Energy.sigma();
double energy_amount_single_dest = energy_amount_original/dest_count;
double energy_amount_left = 0;
GCS::GElementInfluence influence_partial(influence.source(),GEnergy(energy_level_original,energy_amount_single_dest,energy_sigma_original));
GElement* e = influenced_elements.first();
while(e)
{
// qDebug(" sending to " + QString::number(e->getElementID().getID()));
e->receiveInfluence(influence_partial);
data->close(e->getElementID());
// energy_amount_left += influence.Energy.amount();
e = influenced_elements.next();
}
//set the energy amount to what has been left by the receivers
//the sender should put this energy back to it's own energy
// influence.Energy.set(energy_level_original,energy_amount_left,energy_sigma_original);
}
else
{
qWarning("destination count for influence radiation is 0!");
}
data->close(source_element->getElementID());
}
void GweController::routeInfluence(const GElementID& destination, const GElementInfluence& influence)
{
// qDebug("got influence to route from " + QString::number(influence.source().getID()));
GDataController* data = this->getDataController();
Q_CHECK_PTR(data);
GElement* element = data->open(destination);
Q_CHECK_PTR(element);
if (element)
{
element->receiveInfluence(influence);
data->close(element->getElementID());
}
else
{
element = data->open(influence.source());
if (element==NULL)
{
Q_CHECK_PTR(element);
qDebug("got influence from %lu, but this element is not stored?!",influence.source().getID());
}
else
{
// qDebug(" sending to " + QString::number(element->getElementID().getID()));
element->receiveInfluence(influence);
}
data->close(element->getElementID());
}
}
void GweController::handleReparenting()
{
// return; //not implemented yet
const QObject* sender_generic = sender();
Q_ASSERT(sender_generic->inherits("GCS::GElement")); //sender MUST be a GElement;
if (!sender_generic->inherits("GCS::GElement"))
return;
const GCS::GElement* const_element = (const GCS::GElement*)sender_generic;
GCS::GElement* element = this->Data->getOpenElement(const_element->getElementID()); //element MUST be open
Q_CHECK_PTR(element->getObject());
if (element->getObject() && element->getObject()->hasForm())
{
const GCS::GForm* f = element->getObject()->getForm();
const GCS::GElementID& id = element->getElementID();
const GCS::GElementID& old_parent = element->getObject()->getParent();
if (old_parent.getID() == 0)
return;
if (old_parent == id)
return; //we're our own parent, probably the universe element
// if (old_parent.getID() == 0)
// {
// const GCS::GElementID& new_parent = const_old_parent_element->getObject()->getParent();
// if (new_parent == old_parent)
// {
// return;
// }
// qWarning("handling reparenting: old parent had ID 0, not notifying old parent");
// new_parent_element->addChildElement(id);
// GCS::GElement* new_parent_element = this->Data->open(new_parent);
// GCS::GMatrix44 m = GCS::GMatrix44::createScaleMatrix(f_old_parent->Ellipsoid);
// m.multiply(GCS::GMatrix44::createRotationAroundX(f_old_parent->Rotation.x));
// m.multiply(GCS::GMatrix44::createRotationAroundY(f_old_parent->Rotation.y));
// m.multiply(GCS::GMatrix44::createRotationAroundZ(f_old_parent->Rotation.z));
// m.multiply(GCS::GMatrix44::createTranslationMatrix(f_old_parent->Position));
// qDebug(QString("reparenting element %1 from old parent %2 to new parent %3").arg(QString::number(id.getID()),QString::number(old_parent.getID()),QString::number(new_parent.getID())));
// element->reparent(old_parent,new_parent,m);
// this->Data.close(new_parent);
// return;
// }
const GCS::GElement* const_old_parent_element = this->Data->read(old_parent);
Q_CHECK_PTR(const_old_parent_element);
if (!const_old_parent_element)
return;
if (!const_old_parent_element->getObject()->hasForm())
return; //@todo: if parent has no form, traverse up until a parent with form is found
const GCS::GForm* f_old_parent = const_old_parent_element->getObject()->getForm();
double old_parent_radius_min = f_old_parent->getRadiusMin();
double farthest_distance_current = f->Position.length() + f->getRadiusMax();
// qDebug(QString("reparenting, old parent radius min: %1, this max distance: %2").arg(QString::number(old_parent_radius_min),QString::number(farthest_distance_current)));
if (f_old_parent->getRadiusMin() < f->Position.length() + f->getRadiusMax())
{
const GCS::GElementID& new_parent = const_old_parent_element->getObject()->getParent();
if (new_parent != old_parent)
{
GCS::GMatrix44 m = GCS::GMatrix44::createScaleMatrix(f_old_parent->Ellipsoid);
m.multiply(GCS::GMatrix44::createRotationAroundX(f_old_parent->Rotation.x));
m.multiply(GCS::GMatrix44::createRotationAroundY(f_old_parent->Rotation.y));
m.multiply(GCS::GMatrix44::createRotationAroundZ(f_old_parent->Rotation.z));
m.multiply(GCS::GMatrix44::createTranslationMatrix(f_old_parent->Position));
qDebug(QString("reparenting element %1 from old parent %2 to new parent %3").arg(QString::number(id.getID()),QString::number(old_parent.getID()),QString::number(new_parent.getID())));
element->reparent(old_parent,new_parent,m);
}
}
//@todo IMPORTANT: check if element enters a sibling!
}
}
void GweController::removeElementWithNoEnergyLeft(const GCS::GEnergy& changedEnergy)
{
if (changedEnergy.amount()>0)
return;
const QObject* sender_generic = sender();
Q_ASSERT(sender_generic->inherits("GCS::GElement")); //sender MUST be a GElement;
if (!sender_generic->inherits("GCS::GElement"))
return;
const GCS::GElement* const_element = (const GCS::GElement*)sender_generic;
Q_CHECK_PTR(const_element->getObject());
qDebug(QString("removing element %1 because energy amount is 0").arg(const_element->getElementID().getID()));
Data->postDelete(const_element->getElementID());
}
void GweController::shutdown()
{
qWarning("Shutting down world engine");
this->Data->shutdown();
QTimer::singleShot(2000,this,SIGNAL(quit()));
}
}
syntax highlighted by Code2HTML, v. 0.9.1