/* 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::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::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; } }