Panda3D
pathFind.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file pathFind.cxx
10  * @author Deepak, John, Navin
11  * @date 2009-10-12
12  */
13 
14 #include "pathFind.h"
15 
16 using std::cout;
17 using std::endl;
18 using std::string;
19 
20 PathFind::PathFind(AICharacter *ai_ch) {
21  _ai_char = ai_ch;
22 
23  _parent = new GeomNode("parent");
24  _ai_char->_window_render.attach_new_node(_parent);
25 
26  _pen = new LineSegs("pen");
27  _pen->set_color(1.0, 0.0, 0.0);
28  _pen->set_thickness(2.0);
29 
30  _path_finder_obj = nullptr;
31  _dynamic_avoid = false;
32 }
33 
34 PathFind::~PathFind() {
35 }
36 
37 /**
38  * This function recreates the navigation mesh from the .csv file
39  */
40 void PathFind::create_nav_mesh(const char* navmesh_filename) {
41  // Stage variables.
42  int grid_x, grid_y;
43  float l, w, h;
44  LVecBase3 position;
45 
46  // Variable to hold line data read from file.
47  string line;
48 
49  // Array for storing data members obtained from each line of the file.
50  string fields[10];
51 
52  // Open data file for reading.
53  std::ifstream nav_mesh_file (navmesh_filename);
54 
55  if(nav_mesh_file.is_open()) {
56  // Capture the grid size from the file.
57  getline(nav_mesh_file, line);
58  int pos = line.find(",");
59  _grid_size = atoi((line.substr(pos + 1)).c_str());
60 
61  // Initialize the stage mesh with NULL nodes.
62  for(int r = 0; r < _grid_size; ++r) {
63  _nav_mesh.push_back(std::vector<Node*>());
64  for(int c = 0; c < _grid_size; ++c) {
65  _nav_mesh[r].push_back(nullptr);
66  }
67  }
68 
69  // Ignore the header of the navmesh.csv file.
70  getline(nav_mesh_file, line);
71 
72  // Begin reading data from the file.
73  while(!nav_mesh_file.eof()) {
74  getline(nav_mesh_file, line);
75  std::stringstream linestream (line);
76 
77  // Stores all the data members in the line to the array. Data
78  // structure:
79  // NULL,NodeType,GridX,GridY,Length,Width,Height,PosX,PosY,PosZ
80  for(int i = 0; i < 10; ++i) {
81  getline(linestream, fields[i], ',');
82  }
83 
84  // Populate the main nodes into stage mesh.
85  if(fields[0] == "0" && fields[1] == "0") {
86  grid_x = atoi(fields[2].c_str());
87  grid_y = atoi(fields[3].c_str());
88  l = atof(fields[4].c_str());
89  w = atof(fields[5].c_str());
90  h = atof(fields[6].c_str());
91  position = LVecBase3(atof(fields[7].c_str()), atof(fields[8].c_str()), atof(fields[9].c_str()));
92 
93  Node *stage_node = new Node(grid_x, grid_y, position, w, l, h);
94 
95 
96  _nav_mesh[grid_y][grid_x] = stage_node;
97  }
98  else if(fields[0] == "") {
99  // End of file reached at this point.
100  nav_mesh_file.close();
101 
102  // Assign the neighbor nodes for each of the main nodes that just got
103  // populated into the stage mesh.
104  assign_neighbor_nodes(navmesh_filename);
105  }
106  }
107  }
108  else {
109  cout<<"error opening navmesh.csv file!"<<endl;
110  }
111 }
112 
113 /**
114  * This function assigns the neighbor nodes for each main node present in
115  * _nav_mesh.
116  */
117 void PathFind::assign_neighbor_nodes(const char* navmesh_filename){
118  std::ifstream nav_mesh_file (navmesh_filename);
119 
120  // Stage variables.
121  int gd_x, gd_y, gd_xn, gd_yn;
122  string ln;
123  string fields[10];
124  string fields_n[10];
125 
126  if(nav_mesh_file.is_open()) {
127  getline(nav_mesh_file, ln); // Get rid of grid size line.
128  getline(nav_mesh_file, ln); // Get rid of the header.
129 
130  while(!nav_mesh_file.eof()) {
131  getline(nav_mesh_file, ln); // Gets main node data only. No neighbor nodes.
132  std::stringstream linestream (ln);
133  for(int i = 0; i < 10; ++i) {
134  getline(linestream, fields[i], ',');
135  }
136  if(fields[0] == "0" && fields[1] == "0") {
137  // Usable main node.
138  gd_x = atoi(fields[2].c_str());
139  gd_y = atoi(fields[3].c_str());
140  for(int i = 0; i < 8; ++i) {
141  getline(nav_mesh_file, ln); // Gets neighbor node data only. No main nodes.
142  std::stringstream linestream_n (ln);
143  for(int j = 0; j < 10; ++j) {
144  getline(linestream_n, fields_n[j], ',');
145  }
146  gd_xn = atoi(fields_n[2].c_str());
147  gd_yn = atoi(fields_n[3].c_str());
148 
149  if(fields_n[0] == "0" && fields_n[1] == "1") {
150  // Usable neighbor for main node. TODO: The indices of the vector
151  // are inverted when compared to the values of the nodes on actual
152  // grid. Fix this!
153  _nav_mesh[gd_y][gd_x]->_neighbours[i] = _nav_mesh[gd_yn][gd_xn];
154  }
155  else if(fields_n[0] == "1" && fields_n[1] == "1") {
156  // NULL neighbor.
157  _nav_mesh[gd_y][gd_x]->_neighbours[i] = nullptr;
158  }
159  else {
160  cout<<"Warning: Corrupt data!"<<endl;
161  }
162  }
163  }
164  else if(fields[0] == "") {
165  // End of file reached at this point.
166  nav_mesh_file.close();
167  }
168  }
169  }
170  else {
171  cout<<"error opening navmesh.csv file!"<<endl;
172  }
173 }
174 
175 /**
176  * This function starts the path finding process after reading the given
177  * navigation mesh.
178  */
179 void PathFind::set_path_find(const char* navmesh_filename) {
180  create_nav_mesh(navmesh_filename);
181 
182  if(_ai_char->_steering->_path_follow_obj) {
183  _ai_char->_steering->remove_ai("pathfollow");
184  }
185 
186  _ai_char->_steering->path_follow(1.0f);
187 
188  if(_path_finder_obj) {
189  delete _path_finder_obj;
190  _path_finder_obj = nullptr;
191  }
192 
193  _path_finder_obj = new PathFinder(_nav_mesh);
194 }
195 
196 /**
197  * This function checks for the source and target in the navigation mesh for
198  * its availability and then finds the best path via the A* algorithm Then it
199  * calls the path follower to make the object follow the path.
200  */
201 void PathFind::path_find(LVecBase3 pos, string type) {
202  if(type == "addPath") {
203  if(_ai_char->_steering->_path_follow_obj) {
204  _ai_char->_steering->remove_ai("pathfollow");
205  }
206 
207  _ai_char->_steering->path_follow(1.0f);
208  }
209 
210  clear_path();
211 
212  Node* src = find_in_mesh(_nav_mesh, _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), _grid_size);
213 
214  if(src == nullptr) {
215  cout<<"couldnt find source"<<endl;
216  }
217 
218  Node* dst = find_in_mesh(_nav_mesh, pos, _grid_size);
219 
220  if(dst == nullptr) {
221  cout<<"couldnt find destination"<<endl;
222  }
223 
224  if(src != nullptr && dst != nullptr) {
225  _path_finder_obj->find_path(src, dst);
226  trace_path(src);
227  }
228 
229  if(!_ai_char->_steering->_path_follow_obj->_start) {
230  _ai_char->_steering->start_follow();
231  }
232 }
233 
234 /**
235  * This function checks for the source and target in the navigation mesh for
236  * its availability and then finds the best path via the A* algorithm Then it
237  * calls the path follower to make the object follow the path.
238  */
239 void PathFind::path_find(NodePath target, string type) {
240  if(type == "addPath") {
241  if(_ai_char->_steering->_path_follow_obj) {
242  _ai_char->_steering->remove_ai("pathfollow");
243  }
244 
245  _ai_char->_steering->path_follow(1.0f);
246  }
247 
248  clear_path();
249 
250  _path_find_target = target;
251  _prev_position = target.get_pos(_ai_char->_window_render);
252 
253  Node* src = find_in_mesh(_nav_mesh, _ai_char->_ai_char_np.get_pos(_ai_char->_window_render), _grid_size);
254 
255  if(src == nullptr) {
256  cout<<"couldnt find source"<<endl;
257  }
258 
259  Node* dst = find_in_mesh(_nav_mesh, _prev_position, _grid_size);
260 
261  if(dst == nullptr) {
262  cout<<"couldnt find destination"<<endl;
263  }
264 
265  if(src != nullptr && dst != nullptr) {
266  _path_finder_obj->find_path(src, dst);
267  trace_path(src);
268  }
269 
270  if(_ai_char->_steering->_path_follow_obj!=nullptr) {
271  if(!_ai_char->_steering->_path_follow_obj->_start) {
272  _ai_char->_steering->start_follow("pathfind");
273  }
274  }
275 }
276 
277 /**
278  * Helper function to restore the path and mesh to its initial state
279  */
281  // Initialize to zero
282  for(int i = 0; i < _grid_size; ++i) {
283  for(int j = 0; j < _grid_size; ++j) {
284  if(_nav_mesh[i][j] != nullptr) {
285  _nav_mesh[i][j]->_status = _nav_mesh[i][j]->neutral;
286  _nav_mesh[i][j]->_cost = 0;
287  _nav_mesh[i][j]->_heuristic = 0;
288  _nav_mesh[i][j]->_score = 0;
289  _nav_mesh[i][j]->_prv_node = nullptr;
290  }
291  }
292  }
293 
294  if(_path_finder_obj) {
295  _path_finder_obj->_open_list.clear();
296  _path_finder_obj->_closed_list.clear();
297  }
298 }
299 
300 /**
301  * This function is the function which sends the path information one by one
302  * to the path follower so that it can store the path needed to be traversed
303  * by the pathfinding object
304  */
306  if(_ai_char->_pf_guide) {
307  _parent->remove_all_children();
308  }
309  else {
310  _parent->remove_all_children();
311  }
312 
313  if(_path_finder_obj->_closed_list.size() > 0) {
314  Node *traversor = _path_finder_obj->_closed_list[_path_finder_obj->_closed_list.size() - 0.5];
315  while(traversor != src) {
316  if(_ai_char->_pf_guide) {
317  _pen->move_to(traversor->_position.get_x(), traversor->_position.get_y(), 1);
318  _pen->draw_to(traversor->_prv_node->_position.get_x(), traversor->_prv_node->_position.get_y(), 0.5);
319  PT(GeomNode) gnode = _pen->create();
320  _parent->add_child(gnode);
321  }
322  _ai_char->_steering->add_to_path(traversor->_position);
323  traversor = traversor->_prv_node;
324  }
325  }
326 }
327 
328 /**
329  * This function allows the user to dynamically add obstacles to the game
330  * environment. The function will update the nodes within the bounding volume
331  * of the obstacle as non-traversable. Hence will not be considered by the
332  * pathfinding algorithm.
333  */
335  PT(BoundingVolume) np_bounds = obstacle.get_bounds();
336  CPT(BoundingSphere) np_sphere = np_bounds->as_bounding_sphere();
337 
338  Node* temp = find_in_mesh(_nav_mesh, obstacle.get_pos(), _grid_size);
339 
340  if(temp != nullptr) {
341  float left = temp->_position.get_x() - np_sphere->get_radius();
342  float right = temp->_position.get_x() + np_sphere->get_radius();
343  float top = temp->_position.get_y() + np_sphere->get_radius();
344  float down = temp->_position.get_y() - np_sphere->get_radius();
345 
346  for(int i = 0; i < _grid_size; ++i) {
347  for(int j = 0; j < _grid_size; ++j) {
348  if(_nav_mesh[i][j] != nullptr && _nav_mesh[i][j]->_type == true) {
349  if(_nav_mesh[i][j]->_position.get_x() >= left && _nav_mesh[i][j]->_position.get_x() <= right &&
350  _nav_mesh[i][j]->_position.get_y() >= down && _nav_mesh[i][j]->_position.get_y() <= top) {
351  _nav_mesh[i][j]->_type = false;
352  _previous_obstacles.insert(_previous_obstacles.end(), i);
353  _previous_obstacles.insert(_previous_obstacles.end(), j);
354  }
355  }
356  }
357  }
358  }
359 }
360 
361 /**
362  * This function does the updation of the collisions to the mesh based on the
363  * new positions of the obstacles.
364  */
367  _previous_obstacles.clear();
368  for(unsigned int i = 0; i < _dynamic_obstacle.size(); ++i) {
369  add_obstacle_to_mesh(_dynamic_obstacle[i]);
370  }
371 }
372 
373 /**
374  * Helper function to reset the collisions if the obstacle is not on the node
375  * anymore
376  */
378  for(unsigned int i = 0; i < _previous_obstacles.size(); i = i + 2) {
379  _nav_mesh[_previous_obstacles[i]][_previous_obstacles[i + 1]]->_type = true;
380  }
381 }
382 
383 /**
384  * This function starts the pathfinding obstacle navigation for the passed in
385  * obstacle.
386  */
388  _dynamic_avoid = true;
389  _dynamic_obstacle.insert(_dynamic_obstacle.end(), obstacle);
390 }
void clear_path()
Helper function to restore the path and mesh to its initial state.
Definition: pathFind.cxx:280
void clear_previous_obstacles()
Helper function to reset the collisions if the obstacle is not on the node anymore.
Definition: pathFind.cxx:377
void add_to_path(LVecBase3 pos)
This function adds positions to the path to follow.
Node * find_in_mesh(NavMesh nav_mesh, LVecBase3 pos, int grid_size)
This function allows the user to pass a position and it returns the corresponding node on the navigat...
void dynamic_avoid(NodePath obstacle)
This function starts the pathfinding obstacle navigation for the passed in obstacle.
Definition: pathFind.cxx:387
This class is used to assign the nodes on the mesh.
Definition: meshNode.h:16
This defines a bounding sphere, consisting of a center and a radius.
This class implements pathfinding using A* algorithm.
Definition: aiPathFinder.h:31
void create_nav_mesh(const char *navmesh_filename)
This function recreates the navigation mesh from the .csv file.
Definition: pathFind.cxx:40
GeomNode * create(bool dynamic=false)
Creates a new GeomNode that will render the series of line segments and points described via calls to...
Definition: lineSegs.I:108
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a=1.0f)
Establishes the color that will be assigned to all vertices created by future calls to move_to() and ...
Definition: lineSegs.I:56
void set_thickness(PN_stdfloat thick)
Establishes the line thickness or point size in pixels that will be assigned to all lines and points ...
Definition: lineSegs.I:74
void find_path(Node *src_node, Node *dest_node)
This function initializes the pathfinding process by accepting the source and destination nodes.
void move_to(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Moves the pen to the given point without drawing a line.
Definition: lineSegs.I:84
void remove_ai(std::string ai_type)
This function removes individual or all the AIs.
Encapsulates creation of a series of connected or disconnected line segments or points,...
Definition: lineSegs.h:33
void path_find(LVecBase3 pos, std::string type="normal")
This function checks for the source and target in the navigation mesh for its availability and then f...
Definition: pathFind.cxx:201
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:563
void add_obstacle_to_mesh(NodePath obstacle)
This function allows the user to dynamically add obstacles to the game environment.
Definition: pathFind.cxx:334
void start_follow(std::string type="normal")
This function starts the path follower.
void assign_neighbor_nodes(const char *navmesh_filename)
This function assigns the neighbor nodes for each main node present in _nav_mesh.
Definition: pathFind.cxx:117
LPoint3 get_pos() const
Retrieves the translation component of the transform.
Definition: nodePath.cxx:992
void draw_to(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Draws a line segment from the pen's last position (the last call to move_to or draw_to) to the indica...
Definition: lineSegs.I:94
void trace_path(Node *src)
This function is the function which sends the path information one by one to the path follower so tha...
Definition: pathFind.cxx:305
void path_follow(float follow_wt)
This function activates path following.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void do_dynamic_avoid()
This function does the updation of the collisions to the mesh based on the new positions of the obsta...
Definition: pathFind.cxx:365
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
void set_path_find(const char *navmesh_filename)
This function starts the path finding process after reading the given navigation mesh.
Definition: pathFind.cxx:179