Inital work on one graph interface

This commit is contained in:
Benau 2016-09-15 11:46:31 +08:00
parent 061f187142
commit 8c830bdabf
14 changed files with 1487 additions and 53 deletions

343
src/tracks/arena_graph.cpp Normal file
View 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
// ----------------------------------------------------------------------------

View 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
View 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
View 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

View 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

View 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
View 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
View 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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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