/***************************************************************************
* Copyright (C) 2004 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 "GDynamicGeneratorAgent.h"
#include "GSolarsystemCategory.h"
#include "GPlanetCategory.h"
#include <GObject.h>
#include <GForm.h>
#include <GVector3.h>
#include <GWorldData.h>
#include <qdom.h>
#include <exception>
#include <cmath>
using namespace GCS;
using namespace GBE::Util;
namespace GBE {
// GDynamicGeneratorOctreeNode CLASS DECLARATION
/**
* \class GDynamicGeneratorOctreeNode GDynamicGeneratorAgent.cpp
* \brief The generation space is structured with octrees
* @author Raphael Langerhorst
* Basically a node serves three purposes:
* 1) It serves as starting point for generating all child nodes in the nodes area,
* 2) it stores the seed value for the pseudo RNG to generate the child nodes and
* 3) it stores the size of the area in which a child element would potentially be created.
*/
class GDynamicGeneratorOctreeNode
{
protected:
/**
* Each octreee node directly has access to its data in the
* element data document. This makes fast updates easily possible.
* In particular the element data can be kept in sync with the
* octree with low overhead.
*/
QDomElement Data;
/**
* The RNG seed number for this node.
* THIS is the actual data we need for child generation
* at the node's position.
*/
unsigned long RandomSeed;
/**
* The position relative to the element centre.
* @todo it should somehow be possible to respond to radius changes of the element's form.
*/
GCS::GVector3 Position;
/**
* Segment size of the enclosing cube.
* This value is both required to generate positions and sizes of
* child nodes as well as to be able to "randomly" place the
* created child element somewhere in this cube. If this second
* functionality were not given, a randomly created element content
* (=children of one specific element) could look like a grid.
* @see Position
*/
double SegmentSize;
/**
* Set to true when this node is an end node and the element for this
* node has been created.
* If this node is not an end node but all child nodes have been used
* for creating elements then this is also set to true.
*
* This means that if Generated is set to true then the whole cubic
* area this node covers had been generated. Thus it is possible to
* reduce nodes and save memory. In the end, when the whole element
* was created, there will be only one node left: the top node,
* and its Generated attribute will be set to true.
*/
bool Generated;
/**
* Array of child nodes. Seen FROM positive y:
* the first four are the first four (mathematical) quadrants
* and the second four children are the last four quadrants.
*/
GDynamicGeneratorOctreeNode* Children[8];
public:
/**
* Constructor.
*/
GDynamicGeneratorOctreeNode(QDomElement data, unsigned long rand_seed, const GCS::GVector3& position, double segment_size);
/**
* Destructor, deletes all child nodes.
*/
~GDynamicGeneratorOctreeNode();
/**
* Reads the random seed from the element data.
* The random seed is stored in the rs attribute.
* Additionally all child nodes are initialized
* from element data as well.
*
* This is the usual procedure when reinitializing
* an element that is opend - or woken - from the
* persistent storage.
*/
void initFromData();
/**
* @Returns the seed for the RNG for this node.
*/
unsigned long getRandomSeed();
/**
* @Returns the position of this node.
*/
GCS::GVector3 getPosition();
/**
* @Returns the segment size of the enclosing node cube.
*/
double getSegmentSize();
/**
* @Returns true when given position is inside the cube of this octree node.
*/
bool isPositionInsideNode(const GCS::GVector3& position);
/**
* @Returns true when given area is enclosed by the cube of this octree node.
*/
bool isAreaInsideNode(const GVector3& position, double radius);
/**
* @Returns true when given area touches the cube of this octree node.
*/
bool touchesArea(const GCS::GVector3& position, double radius);
/**
* Sets Generated to true.
* @see Generated
*/
void setGenerated();
/**
* @Returns true when an element was already created at this node position.
* @see Generated
*/
bool isGenerated();
/**
* @Returns a pointer list with all child nodes.
*/
QPtrList<GDynamicGeneratorOctreeNode> getChildNodes();
/**
* @returns true when child nodes are created (so they are in memory).
*/
bool isNodeExpanded();
/**
* Creates all 8 child nodes for this octree node.
*
* @param rng specifies the random number generator to use.
* @return true when all child nodes have been successfully created.
* @return false on out of memory errors.
*/
bool expandNode(Util::PseudoRNG& rng);
/**
* Deletes all child nodes.
*/
void reduceNode();
/**
* Reduces all child nodes that have been used for element creation (isGenerated()==true)
*/
void reduceGenerated(bool recursive = true);
/**
* @Returns the child node of this octree node that covers given position.
* @Returns NULL if position is completely outside of this node!
* @todo write a test case for this.
*/
GDynamicGeneratorOctreeNode* getChildNodeForPosition(const GCS::GVector3& position);
/**
* @Returns all child nodes that cover part of given area;
* @todo write a test case for this.
*/
QPtrList<GDynamicGeneratorOctreeNode> getChildNodesForArea(const GCS::GVector3& position, double radius);
/**
* @returns a deterministic random position based on RandomSeed inside this node's cube.
*/
GCS::GVector3 getRandomPositionInCube(Util::PseudoRNG& rng);
};
// GDynamicGeneratorOctreeNode IMPLEMENTATION
GDynamicGeneratorOctreeNode::GDynamicGeneratorOctreeNode(QDomElement data, unsigned long rand_seed, const GCS::GVector3& position, double segment_size)
: Data(data),
RandomSeed(rand_seed),
Position(position),
SegmentSize(segment_size),
Generated(false)
{
Children[0]=NULL;
Children[1]=NULL;
Children[2]=NULL;
Children[3]=NULL;
Children[4]=NULL;
Children[5]=NULL;
Children[6]=NULL;
Children[7]=NULL;
if (data.attribute("done","0")=="1")
{
setGenerated();
}
}
GDynamicGeneratorOctreeNode::~GDynamicGeneratorOctreeNode()
{
if (this->isNodeExpanded())
this->reduceNode();
}
unsigned long GDynamicGeneratorOctreeNode::getRandomSeed()
{
return RandomSeed;
}
GVector3 GDynamicGeneratorOctreeNode::getPosition()
{
return Position;
}
double GDynamicGeneratorOctreeNode::getSegmentSize()
{
return SegmentSize;
}
bool GDynamicGeneratorOctreeNode::isPositionInsideNode(const GVector3& position)
{
double half_seg_size = this->SegmentSize*0.5;
if (position.x >= this->Position.x - half_seg_size && position.x <= this->Position.x + half_seg_size &&
position.y >= this->Position.y - half_seg_size && position.y <= this->Position.y + half_seg_size &&
position.z >= this->Position.z - half_seg_size && position.z <= this->Position.z + half_seg_size)
return true;
else
return false;
}
bool GDynamicGeneratorOctreeNode::isAreaInsideNode(const GVector3& position, double radius)
{
double half_seg_size = this->SegmentSize*0.5;
if (position.x + radius >= this->Position.x - half_seg_size && position.x - radius <= this->Position.x + half_seg_size &&
position.y + radius >= this->Position.y - half_seg_size && position.y - radius <= this->Position.y + half_seg_size &&
position.z + radius >= this->Position.z - half_seg_size && position.z - radius <= this->Position.z + half_seg_size)
return true;
else
return false;
}
bool GDynamicGeneratorOctreeNode::touchesArea(const GVector3& position, double radius)
{
if (this->isPositionInsideNode(position))
return true;
//@todo this testing testes spheres!!! but a node is a cube!
if (this->Position.distanceTo(position) < radius + this->SegmentSize*0.8)
return true;
return false;
//
//
// if (position.x + radius >= this->Position.x - half_seg_size && position.x - radius <= this->Position.x + half_seg_size ||
// position.y + radius >= this->Position.y - half_seg_size && position.y - radius <= this->Position.y + half_seg_size ||
// position.z + radius >= this->Position.z - half_seg_size && position.z - radius <= this->Position.z + half_seg_size)
// return true;
//
// else
// return false;
}
void GDynamicGeneratorOctreeNode::setGenerated()
{
Data.setAttribute("done","1");
this->Generated = true;
}
bool GDynamicGeneratorOctreeNode::isGenerated()
{
return Generated;
}
QPtrList<GDynamicGeneratorOctreeNode> GDynamicGeneratorOctreeNode::getChildNodes()
{
QPtrList<GDynamicGeneratorOctreeNode> list;
for (short i = 0; i< 8; i++)
{
list.append(Children[i]);
}
return list;
}
bool GDynamicGeneratorOctreeNode::isNodeExpanded()
{
// if (Children[0]==NULL)
// return false;
// else
// return true;
return Children[0] ? true : false;
}
bool GDynamicGeneratorOctreeNode::expandNode(PseudoRNG& rng)
{
if (isGenerated())
{
qWarning("Node is expanding ALTOUGH it is already generated! This is madness! - Please contact the developers!");
}
if (!isNodeExpanded())
{
//@todo 1) set RNG, 2) create 1 through 8 with correct positions and segment sizes
rng.setNumber(this->RandomSeed);
double child_segment_size = this->SegmentSize*0.5;
GVector3 child_pos_array[8];
GVector3 child_pos(this->Position.x + child_segment_size*0.5,
this->Position.y + child_segment_size*0.5,
this->Position.z + child_segment_size*0.5);
// BEGIN TOPLEVEL NODES
child_pos_array[0] = child_pos;
child_pos.x -= child_segment_size;
child_pos_array[1] = child_pos;
child_pos.z -= child_segment_size;
child_pos_array[2] = child_pos;
child_pos.x += child_segment_size;
child_pos_array[3] = child_pos;
// END TOPLEVEL NODES
// BEGIN BOTTOMLEVEL NODES
child_pos.y -= child_segment_size;
child_pos.z += child_segment_size;
child_pos_array[4] = child_pos;
child_pos.x -= child_segment_size;
child_pos_array[5] = child_pos;
child_pos.z -= child_segment_size;
child_pos_array[6] = child_pos;
child_pos.x += child_segment_size;
child_pos_array[7] = child_pos;
// END BOTTOMLEVEL NODES
QDomElement gs[8];
if (Data.hasChildNodes())
{
QDomNodeList list = Data.childNodes();
if (list.count()==8)
{
int i;
for (i = 0; i<8; i++)
{
QDomElement e = list.item(i).toElement();
Q_ASSERT(!gs[i].isNull());
bool ok;
int n = e.attribute("n",QString::number(i)).toInt(&ok);
if (!ok)
n = i;
gs[n] = e;
}
//let's do a sanity check
bool consistent = true;
for (i = 0; i<8; i++)
{
if (gs[i].isNull())
{
qWarning("INCONSISTENCY DETECTED: found existing octree node data, but not all child elements could be found! Replacing existing node data.");
consistent = false;
}
}
if (!consistent)
{
for (i = 0; i<8; i++)
{
if (!gs[i].isNull())
{
gs[i].clear();
}
}
}
}
else
{
qWarning(QString("INCONSISTENCY DETECTED: octree nodes for world generation must either have 0 or 8 child nodes! We have %1. Replacing existing nodes").arg(list.count()));
while(Data.hasChildNodes())
{
qWarning("clearing node");
Data.firstChild().clear();
}
}
}
if (!Data.hasChildNodes())
{
//if there is nothing we create the child nodes now.
for (int i = 0; i<8; i++)
{
QDomElement e = Data.ownerDocument().createElement("gs");
Data.appendChild(e);
e.setAttribute("n",QString::number(i));
int done = this->isGenerated() ? 1 : 0;
e.setAttribute("done",QString::number(done)); // in case we're expanding although we are already generated...
gs[i] = e;
}
}
for (short child_number = 0; child_number < 8; child_number++)
{
unsigned long child_random_seed = rng.getNumberInt();
Children[child_number] = new GDynamicGeneratorOctreeNode(gs[child_number],child_random_seed,child_pos_array[child_number],child_segment_size);
if (Children[child_number]==NULL)
{
for (short i=0; i<child_number; i++)
{
delete Children[i];
Children[i]=NULL;
gs[i].clear();
}
qWarning("Octree child node creation: out of memory");
return false;
}
}
}
return true; //if nodes either already exist or have been successfully created.
}
void GDynamicGeneratorOctreeNode::reduceNode()
{
//We use Children[0] to test whether expanding had occured or not.
if (isNodeExpanded())
{
for (short i = 0; i<8; i++)
{
delete Children[i];
Children[i]=NULL;
}
Data.clear();
}
}
void GDynamicGeneratorOctreeNode::reduceGenerated(bool recursive)
{
if (isNodeExpanded())
{
bool all_childs_generated = true;
if (recursive)
{
for (short i = 0; i<8; i++)
{
Children[i]->reduceGenerated(recursive);
}
}
for (short i = 0; i<8; i++)
{
if (Children[i]->isGenerated() == false)
{
all_childs_generated = false;
}
}
if (all_childs_generated)
{
this->reduceNode();
this->setGenerated();
}
}
}
GDynamicGeneratorOctreeNode* GDynamicGeneratorOctreeNode::getChildNodeForPosition(const GVector3& position)
{
if (!this->isNodeExpanded())
{
qWarning("Node not expanded, can't return children");
return NULL;
}
if (this->isPositionInsideNode(position))
{
for (short i = 0; i<8; i++)
{
if (Children[i]->isPositionInsideNode(position))
return Children[i];
}
qWarning("Position was inside node but no appropriate child node found!");
return NULL;
}
else
return NULL;
}
QPtrList<GDynamicGeneratorOctreeNode> GDynamicGeneratorOctreeNode::getChildNodesForArea(const GVector3& position, double radius)
{
QPtrList<GDynamicGeneratorOctreeNode> list;
if (!this->isNodeExpanded())
{
qWarning("Node is not expanded, can't get children for area!!");
return list;
}
// qDebug("Getting child nodes in area");
for (short i = 0; i<8; i++)
{
if (Children[i]->touchesArea(position,radius))
{
// qDebug("Found child in area");
list.append(Children[i]);
}
}
return list;
}
GVector3 GDynamicGeneratorOctreeNode::getRandomPositionInCube(PseudoRNG& rng)
{
rng.setNumber(this->RandomSeed+10); //pick some other seed value than for child nodes.
GVector3 pos(this->Position.x + (rng.getNumberDouble()-0.5)*this->SegmentSize,
this->Position.y + (rng.getNumberDouble()-0.5)*this->SegmentSize,
this->Position.z + (rng.getNumberDouble()-0.5)*this->SegmentSize);
return pos;
}
// GDynamicGeneratorAgent IMPLEMENTATION
GDynamicGeneratorAgent::GDynamicGeneratorAgent()
: EnergyFractionChildCreation(0),
EnergyFractionDirtyFlag(true),
DataLoaded(FALSE),
RandomSeed(1000),
Categories(),
CreationTime(QDateTime::currentDateTime()),
Density(5),
CreateAllAtOnce(false),
TopNode(NULL)
{
this->Categories.setAutoDelete(true);
}
GDynamicGeneratorAgent::~GDynamicGeneratorAgent()
{
if (TopNode)
{
delete TopNode; //deletes all child nodes recursively.
TopNode=NULL;
}
}
void GDynamicGeneratorAgent::generateInArea(const GCS::GVector3& position, double radius)
{
//implement recursive search until smallest node is found that still encompasses the whole area - then generate this node (might STILL hold childnodes that do the actual generation!!)
// qDebug("Generating child elements in area");
GForm* f = requestForm();
if (f->getRadiusMax() < position.length() - radius)
{
// qDebug("Area completely outside of this element");
return; //if target area is completely outside of this element's form
}
//generate top node if it does not exist
if (TopNode == NULL)
{
// qDebug("TopNode did not exist, generating top node...");
RNG.setNumber(this->RandomSeed);
unsigned long rs = RNG.getNumberInt();
bool ok;
QDomElement data = xmlGetElement("/worldgeneration/gs",ok);
Q_ASSERT(!data.isNull());
if (!data.hasAttribute("n"))
data.setAttribute("n","0");
if (!data.hasAttribute("done"))
data.setAttribute("done","0");
if (data.hasAttribute("rs"))
{
bool ok;
unsigned long rs_ = data.attribute("rs",QString::number(rs)).toULong(&ok);
if (ok)
rs = rs_;
}
else
{
data.setAttribute("rs",QString::number(rs));
}
TopNode = new GDynamicGeneratorOctreeNode(data,rs,GVector3(0,0,0),f->getRadiusMax()*2);
if (TopNode==NULL)
{
qWarning("not enough memory to generate top node");
return;
}
TopNode->expandNode(RNG);
}
if (TopNode->isGenerated())
{
// qDebug("TopNode is generated, thus the whole element is already generated");
return; //Whole element is generated.
}
//here we store the current depth of the tree, when depth == density()
//then we are at the lowest level - here actual element generation happens
short unsigned depth = 0;
//start recursive search until the smallest node is found that still covers the whole given area
recursiveGeneration(TopNode,position,radius,depth);
}
void GDynamicGeneratorAgent::recursiveGeneration(GDynamicGeneratorOctreeNode* node, const GCS::GVector3& position, double radius, short unsigned depth)
{
// qDebug("Recursive generation, depth = " + QString::number(depth));
if (node->isGenerated()) //if node is already generated there is nothing to do, note that we currently double check this
{
// qDebug("Node is generated, returning");
return;
}
if (depth>=this->Density)
{
//@todo determine category - if a category is determined (and not empty space) then we generate with that category at this node
//mark node as already being used to generate elements
node->setGenerated();
RNG.setNumber(node->getRandomSeed());
double random_number = RNG.getNumberDouble();
// qDebug("Double number generated: " + QString::number(random_number));
double range_sum = 0;
for (GDynamicGeneratorCategory* category = Categories.first(); category != NULL; category = Categories.next())
{
range_sum += category->getRange();
if (range_sum > 1)
qWarning("Range sum bigger than 1: " + QString::number(range_sum));
if (range_sum >= random_number) //category HIT, we generate a child element!!
{
// qDebug("Category found, creating element");
GVector3 position = node->getRandomPositionInCube(RNG);
GElement* element = this->createElement(*category,position);
Q_CHECK_PTR(element);
if (element)
{
emit this->childElementCreated(element);
element->executeElement(this->CreationTime.secsTo(QDateTime::currentDateTime()));
}
return;
}
}
//if we get here then no generation happened
//that means no category applied as the sum of category ranges
//did not exceed the generated random number.
}
else
{
if (!node->isNodeExpanded())
{
node->expandNode(RNG);
}
if (node->isNodeExpanded()) //we check again in case node can't be expanded (memory!!)
{
unsigned short new_depth = depth+1;
QPtrList<GDynamicGeneratorOctreeNode> nodes = node->getChildNodesForArea(position, radius);
for (GDynamicGeneratorOctreeNode* current = nodes.first(); current != NULL; current = current = nodes.next())
{
if (!current->isGenerated())
{
recursiveGeneration(current,position,radius,new_depth);
//reduce generated nodes, saves lots of memory.
//this means that if a whole (higher level) node had been
//completely generated then all child nodes can be removed and this higher node can be set to generated
//does not need to be recursive, as we call it at each level anyway...
current->reduceGenerated(false);
}
}
}
}
}
GElement* GDynamicGeneratorAgent::createElement(const GDynamicGeneratorCategory& category, const GCS::GVector3& position)
{
//hmm.... how to get deterministic energy? I think THIS will be _different_ for each generation...
// at least if the element is not generated in "all at once" mode.
//@todo use a member variable for storing the fraction of energy we take, this takes too much cpu time
if (this->EnergyFractionDirtyFlag)
{
double range_sum = 0;
for (GDynamicGeneratorCategory* category = Categories.first(); category != NULL; category = Categories.next())
{
range_sum += category->getRange();
}
if (range_sum <= 0)
{
qWarning("Generating child elements will not work: range sum is 0 or less");
this->EnergyFractionChildCreation=0;
}
else
{
this->EnergyFractionChildCreation = 1/(std::pow((double)8,(double)this->Density)*range_sum);
}
this->EnergyFractionDirtyFlag=false;
}
GEnergy* e = new GEnergy(requestEnergy()->take(this->EnergyFractionChildCreation));
double level_shift = (e->sigma()*(RNG.getNumberDouble()-0.5));
double level_shift_positive = level_shift;
if (level_shift < 0)
level_shift_positive *= -1;
e->set(e->level()+level_shift,e->amount(),e->sigma()-level_shift_positive);
//@todo this energy change *should* be reflected in the original energy,
// but if it's used for all child elements it probably doesn't matter much
Q_CHECK_PTR(e);
if (e==NULL)
return NULL;
//@todo check if there is already a child element that covers this area if chosen category does not allow overlapping
//@todo how to set parent ID for created element? Additional parameter?
GForm* form = category.createForm(*e,position,RNG);
Q_CHECK_PTR(form);
if (form==NULL)
{
delete e;
return NULL;
}
GObject* object = new GObject(e,form,getElementID(),GElementID::getFreeID(),GElementID(0),new QDomDocument(),requestObject()->getWorldData());
Q_CHECK_PTR(object);
GElement* element = new GElement(object);
Q_CHECK_PTR(element);
category.postProcess(element,RNG); // DO NOT(!!!) execute agents in postprocessing
return element;
}
void GDynamicGeneratorAgent::loadData(bool force)
{
if (force)
this->DataLoaded = false;
if (this->DataLoaded)
return;
bool ok;
unsigned long rs = xmlGetULongInteger("/worldgeneration/randomseed",ok);
if (ok)
this->RandomSeed = rs;
else
{
qWarning("Random Seed was not found!");
initRandomSeed(RandomSeed);
}
QDateTime time;
time.fromString(xmlGetElement("/worldgeneration/creationtime",ok).text());
if (ok)
this->CreationTime = time;
else
{
qWarning("Creation Time was not found!");
initCreationTime(CreationTime);
}
int density = xmlGetInteger("/worldgeneration/density",ok);
if (density < 0)
{
qWarning("Density < 0; setting density to 3");
density = 3;
ok=false;
}
if (ok)
this->Density = density;
else
{
qWarning("Density was not found!");
initDensity(Density);
}
int all_at_once = xmlGetInteger("/worldgeneration/allatonce",ok);
if (ok)
{
if (all_at_once == 1)
this->CreateAllAtOnce = true;
else
this->CreateAllAtOnce = false;
}
else
{
qWarning("CreateAllAtOnce was not found!");
initAllAtOnce(this->CreateAllAtOnce);
}
bool existed;
QDomElement categories_element = xmlGetElement("/worldgeneration/categories",existed);
if (!categories_element.hasChildNodes())
{
qWarning("No categories found in element data, this agent is quite useless without these.");
if (!this->Categories.isEmpty())
{
}
}
else
{
this->Categories.clear(); //autodelete = true
QDomNodeList categories = categories_element.childNodes();
for (int i=0; i<categories.count(); i++)
{
QDomElement c = categories.item(i).toElement();
if (c.isNull())
{
qWarning("Category is null element!");
}
else
{
QString name = c.tagName();
GDynamicGeneratorCategory* c_object = NULL;
if (name == "solarsystem")
{
bool existed,ok;
double range = xmlGetElement(c,"range",existed).text().toDouble(&ok);
if (existed && ok)
{
c_object = new GSolarsystemCategory(range);
}
else if (existed && !ok)
{
qWarning(QString("Could not convert number: %1").arg(xmlGetElement(c,"range",existed).text()));
}
else
{
qWarning("Range element not found for solarsystem category!");
}
}
else if (name == "planet")
{
bool existed;
bool ok;
double range = xmlGetElement(c,"range",existed).text().toDouble(&ok);
if (existed && ok)
{
c_object = new GPlanetCategory(range);
}
else if (existed && !ok)
{
qWarning(QString("Could not convert number: %1").arg(xmlGetElement(c,"range",existed).text()));
}
else
{
qWarning("Range element not found for planet category!");
}
}
else
{
qWarning(QString("Unknown category name: %1").arg(name));
}
if (c_object)
{
this->Categories.append(c_object);
}
}
}
}
this->DataLoaded = true;
}
void GDynamicGeneratorAgent::receiveInfluence(const GCS::GElementInfluence& influence)
{
if (this->requestObject()->hasForm())
{
if (influence.source() == this->requestObject()->getParent() || influence.source() == this->getElementID())
{
// qDebug("Recieved influence from myself or parent, not creating content");
return; //don't react to parent influence or own influence, since that would automatically create ALL the content
}
// qDebug("Generating element content");
if (!this->DataLoaded)
loadData(); //make sure we've all data initialized
//the criteria on which we create child elements is based on whether the
//influence comes from an external source (= a sibling and neither the parent,
//nor any of the existing children)
const GElement* sender = this->requestObject()->getWorldData()->read(influence.source());
Q_CHECK_PTR(sender);
if (sender==NULL)
return;
const GObject* sender_object = sender->getObject();
Q_CHECK_PTR(sender_object);
if (!sender_object->hasForm())
return;
const GForm* sender_form = sender_object->getForm();
Q_CHECK_PTR(sender_form);
//@todo continue from here
// GActiveElementInfluence& active_element_influence = dynamic_cast<GActiveElementInfluence&>(influence);
// qDebug("Received element influence, checking if additional elements should be created.");
GVector3 pos = sender_form->Position;
GVector3 rot = sender_form->Rotation;
//@todo add a factor to element data
double arearadius = sender_form->getRadiusMax();
double detail_arearadius = sender_form->getRadiusMin();
// Generating contents for active elements INSIDE this element. This would
// mean for example to see the planets within a solar system when the
// source element is inside the solar system itself.
// Note that for this generation the normal area size is used as
// the source element moves inside this element.
if (sender_object->getParent() == this->getElementID())
{
// qDebug("Influence source is a child element");
//no transformation needed
generateInArea(pos,arearadius);
}
// Generating contents for active elements OUTSIDE this element. This would
// mean for example to see the planets within a solar system even when being
// far away from the solar system itself.
// Note that for this generation the detail area radius is used as the source
// element is not really inside this element. But it can be useful to see
// element content if the element is getting nearer.
else if (sender_object->getParent() == this->requestObject()->getParent() /* && active_element_influence.parent() != this->getElementID()*/ )
{
// qDebug("Influence source on the same hierarchy level");
GForm* f = requestForm();
//transformation is just influence source position - this position, and rotation!!
pos.sub(f->Position);
rot.sub(f->Rotation);
pos.turnAroundAxis(GVector3(1,0,0),rot.x);
pos.turnAroundAxis(GVector3(0,1,0),rot.y);
pos.turnAroundAxis(GVector3(0,0,1),rot.x);
generateInArea(pos,detail_arearadius);
}
}
}
//BEGIN XML Element Data initialization
/*
* <worldgeneration>
* <randomseed>number</randomseed>
* <creationtime>datetime</creationtime>
* <density>double value</density>
* <allatonce>bool</allatonce>
* <categories>
* <!-- sequence of categories, these are examples -->
* <solarsystem>
* <range>double value</range>
* <radiusmin>double value</radiusmin>
* <radiusmax>double value</radiusmax>
* <allowoverlap>bool</allowoverlap>
* </solarsystem>
* <planet>
* <range>double value</range>
* <radiusmin>double value</radiusmin>
* <radiusmax>double value</radiusmax>
* <allowoverlap>bool</allowoverlap>
* </planet>
* </categories>
* </worldgeneration>
*/
void GDynamicGeneratorAgent::initRandomSeed(unsigned long random_seed)
{
this->RandomSeed = random_seed;
bool existed;
xmlSetULongInteger("/worldgeneration/randomseed",random_seed,existed);
}
void GDynamicGeneratorAgent::initCreationTime(QDateTime creation_time)
{
this->CreationTime = creation_time;
bool existed;
QDomElement e = xmlGetElement("/worldgeneration/creationtime",existed);
if (e.isNull())
{
qWarning("Can't set creation time because element is NULL!");
return;
}
xmlRemoveNodeContent(e);
e.appendChild(e.ownerDocument().createTextNode(creation_time.toString(Qt::ISODate)));
}
void GDynamicGeneratorAgent::initDensity(unsigned short density)
{
this->Density = density;
this->EnergyFractionDirtyFlag = true;
bool existed;
xmlSetInteger("/worldgeneration/density",density,existed);
}
void GDynamicGeneratorAgent::initAllAtOnce(bool create_all_at_once)
{
this->CreateAllAtOnce = create_all_at_once;
bool existed;
int n = create_all_at_once==true ? 1 : 0;
// qDebug(QString("all at once creation is set to %1").arg(n));
xmlSetInteger("/worldgeneration/allatonce",n,existed);
}
//@todo this is currently a bit... well, we should think of some kind of factory or plugin system for categories
void GDynamicGeneratorAgent::initCategory(GDynamicGeneratorCategory* category)
{
this->Categories.append(category);
this->EnergyFractionDirtyFlag = true; //recalculate energy fraction for child creation
QString classname = "category";
try
{
GSolarsystemCategory* c = dynamic_cast<GSolarsystemCategory*>(category);
classname = "solarsystem";
}
catch(std::exception e)
{
try
{
GPlanetCategory* c = dynamic_cast<GPlanetCategory*>(category);
classname = "planet";
}
catch(std::exception e)
{
}
}
if (classname=="category")
{
qWarning("unknown category, can't add it to element data");
return;
}
bool existed;
xmlSetDouble(QString("/worldgeneration/") + classname + "/range",category->getRange(),existed);
//radiusmin, radiusmax, allowoverlap are not used for now... solar systems and planets both don't need them
}
//END XML Element Data initialization
};
syntax highlighted by Code2HTML, v. 0.9.1