mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-12 05:21:29 +00:00
363 lines
8.7 KiB
C++
363 lines
8.7 KiB
C++
/*
|
|
|
|
Father Nitwit's Fear Pathing File Maker Thing
|
|
Copyright (C) 2005 Father Nitwit (eqemu@8ass.com)
|
|
|
|
I'll release thisunder the GPL, even though I hate the GPL.
|
|
|
|
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; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
|
are required to give you total support for your newly bought product;
|
|
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, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
|
|
This is a modified quadtree that stores a list of all
|
|
nodes reachable within a defined distance from the bounds
|
|
of that quadtree node. Therefor this distance should be
|
|
the maximum distance used to search for a node.
|
|
|
|
In case this distance is not big enough, we also store a
|
|
complete list of reachable nodes at each quadtree level, so
|
|
the root has all nodes for sure. This makes it possible to
|
|
garuntee that we can find A node which is closest.
|
|
|
|
*/
|
|
|
|
#include "quadtree.h"
|
|
#include "apathing.h"
|
|
#include "../zone/map.h"
|
|
|
|
//#define SPLIT_DEBUG
|
|
|
|
QTNode::QTNode(Map *_map, float dist2, float Tminx, float Tmaxx, float Tminy, float Tmaxy) {
|
|
node1 = NULL;
|
|
node2 = NULL;
|
|
node3 = NULL;
|
|
node4 = NULL;
|
|
minx = Tminx;
|
|
maxx = Tmaxx;
|
|
miny = Tminy;
|
|
maxy = Tmaxy;
|
|
map = _map;
|
|
search_dist2 = dist2; //this is a distance-squared
|
|
final = false;
|
|
buildVertexes();
|
|
|
|
}
|
|
|
|
QTNode::~QTNode() {
|
|
clearNodes();
|
|
}
|
|
|
|
void QTNode::clearNodes() {
|
|
if(node1 != NULL)
|
|
delete node1;
|
|
if(node2 != NULL)
|
|
delete node2;
|
|
if(node3 != NULL)
|
|
delete node3;
|
|
if(node4 != NULL)
|
|
delete node4;
|
|
node1 = NULL;
|
|
node2 = NULL;
|
|
node3 = NULL;
|
|
node4 = NULL;
|
|
}
|
|
|
|
|
|
//assumes that both supplied arrays are big enough per countQTNodes/Facelists
|
|
void QTNode::fillBlocks(PathTree_Struct *heads, PathPointRef *flist, unsigned long &hindex, unsigned long &findex) {
|
|
PathTree_Struct *head = &heads[hindex];
|
|
hindex++;
|
|
|
|
head->minx = minx;
|
|
head->maxx = maxx;
|
|
head->miny = miny;
|
|
head->maxy = maxy;
|
|
head->flags = 0;
|
|
|
|
//rearranged to give all QT nodes a node list
|
|
head->nodelist.count = nodes.size();
|
|
head->nodelist.offset = findex;
|
|
list<PathNode *>::iterator curs,end;
|
|
curs = nodes.begin();
|
|
end = nodes.end();
|
|
for(; curs != end; curs++) {
|
|
PathNode *cur = *curs;
|
|
flist[findex] = cur->node_id;
|
|
findex++;
|
|
}
|
|
// findex += head->nodes.count;
|
|
|
|
|
|
if(final) {
|
|
head->flags |= pathNodeFinal;
|
|
} else {
|
|
head->flags = 0;
|
|
//branch node.
|
|
|
|
if(node1 != NULL) {
|
|
head->nodes[0] = hindex;
|
|
node1->fillBlocks(heads, flist, hindex, findex);
|
|
} else {
|
|
head->nodes[0] = PATH_NODE_NONE;
|
|
}
|
|
if(node2 != NULL) {
|
|
head->nodes[1] = hindex;
|
|
node2->fillBlocks(heads, flist, hindex, findex);
|
|
} else {
|
|
head->nodes[1] = PATH_NODE_NONE;
|
|
}
|
|
if(node3 != NULL) {
|
|
head->nodes[2] = hindex;
|
|
node3->fillBlocks(heads, flist, hindex, findex);
|
|
} else {
|
|
head->nodes[2] = PATH_NODE_NONE;
|
|
}
|
|
if(node4 != NULL) {
|
|
head->nodes[3] = hindex;
|
|
node4->fillBlocks(heads, flist, hindex, findex);
|
|
} else {
|
|
head->nodes[3] = PATH_NODE_NONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned long QTNode::countQTNodes() const {
|
|
unsigned long c = 1;
|
|
if(node1 != NULL)
|
|
c += node1->countQTNodes();
|
|
if(node2 != NULL)
|
|
c += node2->countQTNodes();
|
|
if(node3 != NULL)
|
|
c += node3->countQTNodes();
|
|
if(node4 != NULL)
|
|
c += node4->countQTNodes();
|
|
return(c);
|
|
}
|
|
|
|
/*unsigned long QTNode::countNodes() const {
|
|
unsigned long c = nodes.size();
|
|
if(node1 != NULL)
|
|
c += node1->countNodes();
|
|
if(node2 != NULL)
|
|
c += node2->countNodes();
|
|
if(node3 != NULL)
|
|
c += node3->countNodes();
|
|
if(node4 != NULL)
|
|
c += node4->countNodes();
|
|
return(c);
|
|
}*/
|
|
|
|
unsigned long QTNode::countPathNodes() const {
|
|
// unsigned long c = final? nodes.size() : 0;
|
|
unsigned long c = nodes.size();
|
|
if(node1 != NULL)
|
|
c += node1->countPathNodes();
|
|
if(node2 != NULL)
|
|
c += node2->countPathNodes();
|
|
if(node3 != NULL)
|
|
c += node3->countPathNodes();
|
|
if(node4 != NULL)
|
|
c += node4->countPathNodes();
|
|
return(c);
|
|
}
|
|
|
|
void QTNode::divideYourself(int depth) {
|
|
// printf("Dividing in box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
|
// minx, maxx, miny, maxy, depth, nodes.size());
|
|
|
|
unsigned long cc;
|
|
cc = nodes.size();
|
|
#ifdef MAX_QUADRENT_NODES
|
|
if(cc <= MAX_QUADRENT_NODES) {
|
|
#ifdef SPLIT_DEBUG
|
|
printf("Stopping (nodecount) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
|
minx, maxx, miny, maxy, depth, cc);
|
|
#endif
|
|
final = true;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MIN_QUADRENT_SIZE
|
|
if((maxx - minx) < MIN_QUADRENT_SIZE || (maxy - miny) < MIN_QUADRENT_SIZE) {
|
|
#ifdef SPLIT_DEBUG
|
|
printf("Stopping on box (size) (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
|
minx, maxx, miny, maxy, depth, cc);
|
|
#endif
|
|
final = true;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
doSplit();
|
|
|
|
//get counts on our split
|
|
float c1, c2, c3, c4;
|
|
c1 = node1? node1->nodes.size() : 0;
|
|
c2 = node2? node2->nodes.size() : 0;
|
|
c3 = node3? node3->nodes.size() : 0;
|
|
c4 = node4? node4->nodes.size() : 0;
|
|
|
|
#ifdef MIN_QUADRENT_GAIN
|
|
int miss = 0;
|
|
float gain1 = 1.0 - c1 / cc;
|
|
float gain2 = 1.0 - c2 / cc;
|
|
float gain3 = 1.0 - c3 / cc;
|
|
float gain4 = 1.0 - c4 / cc;
|
|
|
|
//see how many missed the gain mark
|
|
if(gain1 < MIN_QUADRENT_GAIN)
|
|
miss++;
|
|
if(gain2 < MIN_QUADRENT_GAIN)
|
|
miss++;
|
|
if(gain3 < MIN_QUADRENT_GAIN)
|
|
miss++;
|
|
if(gain4 < MIN_QUADRENT_GAIN)
|
|
miss++;
|
|
|
|
if(miss > MAX_QUADRENT_MISSES) {
|
|
#ifdef SPLIT_DEBUG
|
|
printf("Stopping (gain) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
|
minx, maxx, miny, maxy, depth, cc);
|
|
#endif
|
|
final = true;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
//if all nodes pass through all quadrents, then we are done
|
|
//partially obsoleted by gain test.
|
|
if(c1 == c2 && c1 == c3 && c1 == c4) {
|
|
#ifdef SPLIT_DEBUG
|
|
printf("Stopping (empty) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n",
|
|
minx, maxx, miny, maxy, depth, cc);
|
|
printf("Our counts: %.3f, %.3f, %.3f, %.3f\n", c1, c2, c3, c4);
|
|
#endif
|
|
final = true;
|
|
return;
|
|
}
|
|
|
|
//there are prolly some more intelligent stopping criteria...
|
|
|
|
depth++;
|
|
|
|
if(node1 != NULL)
|
|
node1->divideYourself(depth);
|
|
if(node2 != NULL)
|
|
node2->divideYourself(depth);
|
|
if(node3 != NULL)
|
|
node3->divideYourself(depth);
|
|
if(node4 != NULL)
|
|
node4->divideYourself(depth);
|
|
|
|
|
|
}
|
|
|
|
void QTNode::buildVertexes() {
|
|
|
|
v[0].x = v[1].x = v[2].x = v[3].x = minx;
|
|
v[4].x = v[5].x = v[6].x = v[7].x = maxx;
|
|
|
|
v[0].y = v[1].y = v[4].y = v[5].y = miny;
|
|
v[2].y = v[3].y = v[6].y = v[7].y = maxy;
|
|
|
|
v[0].z = v[3].z = v[4].z = v[7].z = -999999;
|
|
v[1].z = v[2].z = v[5].z = v[6].z = 9999999;
|
|
}
|
|
|
|
bool QTNode::IsInNode(const QTNode *n, const PathNode *o) {
|
|
if( o->x >= n->minx && o->x < n->maxx
|
|
&& o->y >= n->miny && o->y < n->maxy )
|
|
return(true);
|
|
|
|
//well its not inside the node, so see if it is reachable from it
|
|
|
|
//4 points of this node
|
|
GPoint pt1(n->minx, n->miny, 0),
|
|
pt2(n->minx, n->maxy, 0),
|
|
pt3(n->maxx, n->miny, 0),
|
|
pt4(n->maxx, n->maxy, 0);
|
|
if( o->Dist2(&pt1) < search_dist2
|
|
|| o->Dist2(&pt2) < search_dist2
|
|
|| o->Dist2(&pt3) < search_dist2
|
|
|| o->Dist2(&pt4) < search_dist2)
|
|
return(true);
|
|
|
|
//not inside, and not reachable...
|
|
|
|
return(false);
|
|
}
|
|
|
|
void QTNode::doSplit() {
|
|
|
|
|
|
//find midpoints...
|
|
float midx = minx + (maxx - minx) / 2.0;
|
|
float midy = miny + (maxy - miny) / 2.0;
|
|
|
|
//ordering following definitions in map.h
|
|
node1 = new QTNode(map, search_dist2, midx, maxx, midy, maxy);
|
|
node2 = new QTNode(map, search_dist2, minx, midx, midy, maxy);
|
|
node3 = new QTNode(map, search_dist2, minx, midx, miny, midy);
|
|
node4 = new QTNode(map, search_dist2, midx, maxx, miny, midy);
|
|
if(node1 == NULL || node2 == NULL || node3 == NULL || node4 == NULL) {
|
|
printf("Error: unable to allocate new QTNode, giving up.\n");
|
|
return;
|
|
}
|
|
|
|
// unsigned long l;
|
|
// l = faces.size();
|
|
// for(r = 0; r < l; r++) {
|
|
// PathNode *cur = faces[r];
|
|
list<PathNode *>::iterator curs,end;
|
|
curs = nodes.begin();
|
|
end = nodes.end();
|
|
for(; curs != end; curs++) {
|
|
PathNode *cur = *curs;
|
|
if(IsInNode(node1, cur))
|
|
node1->nodes.push_back(cur);
|
|
if(IsInNode(node2, cur))
|
|
node2->nodes.push_back(cur);
|
|
if(IsInNode(node3, cur))
|
|
node3->nodes.push_back(cur);
|
|
if(IsInNode(node4, cur))
|
|
node4->nodes.push_back(cur);
|
|
}
|
|
|
|
//clean up empty sets.
|
|
if(node1->nodes.size() == 0) {
|
|
delete node1;
|
|
node1 = NULL;
|
|
}
|
|
if(node2->nodes.size() == 0) {
|
|
delete node2;
|
|
node2 = NULL;
|
|
}
|
|
if(node3->nodes.size() == 0) {
|
|
delete node3;
|
|
node3 = NULL;
|
|
}
|
|
if(node4->nodes.size() == 0) {
|
|
delete node4;
|
|
node4 = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|