mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2025-11-29 17:38:24 +08:00
269 lines
7.1 KiB
C++
269 lines
7.1 KiB
C++
#ifndef _REGULAR_GRID_H
|
|
#define _REGULAR_GRID_H
|
|
|
|
|
|
#include <G3D/Ray.h>
|
|
#include <G3D/Table.h>
|
|
#include <G3D/BoundsTrait.h>
|
|
#include <G3D/PositionTrait.h>
|
|
|
|
#include "Errors.h"
|
|
|
|
template <class Node>
|
|
class NodeArray
|
|
{
|
|
public:
|
|
explicit NodeArray() { memset(&_nodes, 0, sizeof(_nodes)); }
|
|
void AddNode(Node* n)
|
|
{
|
|
for (uint8 i = 0; i < 9; ++i)
|
|
if (_nodes[i] == 0)
|
|
{
|
|
_nodes[i] = n;
|
|
return;
|
|
}
|
|
else if (_nodes[i] == n)
|
|
return;
|
|
}
|
|
Node* _nodes[9];
|
|
};
|
|
|
|
template<class Node>
|
|
struct NodeCreator
|
|
{
|
|
static Node* makeNode(int /*x*/, int /*y*/) { return new Node();}
|
|
};
|
|
|
|
template<class T,
|
|
class Node,
|
|
class NodeCreatorFunc = NodeCreator<Node>,
|
|
/*class BoundsFunc = BoundsTrait<T>,*/
|
|
class PositionFunc = PositionTrait<T>
|
|
>
|
|
class RegularGrid2D
|
|
{
|
|
public:
|
|
|
|
enum
|
|
{
|
|
CELL_NUMBER = 64,
|
|
};
|
|
|
|
#define HGRID_MAP_SIZE (533.33333f * 64.f) // shouldn't be changed
|
|
#define CELL_SIZE float(HGRID_MAP_SIZE/(float)CELL_NUMBER)
|
|
|
|
typedef G3D::Table<const T*, NodeArray<Node>> MemberTable;
|
|
|
|
MemberTable memberTable;
|
|
Node* nodes[CELL_NUMBER][CELL_NUMBER];
|
|
|
|
RegularGrid2D()
|
|
{
|
|
memset(nodes, 0, sizeof(nodes));
|
|
}
|
|
|
|
~RegularGrid2D()
|
|
{
|
|
for (int x = 0; x < CELL_NUMBER; ++x)
|
|
for (int y = 0; y < CELL_NUMBER; ++y)
|
|
delete nodes[x][y];
|
|
}
|
|
|
|
void insert(const T& value)
|
|
{
|
|
G3D::Vector3 pos[9];
|
|
pos[0] = value.getBounds().corner(0);
|
|
pos[1] = value.getBounds().corner(1);
|
|
pos[2] = value.getBounds().corner(2);
|
|
pos[3] = value.getBounds().corner(3);
|
|
pos[4] = (pos[0] + pos[1]) / 2.0f;
|
|
pos[5] = (pos[1] + pos[2]) / 2.0f;
|
|
pos[6] = (pos[2] + pos[3]) / 2.0f;
|
|
pos[7] = (pos[3] + pos[0]) / 2.0f;
|
|
pos[8] = (pos[0] + pos[2]) / 2.0f;
|
|
|
|
NodeArray<Node> na;
|
|
for (uint8 i = 0; i < 9; ++i)
|
|
{
|
|
Cell c = Cell::ComputeCell(pos[i].x, pos[i].y);
|
|
if (!c.isValid())
|
|
continue;
|
|
Node& node = getGridFor(pos[i].x, pos[i].y);
|
|
na.AddNode(&node);
|
|
}
|
|
|
|
for (uint8 i = 0; i < 9; ++i)
|
|
{
|
|
if (na._nodes[i])
|
|
na._nodes[i]->insert(value);
|
|
else
|
|
break;
|
|
}
|
|
|
|
memberTable.set(&value, na);
|
|
}
|
|
|
|
void remove(const T& value)
|
|
{
|
|
NodeArray<Node>& na = memberTable[&value];
|
|
for (uint8 i = 0; i < 9; ++i)
|
|
{
|
|
if (na._nodes[i])
|
|
na._nodes[i]->remove(value);
|
|
else
|
|
break;
|
|
}
|
|
|
|
// Remove the member
|
|
memberTable.remove(&value);
|
|
}
|
|
|
|
void balance()
|
|
{
|
|
for (int x = 0; x < CELL_NUMBER; ++x)
|
|
for (int y = 0; y < CELL_NUMBER; ++y)
|
|
if (Node* n = nodes[x][y])
|
|
n->balance();
|
|
}
|
|
|
|
bool contains(const T& value) const { return memberTable.containsKey(&value); }
|
|
int size() const { return memberTable.size(); }
|
|
|
|
struct Cell
|
|
{
|
|
int x, y;
|
|
bool operator == (const Cell& c2) const { return x == c2.x && y == c2.y;}
|
|
|
|
static Cell ComputeCell(float fx, float fy)
|
|
{
|
|
Cell c = { int(fx * (1.f / CELL_SIZE) + (CELL_NUMBER / 2)), int(fy * (1.f / CELL_SIZE) + (CELL_NUMBER / 2)) };
|
|
return c;
|
|
}
|
|
|
|
bool isValid() const { return x >= 0 && x < CELL_NUMBER && y >= 0 && y < CELL_NUMBER;}
|
|
};
|
|
|
|
|
|
Node& getGridFor(float fx, float fy)
|
|
{
|
|
Cell c = Cell::ComputeCell(fx, fy);
|
|
return getGrid(c.x, c.y);
|
|
}
|
|
|
|
Node& getGrid(int x, int y)
|
|
{
|
|
ASSERT(x < CELL_NUMBER && y < CELL_NUMBER);
|
|
if (!nodes[x][y])
|
|
nodes[x][y] = NodeCreatorFunc::makeNode(x, y);
|
|
return *nodes[x][y];
|
|
}
|
|
|
|
template<typename RayCallback>
|
|
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float max_dist, bool stopAtFirstHit)
|
|
{
|
|
intersectRay(ray, intersectCallback, max_dist, ray.origin() + ray.direction() * max_dist, stopAtFirstHit);
|
|
}
|
|
|
|
template<typename RayCallback>
|
|
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist, const G3D::Vector3& end, bool stopAtFirstHit)
|
|
{
|
|
Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
|
|
if (!cell.isValid())
|
|
return;
|
|
|
|
Cell last_cell = Cell::ComputeCell(end.x, end.y);
|
|
|
|
if (cell == last_cell)
|
|
{
|
|
if (Node* node = nodes[cell.x][cell.y])
|
|
node->intersectRay(ray, intersectCallback, max_dist, stopAtFirstHit);
|
|
return;
|
|
}
|
|
|
|
float voxel = (float)CELL_SIZE;
|
|
float kx_inv = ray.invDirection().x, bx = ray.origin().x;
|
|
float ky_inv = ray.invDirection().y, by = ray.origin().y;
|
|
|
|
int stepX, stepY;
|
|
float tMaxX, tMaxY;
|
|
if (kx_inv >= 0)
|
|
{
|
|
stepX = 1;
|
|
float x_border = (cell.x + 1) * voxel;
|
|
tMaxX = (x_border - bx) * kx_inv;
|
|
}
|
|
else
|
|
{
|
|
stepX = -1;
|
|
float x_border = (cell.x - 1) * voxel;
|
|
tMaxX = (x_border - bx) * kx_inv;
|
|
}
|
|
|
|
if (ky_inv >= 0)
|
|
{
|
|
stepY = 1;
|
|
float y_border = (cell.y + 1) * voxel;
|
|
tMaxY = (y_border - by) * ky_inv;
|
|
}
|
|
else
|
|
{
|
|
stepY = -1;
|
|
float y_border = (cell.y - 1) * voxel;
|
|
tMaxY = (y_border - by) * ky_inv;
|
|
}
|
|
|
|
//int Cycles = std::max((int)ceilf(max_dist/tMaxX),(int)ceilf(max_dist/tMaxY));
|
|
//int i = 0;
|
|
|
|
float tDeltaX = voxel * fabs(kx_inv);
|
|
float tDeltaY = voxel * fabs(ky_inv);
|
|
do
|
|
{
|
|
if (Node* node = nodes[cell.x][cell.y])
|
|
{
|
|
//float enterdist = max_dist;
|
|
node->intersectRay(ray, intersectCallback, max_dist, stopAtFirstHit);
|
|
}
|
|
if (cell == last_cell)
|
|
break;
|
|
if (tMaxX < tMaxY)
|
|
{
|
|
tMaxX += tDeltaX;
|
|
cell.x += stepX;
|
|
}
|
|
else
|
|
{
|
|
tMaxY += tDeltaY;
|
|
cell.y += stepY;
|
|
}
|
|
//++i;
|
|
} while (cell.isValid());
|
|
}
|
|
|
|
template<typename IsectCallback>
|
|
void intersectPoint(const G3D::Vector3& point, IsectCallback& intersectCallback)
|
|
{
|
|
Cell cell = Cell::ComputeCell(point.x, point.y);
|
|
if (!cell.isValid())
|
|
return;
|
|
if (Node* node = nodes[cell.x][cell.y])
|
|
node->intersectPoint(point, intersectCallback);
|
|
}
|
|
|
|
// Optimized verson of intersectRay function for rays with vertical directions
|
|
template<typename RayCallback>
|
|
void intersectZAllignedRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist)
|
|
{
|
|
Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
|
|
if (!cell.isValid())
|
|
return;
|
|
if (Node* node = nodes[cell.x][cell.y])
|
|
node->intersectRay(ray, intersectCallback, max_dist, false);
|
|
}
|
|
};
|
|
|
|
#undef CELL_SIZE
|
|
#undef HGRID_MAP_SIZE
|
|
|
|
#endif
|