Inital work on one graph interface
This commit is contained in:
parent
061f187142
commit
8c830bdabf
343
src/tracks/arena_graph.cpp
Normal file
343
src/tracks/arena_graph.cpp
Normal file
@ -0,0 +1,343 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2016 SuperTuxKart Team
|
||||
//
|
||||
// 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; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; 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.
|
||||
|
||||
#include "tracks/arena_graph.hpp"
|
||||
|
||||
#include "io/file_manager.hpp"
|
||||
#include "io/xml_node.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "tracks/arena_node.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <queue>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
ArenaGraph::ArenaGraph(const std::string &navmesh, const XMLNode *node)
|
||||
: Graph()
|
||||
{
|
||||
loadNavmesh(navmesh);
|
||||
|
||||
// Compute shortest distance from all nodes
|
||||
for (unsigned int i = 0; i < getNumNodes(); i++)
|
||||
computeDijkstra(i);
|
||||
|
||||
sortNearbyNodes();
|
||||
if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
|
||||
loadGoalNodes(node);
|
||||
|
||||
} // ArenaGraph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
ArenaNode* ArenaGraph::getNode(unsigned int i) const
|
||||
{
|
||||
ArenaNode* n = dynamic_cast<ArenaNode*>(m_all_nodes[i]);
|
||||
assert(n!= NULL);
|
||||
return n;
|
||||
} // getNode
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void ArenaGraph::differentNodeColor(int n, video::SColor* c) const
|
||||
{
|
||||
std::set<int>::iterator it;
|
||||
it = m_red_node.find(n);
|
||||
if (it != m_red_node.end())
|
||||
{
|
||||
*c = video::SColor(255, 255, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
it = m_blue_node.find(n);
|
||||
if (it != m_blue_node.end())
|
||||
{
|
||||
*c = video::SColor(255, 0, 0, 255);
|
||||
return;
|
||||
}
|
||||
|
||||
} // differentNodeColor
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void ArenaGraph::loadNavmesh(const std::string &navmesh)
|
||||
{
|
||||
XMLNode *xml = file_manager->createXMLTree(navmesh);
|
||||
if (xml->getName() != "navmesh")
|
||||
{
|
||||
Log::error("ArenaGraph", "NavMesh is invalid.");
|
||||
delete xml;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<Vec3> all_vertices;
|
||||
for (unsigned int i = 0; i < xml->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node = xml->getNode(i);
|
||||
if (xml_node->getName() == "vertices")
|
||||
{
|
||||
for (unsigned int i = 0; i < xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if (!(xml_node_node->getName() == "vertex"))
|
||||
{
|
||||
Log::error("ArenaGraph", "Unsupported type '%s' found"
|
||||
"in '%s' - ignored.",
|
||||
xml_node_node->getName().c_str(), navmesh.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reading vertices
|
||||
float x, y, z;
|
||||
xml_node_node->get("x", &x);
|
||||
xml_node_node->get("y", &y);
|
||||
xml_node_node->get("z", &z);
|
||||
Vec3 p(x, y, z);
|
||||
m_bb_min.min(p);
|
||||
m_bb_max.max(p);
|
||||
all_vertices.push_back(p);
|
||||
}
|
||||
}
|
||||
if (xml_node->getName() == "faces")
|
||||
{
|
||||
for (unsigned int i = 0; i < xml_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *xml_node_node = xml_node->getNode(i);
|
||||
if (xml_node_node->getName() != "face")
|
||||
{
|
||||
Log::error("NavMesh", "Unsupported type '%s' found in '%s'"
|
||||
" - ignored.",
|
||||
xml_node_node->getName().c_str(), navmesh.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reading quads
|
||||
std::vector<int> quad_index;
|
||||
std::vector<int> adjacent_quad_index;
|
||||
xml_node_node->get("indices", &quad_index);
|
||||
xml_node_node->get("adjacents", &adjacent_quad_index);
|
||||
assert(quad_index.size() == 4);
|
||||
|
||||
createQuad(all_vertices[quad_index[0]],
|
||||
all_vertices[quad_index[1]], all_vertices[quad_index[2]],
|
||||
all_vertices[quad_index[3]], m_all_nodes.size(),
|
||||
false/*invisible*/, false/*ai_ignore*/, true/*is_arena*/);
|
||||
|
||||
ArenaNode* cur_node = getNode(m_all_nodes.size() - 1);
|
||||
cur_node->setAdjacentNodes(adjacent_quad_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete xml;
|
||||
|
||||
// Xml is loaded, now initialize the graph
|
||||
const unsigned int n_nodes = getNumNodes();
|
||||
|
||||
m_distance_matrix = std::vector<std::vector<float>>
|
||||
(n_nodes, std::vector<float>(n_nodes, 9999.9f));
|
||||
for (unsigned int i = 0; i < n_nodes; i++)
|
||||
{
|
||||
ArenaNode* cur_node = getNode(i);
|
||||
for (const int& adjacent : cur_node->getAdjacentNodes())
|
||||
{
|
||||
Vec3 diff = getNode(adjacent)->getCenter() - cur_node->getCenter();
|
||||
float distance = diff.length();
|
||||
m_distance_matrix[i][adjacent] = distance;
|
||||
}
|
||||
m_distance_matrix[i][i] = 0.0f;
|
||||
}
|
||||
|
||||
// Allocate and initialise the previous node data structure:
|
||||
m_parent_node = std::vector<std::vector<int>>
|
||||
(n_nodes, std::vector<int>(n_nodes, Graph::UNKNOWN_SECTOR));
|
||||
for (unsigned int i = 0; i < n_nodes; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < n_nodes; j++)
|
||||
{
|
||||
if (i == j || m_distance_matrix[i][j] >= 9899.9f)
|
||||
m_parent_node[i][j] = -1;
|
||||
else
|
||||
m_parent_node[i][j] = i;
|
||||
} // for j
|
||||
} // for i
|
||||
|
||||
} // loadNavmesh
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Dijkstra shortest path computation. It computes the shortest distance from
|
||||
* the specified node 'source' to all other nodes. At the end of the
|
||||
* computation, m_distance_matrix[i][j] stores the shortest path distance from
|
||||
* source to j and m_parent_node[source][j] stores the last vertex visited on
|
||||
* the shortest path from i to j before visiting j. Suppose the shortest path
|
||||
* from i to j is i->......->k->j then m_parent_node[i][j] = k
|
||||
*/
|
||||
void ArenaGraph::computeDijkstra(int source)
|
||||
{
|
||||
// Stores the distance (float) to 'source' from a specified node (int)
|
||||
typedef std::pair<int, float> IndDistPair;
|
||||
|
||||
class Shortest
|
||||
{
|
||||
public:
|
||||
bool operator()(const IndDistPair &p1, const IndDistPair &p2)
|
||||
{
|
||||
return p1.second > p2.second;
|
||||
}
|
||||
};
|
||||
|
||||
std::priority_queue<IndDistPair, std::vector<IndDistPair>, Shortest> queue;
|
||||
IndDistPair begin(source, 0.0f);
|
||||
queue.push(begin);
|
||||
const unsigned int n = getNumNodes();
|
||||
std::vector<bool> visited;
|
||||
visited.resize(n, false);
|
||||
while (!queue.empty())
|
||||
{
|
||||
// Get element with shortest path
|
||||
IndDistPair current = queue.top();
|
||||
queue.pop();
|
||||
int cur_index = current.first;
|
||||
if (visited[cur_index]) continue;
|
||||
visited[cur_index] = true;
|
||||
|
||||
for (const int& adjacent : getNode(cur_index)->getAdjacentNodes())
|
||||
{
|
||||
// Distance already computed, can be ignored
|
||||
if (visited[adjacent]) continue;
|
||||
|
||||
float new_dist =
|
||||
current.second + m_distance_matrix[cur_index][adjacent];
|
||||
if (new_dist < m_distance_matrix[source][adjacent])
|
||||
{
|
||||
m_distance_matrix[source][adjacent] = new_dist;
|
||||
m_parent_node[source][adjacent] = cur_index;
|
||||
}
|
||||
IndDistPair pair(adjacent, new_dist);
|
||||
queue.push(pair);
|
||||
}
|
||||
}
|
||||
} // computeDijkstra
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** THIS FUNCTION IS ONLY USED FOR UNIT-TESTING, to verify that the new
|
||||
* Dijkstra algorithm gives the same results.
|
||||
* computeFloydWarshall() computes the shortest distance between any two
|
||||
* nodes. At the end of the computation, m_distance_matrix[i][j] stores the
|
||||
* shortest path distance from i to j and m_parent_node[i][j] stores the last
|
||||
* vertex visited on the shortest path from i to j before visiting j. Suppose
|
||||
* the shortest path from i to j is i->......->k->j then
|
||||
* m_parent_node[i][j] = k
|
||||
*/
|
||||
void ArenaGraph::computeFloydWarshall()
|
||||
{
|
||||
unsigned int n = getNumNodes();
|
||||
|
||||
// initialize m_parent_node with unknown_node so that if no path is found
|
||||
// b/w i and j then m_parent_node[i][j] = -1 (UNKNOWN_SECTOR)
|
||||
// AI must check this
|
||||
m_parent_node = std::vector< std::vector<int> >
|
||||
(n, std::vector<int>(n, Graph::UNKNOWN_SECTOR));
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < n; j++)
|
||||
{
|
||||
if(i == j || m_distance_matrix[i][j]>=9899.9f)
|
||||
m_parent_node[i][j]=-1;
|
||||
else
|
||||
m_parent_node[i][j] = i;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int k = 0; k < n; k++)
|
||||
{
|
||||
for (unsigned int i = 0; i < n; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < n; j++)
|
||||
{
|
||||
if ((m_distance_matrix[i][k] + m_distance_matrix[k][j]) <
|
||||
m_distance_matrix[i][j])
|
||||
{
|
||||
m_distance_matrix[i][j] =
|
||||
m_distance_matrix[i][k] + m_distance_matrix[k][j];
|
||||
m_parent_node[i][j] = m_parent_node[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // computeFloydWarshall
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void ArenaGraph::loadGoalNodes(const XMLNode *node)
|
||||
{
|
||||
const XMLNode *check_node = node->getNode("checks");
|
||||
for (unsigned int i = 0; i < check_node->getNumNodes(); i++)
|
||||
{
|
||||
const XMLNode *goal = check_node->getNode(i);
|
||||
if (goal->getName() =="goal")
|
||||
{
|
||||
Vec3 p1, p2;
|
||||
bool first_goal = false;
|
||||
goal->get("first_goal", &first_goal);
|
||||
goal->get("p1", &p1);
|
||||
goal->get("p2", &p2);
|
||||
|
||||
int first = Graph::UNKNOWN_SECTOR;
|
||||
findRoadSector(p1, &first, NULL, true);
|
||||
int last = Graph::UNKNOWN_SECTOR;
|
||||
findRoadSector(p2, &last, NULL, true);
|
||||
|
||||
first_goal ? m_blue_node.insert(first) : m_red_node.insert(first);
|
||||
first_goal ? m_blue_node.insert(last) : m_red_node.insert(last);
|
||||
while (first != last)
|
||||
{
|
||||
// Find all the nodes which connect the two points of
|
||||
// goal, notice: only work if it's a straight line
|
||||
first = getNextShortestPath(first, last);
|
||||
first_goal ? m_blue_node.insert(first) :
|
||||
m_red_node.insert(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // loadGoalNodes
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ArenaGraph::sortNearbyNodes()
|
||||
{
|
||||
// Only save the nearby 8 nodes
|
||||
const unsigned int try_count = 8;
|
||||
for (unsigned int i = 0; i < getNumNodes(); i++)
|
||||
{
|
||||
// Get the distance to all nodes at i
|
||||
ArenaNode* cur_node = getNode(i);
|
||||
std::vector<int> nearby_nodes;
|
||||
std::vector<float> dist = m_distance_matrix[i];
|
||||
|
||||
// Skip the same node
|
||||
dist[i] = 999999.0f;
|
||||
for (unsigned int j = 0; j < try_count; j++)
|
||||
{
|
||||
std::vector<float>::iterator it =
|
||||
std::min_element(dist.begin(), dist.end());
|
||||
const int pos = it - dist.begin();
|
||||
nearby_nodes.push_back(pos);
|
||||
dist[pos] = 999999.0f;
|
||||
}
|
||||
cur_node->setNearbyNodes(nearby_nodes);
|
||||
}
|
||||
|
||||
} // sortNearbyNodes
|
||||
|
||||
// ----------------------------------------------------------------------------
|
84
src/tracks/arena_graph.hpp
Normal file
84
src/tracks/arena_graph.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2016 SuperTuxKart Team
|
||||
//
|
||||
// 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; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; 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.
|
||||
|
||||
#ifndef HEADER_ARENA_GRAPH_HPP
|
||||
#define HEADER_ARENA_GRAPH_HPP
|
||||
|
||||
#include "tracks/graph.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
class ArenaNode;
|
||||
class XMLNode;
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class ArenaGraph : public Graph
|
||||
{
|
||||
private:
|
||||
/** The actual graph data structure, it is an adjacency matrix. */
|
||||
std::vector<std::vector<float>> m_distance_matrix;
|
||||
|
||||
/** The matrix that is used to store computed shortest paths. */
|
||||
std::vector<std::vector<int>> m_parent_node;
|
||||
|
||||
/** Used in soccer mode to colorize the goal lines in minimap. */
|
||||
std::set<int> m_red_node;
|
||||
|
||||
std::set<int> m_blue_node;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void loadGoalNodes(const XMLNode *node);
|
||||
// ------------------------------------------------------------------------
|
||||
void loadNavmesh(const std::string &navmesh);
|
||||
// ------------------------------------------------------------------------
|
||||
void sortNearbyNodes();
|
||||
// ------------------------------------------------------------------------
|
||||
void computeDijkstra(int n);
|
||||
// ------------------------------------------------------------------------
|
||||
void computeFloydWarshall();
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool hasLapLine() const OVERRIDE { return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void differentNodeColor(int n, video::SColor* c) const OVERRIDE;
|
||||
|
||||
public:
|
||||
static ArenaGraph* get() { return dynamic_cast<ArenaGraph*>(m_graph); }
|
||||
// ------------------------------------------------------------------------
|
||||
ArenaGraph(const std::string &navmesh, const XMLNode *node = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~ArenaGraph() {}
|
||||
// ------------------------------------------------------------------------
|
||||
ArenaNode* getNode(unsigned int i) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the next node on the shortest path from i to j.
|
||||
* Note: m_parent_node[j][i] contains the parent of i on path from j to i,
|
||||
* which is the next node on the path from i to j (undirected graph)
|
||||
*/
|
||||
int getNextShortestPath(int i, int j) const
|
||||
{
|
||||
if (i == Graph::UNKNOWN_SECTOR || j == Graph::UNKNOWN_SECTOR)
|
||||
return Graph::UNKNOWN_SECTOR;
|
||||
return m_parent_node[j][i];
|
||||
}
|
||||
|
||||
}; // Graph
|
||||
|
||||
#endif
|
45
src/tracks/arena_node.cpp
Normal file
45
src/tracks/arena_node.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2016 SuperTuxKart-Team
|
||||
//
|
||||
// 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; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; 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.
|
||||
|
||||
#include "tracks/arena_node.hpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
ArenaNode::ArenaNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
|
||||
const Vec3 &p3, const Vec3 &normal,
|
||||
unsigned int node_index)
|
||||
: Quad(p0, p1, p2, p3, normal, node_index)
|
||||
{
|
||||
Vec3 lower_center = (p0 + p1) * 0.5f;
|
||||
Vec3 upper_center = (p2 + p3) * 0.5f;
|
||||
|
||||
m_line = core::line3df(lower_center.toIrrVector(),
|
||||
upper_center.toIrrVector());
|
||||
} // ArenaNode
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the square of the distance between the given point and any point
|
||||
* on the 'centre' line, i.e. the finite line from the middle point of the
|
||||
* lower end of the node to the middle point of the upper end of the node
|
||||
* which belongs to this node.
|
||||
* \param xyz The point for which the distance to the line is computed.
|
||||
*/
|
||||
float ArenaNode::getDistance2FromPoint(const Vec3 &xyz) const
|
||||
{
|
||||
core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector());
|
||||
return (closest-xyz.toIrrVector()).getLengthSQ();
|
||||
} // getDistance2FromPoint
|
63
src/tracks/arena_node.hpp
Normal file
63
src/tracks/arena_node.hpp
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2016 SuperTuxKart-Team
|
||||
//
|
||||
// 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; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; 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.
|
||||
|
||||
#ifndef HEADER_ARENA_NODE_HPP
|
||||
#define HEADER_ARENA_NODE_HPP
|
||||
|
||||
#include "tracks/quad.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class ArenaNode : public Quad
|
||||
{
|
||||
private:
|
||||
core::line3df m_line;
|
||||
|
||||
std::vector<int> m_adjacent_nodes;
|
||||
|
||||
std::vector<int> m_nearby_nodes;
|
||||
|
||||
public:
|
||||
ArenaNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
|
||||
const Vec3 &normal, unsigned int node_index);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~ArenaNode() {}
|
||||
// ------------------------------------------------------------------------
|
||||
const std::vector<int>& getAdjacentNodes() { return m_adjacent_nodes; }
|
||||
// ------------------------------------------------------------------------
|
||||
const std::vector<int>& getNearbyNodes() { return m_nearby_nodes; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setAdjacentNodes(const std::vector<int>& nodes)
|
||||
{
|
||||
m_adjacent_nodes = nodes;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setNearbyNodes(const std::vector<int>& nodes)
|
||||
{
|
||||
m_nearby_nodes = nodes;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getDistance2FromPoint(const Vec3 &xyz) const OVERRIDE;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
49
src/tracks/arena_node_3d.hpp
Normal file
49
src/tracks/arena_node_3d.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2016 SuperTuxKart-Team
|
||||
//
|
||||
// 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; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; 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.
|
||||
|
||||
#ifndef HEADER_ARENA_NODE_3D_HPP
|
||||
#define HEADER_ARENA_NODE_3D_HPP
|
||||
|
||||
#include "tracks/arena_node.hpp"
|
||||
#include "tracks/bounding_box_3d.hpp"
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class ArenaNode3D : public ArenaNode,
|
||||
public BoundingBox3D
|
||||
{
|
||||
public:
|
||||
ArenaNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
|
||||
const Vec3 &normal, unsigned int node_index)
|
||||
: ArenaNode(p0, p1, p2, p3, normal, node_index)
|
||||
{
|
||||
BoundingBox3D::init(p0, p1, p2, p3, normal);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool pointInside(const Vec3& p,
|
||||
bool ignore_vertical = false) const OVERRIDE
|
||||
{
|
||||
return BoundingBox3D::pointInside(p);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool is3DQuad() const OVERRIDE { return true; }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
83
src/tracks/bounding_box_3d.hpp
Normal file
83
src/tracks/bounding_box_3d.hpp
Normal file
@ -0,0 +1,83 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2016 SuperTuxKart-Team
|
||||
//
|
||||
// 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; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; 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.
|
||||
|
||||
#ifndef HEADER_NODE_BOUNDING_BOX_3D_HPP
|
||||
#define HEADER_NODE_BOUNDING_BOX_3D_HPP
|
||||
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class BoundingBox3D
|
||||
{
|
||||
private:
|
||||
/** A 3D box using to check if a point lies inside a quad.
|
||||
*/
|
||||
Vec3 m_box_faces[6][4];
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
bool pointInside(const Vec3& p) const
|
||||
{
|
||||
float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1],
|
||||
m_box_faces[0][2]);
|
||||
for (int i = 1; i < 6; i++)
|
||||
{
|
||||
if (side * p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1],
|
||||
m_box_faces[i][2]) < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void init(const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec3& p3,
|
||||
const Vec3& normal)
|
||||
{
|
||||
// Compute the node bounding box used by pointInside
|
||||
Vec3 box_corners[8];
|
||||
float box_high = 5.0f;
|
||||
float box_low = 1.0f;
|
||||
box_corners[0] = p0 + box_high * normal;
|
||||
box_corners[1] = p1 + box_high * normal;
|
||||
box_corners[2] = p2 + box_high * normal;
|
||||
box_corners[3] = p3 + box_high * normal;
|
||||
box_corners[4] = p0 - box_low * normal;
|
||||
box_corners[5] = p1 - box_low * normal;
|
||||
box_corners[6] = p2 - box_low * normal;
|
||||
box_corners[7] = p3 - box_low * normal;
|
||||
|
||||
Vec3 box_faces[6][4] =
|
||||
{
|
||||
{ box_corners[0], box_corners[1], box_corners[2], box_corners[3] },
|
||||
{ box_corners[3], box_corners[2], box_corners[6], box_corners[7] },
|
||||
{ box_corners[7], box_corners[6], box_corners[5], box_corners[4] },
|
||||
{ box_corners[1], box_corners[0], box_corners[4], box_corners[5] },
|
||||
{ box_corners[4], box_corners[0], box_corners[3], box_corners[7] },
|
||||
{ box_corners[1], box_corners[5], box_corners[6], box_corners[2] }
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < 6 ; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < 4; j++)
|
||||
m_box_faces[i][j] = box_faces[i][j];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
616
src/tracks/graph.cpp
Normal file
616
src/tracks/graph.cpp
Normal file
@ -0,0 +1,616 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2016 SuperTuxKart Team
|
||||
//
|
||||
// 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; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; 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.
|
||||
|
||||
#include "tracks/graph.hpp"
|
||||
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <IMesh.h>
|
||||
#include <IMeshSceneNode.h>
|
||||
#include <ISceneManager.h>
|
||||
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/glwrap.hpp"
|
||||
#include "graphics/shaders.hpp"
|
||||
#include "graphics/rtts.hpp"
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "tracks/arena_node_3d.hpp"
|
||||
#include "tracks/node_2d.hpp"
|
||||
#include "tracks/node_3d.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
const int Graph::UNKNOWN_SECTOR = -1;
|
||||
Graph *Graph::m_graph = NULL;
|
||||
// -----------------------------------------------------------------------------
|
||||
Graph::Graph()
|
||||
{
|
||||
m_scaling = 0;
|
||||
m_node = NULL;
|
||||
m_mesh = NULL;
|
||||
m_mesh_buffer = NULL;
|
||||
m_new_rtt = NULL;
|
||||
m_bb_min = Vec3( 99999, 99999, 99999);
|
||||
m_bb_max = Vec3(-99999, -99999, -99999);
|
||||
} // Graph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
Graph::~Graph()
|
||||
{
|
||||
if (m_new_rtt != NULL)
|
||||
{
|
||||
delete m_new_rtt;
|
||||
m_new_rtt = NULL;
|
||||
}
|
||||
|
||||
if (UserConfigParams::m_track_debug)
|
||||
cleanupDebugMesh();
|
||||
|
||||
for (unsigned int i = 0; i < m_all_nodes.size(); i++)
|
||||
{
|
||||
delete m_all_nodes[i];
|
||||
}
|
||||
m_all_nodes.clear();
|
||||
} // ~Graph
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the debug mesh to display the graph on top of the track
|
||||
* model. */
|
||||
void Graph::createDebugMesh()
|
||||
{
|
||||
if (getNumNodes() <= 0) return; // no debug output if not graph
|
||||
|
||||
createMesh(/*show_invisible*/true,
|
||||
/*enable_transparency*/true);
|
||||
|
||||
video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices();
|
||||
for (unsigned int i = 0; i < m_mesh_buffer->getVertexCount(); i++)
|
||||
{
|
||||
// Swap the alpha and back
|
||||
v[i].Color.setAlpha((i%2) ? 64 : 255);
|
||||
}
|
||||
m_node = irr_driver->addMesh(m_mesh, "track-debug-mesh");
|
||||
#ifdef DEBUG
|
||||
m_node->setName("track-debug-mesh");
|
||||
#endif
|
||||
|
||||
} // createDebugMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Cleans up the debug mesh */
|
||||
void Graph::cleanupDebugMesh()
|
||||
{
|
||||
if (m_node != NULL)
|
||||
irr_driver->removeNode(m_node);
|
||||
|
||||
m_node = NULL;
|
||||
// No need to call irr_driber->removeMeshFromCache, since the mesh
|
||||
// was manually made and so never added to the mesh cache.
|
||||
m_mesh->drop();
|
||||
m_mesh = NULL;
|
||||
} // cleanupDebugMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Creates the actual mesh that is used by createDebugMesh() or makeMiniMap()
|
||||
*/
|
||||
void Graph::createMesh(bool show_invisible, bool enable_transparency,
|
||||
const video::SColor *track_color)
|
||||
{
|
||||
// The debug track will not be lighted or culled.
|
||||
video::SMaterial m;
|
||||
m.BackfaceCulling = false;
|
||||
m.Lighting = false;
|
||||
if (enable_transparency)
|
||||
m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
|
||||
m.setTexture(0, getUnicolorTexture(video::SColor(255, 255, 255, 255)));
|
||||
m.setTexture(1, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
|
||||
m.setTexture(7, getUnicolorTexture(video::SColor(0, 0, 0, 0)));
|
||||
m_mesh = irr_driver->createQuadMesh(&m);
|
||||
m_mesh_buffer = m_mesh->getMeshBuffer(0);
|
||||
assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD);
|
||||
|
||||
unsigned int n = 0;
|
||||
const unsigned int total_nodes = getNumNodes();
|
||||
|
||||
// Count the number of quads to display (some quads might be invisible)
|
||||
for (unsigned int i = 0; i < total_nodes; i++)
|
||||
{
|
||||
if (show_invisible || !m_all_nodes[i]->isInvisible())
|
||||
n++;
|
||||
}
|
||||
|
||||
// Four vertices for each of the n-1 remaining quads
|
||||
video::S3DVertex *new_v = new video::S3DVertex[4*n];
|
||||
// Each quad consists of 2 triangles with 3 elements, so
|
||||
// we need 2*3 indices for each quad.
|
||||
irr::u16 *ind = new irr::u16[6*n];
|
||||
video::SColor c(255, 255, 0, 0);
|
||||
|
||||
if (track_color)
|
||||
c = *track_color;
|
||||
|
||||
// Now add all quads
|
||||
int i = 0;
|
||||
for (unsigned int count = 0; count < total_nodes; count++)
|
||||
{
|
||||
// Ignore invisible quads
|
||||
if (!show_invisible && m_all_nodes[count]->isInvisible())
|
||||
continue;
|
||||
|
||||
// Swap the colours from red to blue and back
|
||||
if (!track_color)
|
||||
{
|
||||
c.setRed ((i%2) ? 255 : 0);
|
||||
c.setBlue((i%2) ? 0 : 255);
|
||||
}
|
||||
|
||||
video::SColor this_color = c;
|
||||
differentNodeColor(count, &this_color);
|
||||
// Transfer the 4 points of the current quad to the list of vertices
|
||||
m_all_nodes[count]->getVertices(new_v+4*i, this_color);
|
||||
|
||||
// Set up the indices for the triangles
|
||||
// (note, afaik with opengl we could use quads directly, but the code
|
||||
// would not be portable to directx anymore).
|
||||
ind[6*i ] = 4*i+2; // First triangle: vertex 0, 1, 2
|
||||
ind[6*i+1] = 4*i+1;
|
||||
ind[6*i+2] = 4*i;
|
||||
ind[6*i+3] = 4*i+3; // second triangle: vertex 0, 1, 3
|
||||
ind[6*i+4] = 4*i+2;
|
||||
ind[6*i+5] = 4*i;
|
||||
i++;
|
||||
}
|
||||
|
||||
m_mesh_buffer->append(new_v, n*4, ind, n*6);
|
||||
|
||||
if (hasLapLine())
|
||||
{
|
||||
video::S3DVertex lap_v[4];
|
||||
irr::u16 lap_ind[6];
|
||||
video::SColor lap_color(128, 255, 0, 0);
|
||||
m_all_nodes[0]->getVertices(lap_v, lap_color);
|
||||
|
||||
// Now scale the length (distance between vertix 0 and 3
|
||||
// and between 1 and 2) to be 'length':
|
||||
// Length of the lap line about 3% of the 'height'
|
||||
// of the track.
|
||||
const float length = (m_bb_max.getZ()-m_bb_min.getZ())*0.03f;
|
||||
|
||||
core::vector3df dl = lap_v[3].Pos-lap_v[0].Pos;
|
||||
float ll2 = dl.getLengthSQ();
|
||||
if (ll2 < 0.001)
|
||||
lap_v[3].Pos = lap_v[0].Pos+core::vector3df(0, 0, 1);
|
||||
else
|
||||
lap_v[3].Pos = lap_v[0].Pos+dl*length/sqrt(ll2);
|
||||
|
||||
core::vector3df dr = lap_v[2].Pos-lap_v[1].Pos;
|
||||
float lr2 = dr.getLengthSQ();
|
||||
if (lr2 < 0.001)
|
||||
lap_v[2].Pos = lap_v[1].Pos+core::vector3df(0, 0, 1);
|
||||
else
|
||||
lap_v[2].Pos = lap_v[1].Pos+dr*length/sqrt(lr2);
|
||||
lap_ind[0] = 2;
|
||||
lap_ind[1] = 1;
|
||||
lap_ind[2] = 0;
|
||||
lap_ind[3] = 3;
|
||||
lap_ind[4] = 2;
|
||||
lap_ind[5] = 0;
|
||||
// Set it a bit higher to avoid issued with z fighting,
|
||||
// i.e. part of the lap line might not be visible.
|
||||
for (unsigned int i = 0; i < 4; i++)
|
||||
lap_v[i].Pos.Y += 0.1f;
|
||||
#ifndef USE_TEXTURED_LINE
|
||||
m_mesh_buffer->append(lap_v, 4, lap_ind, 6);
|
||||
#else
|
||||
lap_v[0].TCoords = core::vector2df(0,0);
|
||||
lap_v[1].TCoords = core::vector2df(3,0);
|
||||
lap_v[2].TCoords = core::vector2df(3,1);
|
||||
lap_v[3].TCoords = core::vector2df(0,1);
|
||||
m_mesh_buffer->append(lap_v, 4, lap_ind, 6);
|
||||
video::SMaterial &m = m_mesh_buffer->getMaterial();
|
||||
video::ITexture *t = irr_driver->getTexture("chess.png");
|
||||
m.setTexture(0, t);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Instead of setting the bounding boxes, we could just disable culling,
|
||||
// since the debug track should always be drawn.
|
||||
//m_node->setAutomaticCulling(scene::EAC_OFF);
|
||||
m_mesh_buffer->recalculateBoundingBox();
|
||||
m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox());
|
||||
|
||||
m_mesh_buffer->getMaterial().setTexture(0, irr_driver
|
||||
->getTexture("unlit.png"));
|
||||
|
||||
delete[] ind;
|
||||
delete[] new_v;
|
||||
} // createMesh
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Takes a snapshot of the graph so they can be used as minimap.
|
||||
*/
|
||||
void Graph::makeMiniMap(const core::dimension2du &dimension,
|
||||
const std::string &name,
|
||||
const video::SColor &fill_color,
|
||||
video::ITexture** oldRttMinimap,
|
||||
FrameBuffer** newRttMinimap)
|
||||
{
|
||||
// Skip minimap when profiling
|
||||
if (ProfileWorld::isNoGraphics()) return;
|
||||
|
||||
const video::SColor oldClearColor = World::getWorld()->getClearColor();
|
||||
World::getWorld()
|
||||
->setClearbackBufferColor(video::SColor(0, 255, 255, 255));
|
||||
World::getWorld()->forceFogDisabled(true);
|
||||
*oldRttMinimap = NULL;
|
||||
*newRttMinimap = NULL;
|
||||
|
||||
RTT* newRttProvider = NULL;
|
||||
IrrDriver::RTTProvider* oldRttProvider = NULL;
|
||||
if (CVS->isGLSL())
|
||||
{
|
||||
m_new_rtt = newRttProvider =
|
||||
new RTT(dimension.Width, dimension.Height);
|
||||
}
|
||||
else
|
||||
{
|
||||
oldRttProvider = new IrrDriver::RTTProvider(dimension, name, true);
|
||||
}
|
||||
|
||||
irr_driver->getSceneManager()
|
||||
->setAmbientLight(video::SColor(255, 255, 255, 255));
|
||||
|
||||
createMesh(/*show_invisible part of the track*/ false,
|
||||
/*enable_transparency*/ false,
|
||||
/*track_color*/ &fill_color);
|
||||
|
||||
m_node = irr_driver->addMesh(m_mesh, "mini_map");
|
||||
#ifdef DEBUG
|
||||
m_node->setName("minimap-mesh");
|
||||
#endif
|
||||
|
||||
m_node->setAutomaticCulling(0);
|
||||
m_node->setMaterialFlag(video::EMF_LIGHTING, false);
|
||||
|
||||
// Add the camera:
|
||||
// ---------------
|
||||
scene::ICameraSceneNode *camera = irr_driver->addCameraSceneNode();
|
||||
Vec3 center = (m_bb_max+m_bb_min)*0.5f;
|
||||
|
||||
float dx = m_bb_max.getX()-m_bb_min.getX();
|
||||
float dz = m_bb_max.getZ()-m_bb_min.getZ();
|
||||
|
||||
// Set the scaling correctly. Also the center point (which is used
|
||||
// as the camera position) needs to be adjusted: the track must
|
||||
// be aligned to the left/top of the texture which is used (otherwise
|
||||
// mapPoint2MiniMap doesn't work), so adjust the camera position
|
||||
// that the track is properly aligned (view from the side):
|
||||
// c camera
|
||||
// / \ .
|
||||
// / \ <--- camera angle
|
||||
// / \ .
|
||||
// { [-] } <--- track flat (viewed from the side)
|
||||
// If [-] is the shorter side of the track, then the camera will
|
||||
// actually render the area in { } - which is the length of the
|
||||
// longer side of the track.
|
||||
// To align the [-] side to the left, the camera must be moved
|
||||
// the distance betwwen '{' and '[' to the right. This distance
|
||||
// is exacly (longer_side - shorter_side) / 2.
|
||||
// So, adjust the center point by this amount:
|
||||
if (dz > dx)
|
||||
{
|
||||
center.setX(center.getX() + (dz-dx)*0.5f);
|
||||
m_scaling = dimension.Width / dz;
|
||||
}
|
||||
else
|
||||
{
|
||||
center.setZ(center.getZ() + (dx-dz)*0.5f);
|
||||
m_scaling = dimension.Width / dx;
|
||||
}
|
||||
|
||||
float range = (dx>dz) ? dx : dz;
|
||||
|
||||
core::matrix4 projection;
|
||||
projection.buildProjectionMatrixOrthoLH
|
||||
(range /* width */, range /* height */, -1,
|
||||
m_bb_max.getY()-m_bb_min.getY()+1);
|
||||
camera->setProjectionMatrix(projection, true);
|
||||
|
||||
irr_driver->suppressSkyBox();
|
||||
irr_driver->clearLights();
|
||||
|
||||
// Adjust Y position by +1 for max, -1 for min - this helps in case that
|
||||
// the maximum Y coordinate is negative (otherwise the minimap is mirrored)
|
||||
// and avoids problems for tracks which have a flat (max Y=min Y) minimap.
|
||||
camera->setPosition(core::vector3df(center.getX(), m_bb_min.getY() + 1.0f,
|
||||
center.getZ()));
|
||||
//camera->setPosition(core::vector3df(center.getX() - 5.0f,
|
||||
// m_bb_min.getY() - 1 - 5.0f, center.getZ() - 15.0f));
|
||||
camera->setUpVector(core::vector3df(0, 0, 1));
|
||||
camera->setTarget(core::vector3df(center.getX(), m_bb_min.getY() - 1,
|
||||
center.getZ()));
|
||||
//camera->setAspectRatio(1.0f);
|
||||
camera->updateAbsolutePosition();
|
||||
|
||||
video::ITexture* texture = NULL;
|
||||
FrameBuffer* frame_buffer = NULL;
|
||||
|
||||
if (CVS->isGLSL())
|
||||
{
|
||||
frame_buffer = newRttProvider->render(camera,
|
||||
GUIEngine::getLatestDt());
|
||||
}
|
||||
else
|
||||
{
|
||||
texture = oldRttProvider->renderToTexture();
|
||||
delete oldRttProvider;
|
||||
}
|
||||
|
||||
cleanupDebugMesh();
|
||||
irr_driver->removeCameraSceneNode(camera);
|
||||
|
||||
if (texture == NULL && frame_buffer == NULL)
|
||||
{
|
||||
Log::error("Graph", "[makeMiniMap] WARNING: RTT does not"
|
||||
"appear to work, mini-map will not be available.");
|
||||
}
|
||||
|
||||
*oldRttMinimap = texture;
|
||||
*newRttMinimap = frame_buffer;
|
||||
World::getWorld()->setClearbackBufferColor(oldClearColor);
|
||||
World::getWorld()->forceFogDisabled(false);
|
||||
|
||||
irr_driver->getSceneManager()->clear();
|
||||
VAOManager::kill();
|
||||
irr_driver->clearGlowingNodes();
|
||||
irr_driver->clearLights();
|
||||
irr_driver->clearForcedBloom();
|
||||
irr_driver->clearBackgroundNodes();
|
||||
} // makeMiniMap
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Returns the 2d coordinates of a point when drawn on the mini map
|
||||
* texture.
|
||||
* \param xyz Coordinates of the point to map.
|
||||
* \param draw_at The coordinates in pixel on the mini map of the point,
|
||||
* only the first two coordinates will be used.
|
||||
*/
|
||||
void Graph::mapPoint2MiniMap(const Vec3 &xyz,Vec3 *draw_at) const
|
||||
{
|
||||
draw_at->setX((xyz.getX()-m_bb_min.getX())*m_scaling);
|
||||
draw_at->setY((xyz.getZ()-m_bb_min.getZ())*m_scaling);
|
||||
|
||||
} // mapPoint
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
|
||||
const Vec3 &p3, unsigned int node_index,
|
||||
bool invisible, bool ai_ignore, bool is_arena)
|
||||
{
|
||||
// Find the normal of this quad by computing the normal of two triangles
|
||||
// and taking their average.
|
||||
core::triangle3df tri1(p0.toIrrVector(), p1.toIrrVector(),
|
||||
p2.toIrrVector());
|
||||
core::triangle3df tri2(p0.toIrrVector(), p2.toIrrVector(),
|
||||
p3.toIrrVector());
|
||||
Vec3 normal1 = tri1.getNormal();
|
||||
Vec3 normal2 = tri2.getNormal();
|
||||
Vec3 normal = -0.5f * (normal1 + normal2);
|
||||
normal.normalize();
|
||||
|
||||
// Use the angle between the normal and an up vector to choose 3d/2d quad
|
||||
const float angle = normal.angle(Vec3(0, 1, 0));
|
||||
|
||||
Quad* q = NULL;
|
||||
if (angle > 0.5f)
|
||||
{
|
||||
Log::debug("Graph", "3d node created, normal: %f, %f, %f",
|
||||
normal.x(), normal.y(), normal.z());
|
||||
if (is_arena)
|
||||
{
|
||||
q = new ArenaNode3D(p0, p1, p2, p3, normal, node_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
q = new Node3D(p0, p1, p2, p3, normal, node_index, invisible,
|
||||
ai_ignore);
|
||||
}
|
||||
}
|
||||
|
||||
Log::debug("Graph", "2d node created, normal: %f, %f, %f",
|
||||
normal.x(), normal.y(), normal.z());
|
||||
if (is_arena)
|
||||
{
|
||||
q = new ArenaNode(p0, p1, p2, p3, normal, node_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
q = new Node2D(p0, p1, p2, p3, normal, node_index, invisible,
|
||||
ai_ignore);
|
||||
}
|
||||
m_all_nodes.push_back(q);
|
||||
|
||||
} // createQuad
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** findRoadSector returns in which sector on the road the position
|
||||
* xyz is. If xyz is not on top of the road, it sets UNKNOWN_SECTOR as sector.
|
||||
*
|
||||
* \param xyz Position for which the segment should be determined.
|
||||
* \param sector Contains the previous sector (as a shortcut, since usually
|
||||
* the sector is the same as the last one), and on return the result
|
||||
* \param all_sectors If this is not NULL, it is a list of all sectors to
|
||||
* test. This is used by the AI to make sure that it ends up on the
|
||||
* selected way in case of a branch, and also to make sure that it
|
||||
* doesn't skip e.g. a loop (see explanation below for details).
|
||||
*/
|
||||
void Graph::findRoadSector(const Vec3& xyz, int *sector,
|
||||
std::vector<int> *all_sectors,
|
||||
bool ignore_vertical) const
|
||||
{
|
||||
// Most likely the kart will still be on the sector it was before,
|
||||
// so this simple case is tested first.
|
||||
if (*sector!=UNKNOWN_SECTOR &&
|
||||
getQuad(*sector)->pointInside(xyz, ignore_vertical))
|
||||
{
|
||||
return;
|
||||
} // if still on same quad
|
||||
|
||||
// Now we search through all quads, starting with
|
||||
// the current one
|
||||
int indx = *sector;
|
||||
|
||||
// If a current sector is given, and max_lookahead is specify, only test
|
||||
// the next max_lookahead quads instead of testing the whole graph.
|
||||
// This is necessary for the AI: if the track contains a loop, e.g.:
|
||||
// -A--+---B---+----F--------
|
||||
// E C
|
||||
// +---D---+
|
||||
// and the track is supposed to be driven: ABCDEBF, the AI might find
|
||||
// the quad on F, and then keep on going straight ahead instead of
|
||||
// using the loop at all.
|
||||
unsigned int max_count = (*sector!=UNKNOWN_SECTOR && all_sectors!=NULL)
|
||||
? (unsigned int)all_sectors->size()
|
||||
: (unsigned int)m_all_nodes.size();
|
||||
*sector = UNKNOWN_SECTOR;
|
||||
for(unsigned int i=0; i<max_count; i++)
|
||||
{
|
||||
if(all_sectors)
|
||||
indx = (*all_sectors)[i];
|
||||
else
|
||||
indx = indx<(int)m_all_nodes.size()-1 ? indx +1 : 0;
|
||||
const Quad* q = getQuad(indx);
|
||||
if(q->pointInside(xyz, ignore_vertical))
|
||||
{
|
||||
*sector = indx;
|
||||
}
|
||||
} // for i<m_all_nodes.size()
|
||||
|
||||
return;
|
||||
} // findRoadSector
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** findOutOfRoadSector finds the sector where XYZ is, but as it name
|
||||
implies, it is more accurate for the outside of the track than the
|
||||
inside, and for STK's needs the accuracy on top of the track is
|
||||
unacceptable; but if this was a 2D function, the accuracy for out
|
||||
of road sectors would be perfect.
|
||||
|
||||
To find the sector we look for the closest line segment from the
|
||||
right and left drivelines, and the number of that segment will be
|
||||
the sector.
|
||||
|
||||
The SIDE argument is used to speed up the function only; if we know
|
||||
that XYZ is on the left or right side of the track, we know that
|
||||
the closest driveline must be the one that matches that condition.
|
||||
In reality, the side used in STK is the one from the previous frame,
|
||||
but in order to move from one side to another a point would go
|
||||
through the middle, that is handled by findRoadSector() which doesn't
|
||||
has speed ups based on the side.
|
||||
|
||||
NOTE: This method of finding the sector outside of the road is *not*
|
||||
perfect: if two line segments have a similar altitude (but enough to
|
||||
let a kart get through) and they are very close on a 2D system,
|
||||
if a kart is on the air it could be closer to the top line segment
|
||||
even if it is supposed to be on the sector of the lower line segment.
|
||||
Probably the best solution would be to construct a quad that reaches
|
||||
until the next higher overlapping line segment, and find the closest
|
||||
one to XYZ.
|
||||
*/
|
||||
int Graph::findOutOfRoadSector(const Vec3& xyz, const int curr_sector,
|
||||
std::vector<int> *all_sectors,
|
||||
bool ignore_vertical) const
|
||||
{
|
||||
int count = (all_sectors!=NULL) ? (int)all_sectors->size() : getNumNodes();
|
||||
int current_sector = 0;
|
||||
if(curr_sector != UNKNOWN_SECTOR && !all_sectors)
|
||||
{
|
||||
// We have to test all quads here: reason is that on track with
|
||||
// shortcuts the n quads of the main drivelines is followed by
|
||||
// the quads of the shortcuts. So after quad n-1 (the last one
|
||||
// before the lap counting line) quad n will not be 0 (the first
|
||||
// quad after the lap counting line), but one of the quads on a
|
||||
// shortcut. If we only tested a limited number of quads to
|
||||
// improve the performance the crossing of a lap might not be
|
||||
// detected (because quad 0 is not tested, only quads on the
|
||||
// shortcuts are tested). If this should become a performance
|
||||
// bottleneck, we need to set up a graph of 'next' quads for each
|
||||
// quad (similar to what the AI does), and only test the quads
|
||||
// in this graph.
|
||||
const int LIMIT = getNumNodes();
|
||||
count = LIMIT;
|
||||
// Start 10 quads before the current quad, so the quads closest
|
||||
// to the current position are tested first.
|
||||
current_sector = curr_sector -10;
|
||||
if(current_sector<0) current_sector += getNumNodes();
|
||||
}
|
||||
|
||||
int min_sector = UNKNOWN_SECTOR;
|
||||
float min_dist_2 = 999999.0f*999999.0f;
|
||||
|
||||
// If a kart is falling and in between (or too far below)
|
||||
// a driveline point it might not fulfill
|
||||
// the height condition. So we run the test twice: first with height
|
||||
// condition, then again without the height condition - just to make sure
|
||||
// it always comes back with some kind of quad.
|
||||
for(int phase=0; phase<2; phase++)
|
||||
{
|
||||
for(int j=0; j<count; j++)
|
||||
{
|
||||
int next_sector;
|
||||
if(all_sectors)
|
||||
next_sector = (*all_sectors)[j];
|
||||
else
|
||||
next_sector = current_sector+1 == (int)getNumNodes()
|
||||
? 0
|
||||
: current_sector+1;
|
||||
|
||||
// A first simple test uses the 2d distance to the center of the
|
||||
// quad.
|
||||
float dist_2 =
|
||||
m_all_nodes[next_sector]->getDistance2FromPoint(xyz);
|
||||
if(dist_2<min_dist_2)
|
||||
{
|
||||
const Quad* q = getQuad(next_sector);
|
||||
float dist = xyz.getY() - q->getMinHeight();
|
||||
// While negative distances are unlikely, we allow some small
|
||||
// negative numbers in case that the kart is partly in the
|
||||
// track. Only do the height test in phase==0, in phase==1
|
||||
// accept any point, independent of height, or this node is 3d
|
||||
// which already takes height into account
|
||||
if(phase==1 || (dist < 5.0f && dist>-1.0f) ||
|
||||
q->is3DQuad() || ignore_vertical)
|
||||
{
|
||||
min_dist_2 = dist_2;
|
||||
min_sector = next_sector;
|
||||
}
|
||||
}
|
||||
current_sector = next_sector;
|
||||
} // for j
|
||||
// Leave in phase 0 if any sector was found.
|
||||
if(min_sector!=UNKNOWN_SECTOR)
|
||||
return min_sector;
|
||||
} // phase
|
||||
|
||||
if(min_sector==UNKNOWN_SECTOR)
|
||||
{
|
||||
Log::info("Graph", "unknown sector found.");
|
||||
}
|
||||
return min_sector;
|
||||
} // findOutOfRoadSector
|
||||
|
||||
//-----------------------------------------------------------------------------
|
148
src/tracks/graph.hpp
Normal file
148
src/tracks/graph.hpp
Normal file
@ -0,0 +1,148 @@
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2016 SuperTuxKart Team
|
||||
//
|
||||
// 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; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; 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.
|
||||
|
||||
#ifndef HEADER_GRAPH_HPP
|
||||
#define HEADER_GRAPH_HPP
|
||||
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
#include <dimension2d.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; }
|
||||
namespace video { class ITexture; struct S3DVertex; class SColor; }
|
||||
}
|
||||
|
||||
using namespace irr;
|
||||
|
||||
class FrameBuffer;
|
||||
class Quad;
|
||||
class RTT;
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class Graph : public NoCopy
|
||||
{
|
||||
protected:
|
||||
static Graph* m_graph;
|
||||
|
||||
std::vector<Quad*> m_all_nodes;
|
||||
|
||||
/** The 2d bounding box, used for hashing. */
|
||||
Vec3 m_bb_min;
|
||||
|
||||
Vec3 m_bb_max;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Factory method to dynamic create 2d / 3d quad. */
|
||||
void createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2,
|
||||
const Vec3 &p3, unsigned int node_index,
|
||||
bool invisible, bool ai_ignore, bool is_arena);
|
||||
|
||||
private:
|
||||
RTT* m_new_rtt;
|
||||
|
||||
/** The node of the graph mesh. */
|
||||
scene::ISceneNode *m_node;
|
||||
|
||||
/** The mesh of the graph mesh. */
|
||||
scene::IMesh *m_mesh;
|
||||
|
||||
/** The actual mesh buffer storing the graph. */
|
||||
scene::IMeshBuffer *m_mesh_buffer;
|
||||
|
||||
/** Scaling for mini map. */
|
||||
float m_scaling;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void createMesh(bool show_invisible=true,
|
||||
bool enable_transparency=false,
|
||||
const video::SColor *track_color=NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
void cleanupDebugMesh();
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool hasLapLine() const = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void differentNodeColor(int n, video::SColor* c) const = 0;
|
||||
|
||||
public:
|
||||
static const int UNKNOWN_SECTOR;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the one instance of this object. It is possible that there
|
||||
* is no instance created (e.g. battle mode without navmesh) so we don't
|
||||
* assert that an instance exist. */
|
||||
static Graph* get() { return m_graph; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set the graph (either driveline or arena graph for now). */
|
||||
static void setGraph(Graph* graph)
|
||||
{
|
||||
assert(m_graph == NULL);
|
||||
m_graph = graph;
|
||||
} // create
|
||||
// ------------------------------------------------------------------------
|
||||
/** Cleans up the graph. It is possible that this function is called even
|
||||
* if no instance exists (e.g. in battle mode without navmesh). So it is
|
||||
* not an error if there is no instance. */
|
||||
static void destroy()
|
||||
{
|
||||
if (m_graph)
|
||||
{
|
||||
delete m_graph;
|
||||
m_graph = NULL;
|
||||
}
|
||||
} // destroy
|
||||
// ------------------------------------------------------------------------
|
||||
Graph();
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~Graph();
|
||||
// ------------------------------------------------------------------------
|
||||
void createDebugMesh();
|
||||
// ------------------------------------------------------------------------
|
||||
void makeMiniMap(const core::dimension2du &where, const std::string &name,
|
||||
const video::SColor &fill_color,
|
||||
video::ITexture** oldRttMinimap,
|
||||
FrameBuffer** newRttMinimap);
|
||||
// ------------------------------------------------------------------------
|
||||
void mapPoint2MiniMap(const Vec3 &xyz, Vec3 *out) const;
|
||||
// ------------------------------------------------------------------------
|
||||
Quad* getQuad(unsigned int i) const
|
||||
{
|
||||
assert(i < m_all_nodes.size());
|
||||
return m_all_nodes[i];
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int getNumNodes() const { return m_all_nodes.size(); }
|
||||
// ------------------------------------------------------------------------
|
||||
void findRoadSector(const Vec3& XYZ, int *sector,
|
||||
std::vector<int> *all_sectors = NULL,
|
||||
bool ignore_vertical = false) const;
|
||||
// ------------------------------------------------------------------------
|
||||
int findOutOfRoadSector(const Vec3& xyz,
|
||||
const int curr_sector = UNKNOWN_SECTOR,
|
||||
std::vector<int> *all_sectors = NULL,
|
||||
bool ignore_vertical = false) const;
|
||||
|
||||
}; // Graph
|
||||
|
||||
#endif
|
@ -210,8 +210,6 @@ public:
|
||||
/** True if this node should be ignored by the AI. */
|
||||
bool letAIIgnore() const { return m_ai_ignore; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getDistance2FromPoint(const Vec3 &xyz) const = 0;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void getDistances(const Vec3 &xyz, Vec3 *result) const = 0;
|
||||
|
||||
}; // GraphNode
|
||||
|
@ -24,35 +24,7 @@ Node3D::Node3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
|
||||
bool ai_ignore)
|
||||
: GraphNode(p0, p1, p2, p3, normal, node_index, invisible, ai_ignore)
|
||||
{
|
||||
// Compute the node bounding box used for pointInNode
|
||||
Vec3 box_corners[8];
|
||||
float box_high = 5.0f;
|
||||
float box_low = 1.0f;
|
||||
box_corners[0] = m_p[0] + box_high * normal;
|
||||
box_corners[1] = m_p[1] + box_high * normal;
|
||||
box_corners[2] = m_p[2] + box_high * normal;
|
||||
box_corners[3] = m_p[3] + box_high * normal;
|
||||
box_corners[4] = m_p[0] - box_low * normal;
|
||||
box_corners[5] = m_p[1] - box_low * normal;
|
||||
box_corners[6] = m_p[2] - box_low * normal;
|
||||
box_corners[7] = m_p[3] - box_low * normal;
|
||||
|
||||
Vec3 box_faces[6][4] =
|
||||
{
|
||||
{ box_corners[0], box_corners[1], box_corners[2], box_corners[3] },
|
||||
{ box_corners[3], box_corners[2], box_corners[6], box_corners[7] },
|
||||
{ box_corners[7], box_corners[6], box_corners[5], box_corners[4] },
|
||||
{ box_corners[1], box_corners[0], box_corners[4], box_corners[5] },
|
||||
{ box_corners[4], box_corners[0], box_corners[3], box_corners[7] },
|
||||
{ box_corners[1], box_corners[5], box_corners[6], box_corners[2] }
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < 6 ; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < 4; j++)
|
||||
m_box_faces[i][j] = box_faces[i][j];
|
||||
}
|
||||
|
||||
BoundingBox3D::init(p0, p1, p2, p3, normal);
|
||||
m_line = core::line3df(m_lower_center.toIrrVector(),
|
||||
m_upper_center.toIrrVector());
|
||||
} // Node3D
|
||||
|
@ -19,19 +19,17 @@
|
||||
#ifndef HEADER_NODE_3D_HPP
|
||||
#define HEADER_NODE_3D_HPP
|
||||
|
||||
#include "tracks/bounding_box_3d.hpp"
|
||||
#include "tracks/graph_node.hpp"
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
/**
|
||||
* \ingroup tracks
|
||||
*/
|
||||
class Node3D : public GraphNode
|
||||
class Node3D : public GraphNode,
|
||||
public BoundingBox3D
|
||||
{
|
||||
private:
|
||||
/** For each node, construct a 3D box to check if a point lies inside it.
|
||||
*/
|
||||
Vec3 m_box_faces[6][4];
|
||||
|
||||
/** Line between lower and upper center, saves computation in
|
||||
* getDistance() later.
|
||||
*/
|
||||
@ -45,15 +43,7 @@ public:
|
||||
virtual bool pointInside(const Vec3& p,
|
||||
bool ignore_vertical = false) const OVERRIDE
|
||||
{
|
||||
float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1],
|
||||
m_box_faces[0][2]);
|
||||
for (int i = 1; i < 6; i++)
|
||||
{
|
||||
if (side * p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1],
|
||||
m_box_faces[i][2]) < 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return BoundingBox3D::pointInside(p);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE;
|
||||
|
@ -24,7 +24,9 @@
|
||||
#include <triangle3d.h>
|
||||
|
||||
/** Constructor, takes 4 points. */
|
||||
Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3)
|
||||
Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
|
||||
const Vec3 &normal, int index, bool invisible)
|
||||
: m_index(index), m_normal(normal), m_invisible(invisible)
|
||||
{
|
||||
m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3;
|
||||
|
||||
|
@ -44,7 +44,16 @@ protected:
|
||||
* This saves some computations at runtime. */
|
||||
Vec3 m_center;
|
||||
|
||||
/** Index of this quad, used only with graph. */
|
||||
int m_index;
|
||||
|
||||
/** Normal of the quad */
|
||||
Vec3 m_normal;
|
||||
|
||||
private:
|
||||
/** Set to true if this quad should not be shown in the minimap. */
|
||||
bool m_invisible;
|
||||
|
||||
/** The minimum height of the quad, used in case that several quads
|
||||
* are on top of each other when determining the sector a kart is on. */
|
||||
float m_min_height;
|
||||
@ -56,9 +65,11 @@ private:
|
||||
public:
|
||||
LEAK_CHECK()
|
||||
// ------------------------------------------------------------------------
|
||||
Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3);
|
||||
Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
|
||||
const Vec3 & normal = Vec3(0, 1, 0), int index = -1,
|
||||
bool invisible = false);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~Quad() {}
|
||||
virtual ~Quad() {}
|
||||
// ------------------------------------------------------------------------
|
||||
void getVertices(video::S3DVertex *v, const video::SColor &color) const;
|
||||
// ------------------------------------------------------------------------
|
||||
@ -69,10 +80,37 @@ public:
|
||||
const Vec3& getCenter () const { return m_center; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the minimum height of a quad. */
|
||||
float getMinHeight() const { return m_min_height; }
|
||||
float getMinHeight() const { return m_min_height; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the index of this quad. */
|
||||
int getIndex() const
|
||||
{
|
||||
// You should not call this if it has default value (like slipstream)
|
||||
assert(m_index != -1);
|
||||
return m_index;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true of this quad is invisible, i.e. not to be shown in
|
||||
* the minimap. */
|
||||
bool isInvisible() const { return m_invisible; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the normal of this quad. */
|
||||
const Vec3& getNormal() const { return m_normal; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if a point is inside this quad. */
|
||||
virtual bool pointInside(const Vec3& p,
|
||||
bool ignore_vertical = false) const;
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns true if this quad is 3D, which additional 3D testing is used in
|
||||
* pointInside. */
|
||||
virtual bool is3DQuad() const { return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual float getDistance2FromPoint(const Vec3 &xyz) const
|
||||
{
|
||||
// You should not call this in a bare quad
|
||||
assert(false);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
}; // class Quad
|
||||
#endif
|
||||
|
@ -53,6 +53,7 @@
|
||||
#include "physics/triangle_mesh.hpp"
|
||||
#include "race/race_manager.hpp"
|
||||
#include "scriptengine/script_engine.hpp"
|
||||
#include "tracks/arena_graph.hpp"
|
||||
#include "tracks/bezier_curve.hpp"
|
||||
#include "tracks/battle_graph.hpp"
|
||||
#include "tracks/check_manager.hpp"
|
||||
@ -668,12 +669,14 @@ void Track::startMusic() const
|
||||
} // startMusic
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Loads the polygon graph for battle, i.e. the definition of all polys, and the way
|
||||
* they are connected to each other. Input file name is hardcoded for now
|
||||
/** Loads the quad graph for arena, i.e. the definition of all quads, and the
|
||||
* way they are connected to each other. Input file name is hardcoded for now
|
||||
*/
|
||||
void Track::loadBattleGraph(const XMLNode &node)
|
||||
{
|
||||
BattleGraph::create(m_root+"navmesh.xml", &node);
|
||||
ArenaGraph* graph = new ArenaGraph(m_root+"navmesh.xml", &node);
|
||||
Graph::setGraph(graph);
|
||||
|
||||
if(BattleGraph::get()->getNumNodes()==0)
|
||||
{
|
||||
@ -725,7 +728,7 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse)
|
||||
void Track::mapPoint2MiniMap(const Vec3 &xyz, Vec3 *draw_at) const
|
||||
{
|
||||
if ((m_is_arena || m_is_soccer) && m_has_navmesh)
|
||||
BattleGraph::get()->mapPoint2MiniMap(xyz, draw_at);
|
||||
Graph::get()->mapPoint2MiniMap(xyz, draw_at);
|
||||
else
|
||||
QuadGraph::get()->mapPoint2MiniMap(xyz, draw_at);
|
||||
draw_at->setX(draw_at->getX() * m_minimap_x_scale);
|
||||
@ -1032,7 +1035,7 @@ void Track::loadMinimap()
|
||||
|
||||
if ((m_is_arena || m_is_soccer) && m_has_navmesh)
|
||||
{
|
||||
BattleGraph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255),
|
||||
Graph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255),
|
||||
&m_old_rtt_mini_map, &m_new_rtt_mini_map);
|
||||
}
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user