So, today is the last day of this year's Google Summmer of Code (although the date on the post will say it's August 17, it's still August 16 where I am =]). Time surely has flown by! I was really worried at the beginning of the program, there was a lot of code I had to get used to, and I still didn't know exactly how some things had to be done. But in the end, everything worked out, and I believe this project was a great success.
I was able to finish all the tasks that were on the original proposal, with minimal adaptations. I also documented everything as well as I could, both with comments in the code and posting the details in this blog. There are two demo applications as well, to show how the API should be used.
I would like to thank everyone from CrystalSpace for the opportunity, specially my mentor Andrew. People have been very helpful and friendly since our first contacts, during the GSoC applications period. I hope the code produced during this program can be put to good use.
Please feel free to contact me at anytime by mail. I will also stick around the IRC channel.
This last week I have made a few changes to the code, and I thought it would be good to make one post to talk a little about them.
First and most important, I found a problem with the demo application (not exactly a bug, more of a map misbehavior). If you plot a path going from the stairs in the basement01 sector to the basement03 sector, near the basement-dungeon sector, the path is likely to go below the stairs and through a wall. After careful examination, I realized this was actually caused by some parts of navigation meshes that were created below the stairs, due to the level geometry. Since Detour searches for the polygon nearest to the path's starting point, one of the polygons in this disconnected piece of mesh was being chosen, which caused the problem. In order to fix this, I altered the portal's polygon position for the portals that connect basement01 and basement03 in the world xml file, moving them up a little (the portal polygons were going below the stair level).
Here is the modified world file: link.
Second, I changed the DebugRender methods to return a list of csSimpleRenderMesh instead of drawing the structures directly using OpenGL. Notice that now you don't have to (read you shouldn't) call those methods every frame, only when the structure being drawn changes (you have to ask the iGraphics3D plugin to draw the meshes every frame, however).
Finally, I added another hotkey to the pathfindingtest application. Upon pressing 't', a path will be calculated between two predetermined points. This path will then be compared to a path previously calculated, and an error message will be displayed if they differ. If the paths are equal, nothing will happen.
I have added a new parameter to the iCelNavMeshParams class, polygonSearchBox. Here is a detailed description (copied from the parameter values post):
"Before creating a path, Detour finds the closest polygons in the navigation mesh to the origin and destination points. This parameter determines the dimensions of the bounding box where Detour will look for the closest polygons. The bounding box is represented by two vertices: (center + polygonSearchBox) and (center - polygonSearchBox), where center is the point one wants to find the closest polygon to.
This parameter is also used to determine if a low level path that was calculated by Detour reaches it's intended destination (Detour always returns a path, either to the destination or the closest possible point). If the distance between the two points, in each of the coordinates, is less then this parameter, then they are considered to be close enough for this purpose."
One simple way to understand this parameter using the demo application is clicking on a point that is not exactly on the navigation mesh. For example, consider clicking on the walls: the greater the y coordinate of the polygonSearchBox, the higher you can click on the wall and still have the path traced.
If some object changes position in a map, instead of rebuilding the whole navigation structure, one can update it using the iCelHNavStruct::Update() method. This method takes a bounding box and a pointer to a sector as parameters (although the sector is optional): the bounding box describes the area of the navigation mesh that one wishes to update, and the sector determines which sector should be updated. If no sector is specified, then the bounding box will be collided with each of the sectors navigation meshes' bounding boxes to determine what sectors have to be updated.
Note that this should probably not be used for objects that are on constant movement (npcs for example), since it's not very fast. This can be seen on the apppathfindingtest application by pressing 'y' (check last modifications to the demo application on this post).
Although updating a navigation structure takes some time, it is still way faster then rebuilding it.
In order to showcase the updating capabilities of the navigation structure, I have added a large stone block to the initial sector of the path finding demo application. This block moves linearly back and forth between two points near the stairs, and it is taken into account when building the navigation structure.
I also added some new hotkeys to this application, here is a description:
Although building navigation structures only take a couple of seconds (at least for the maps in CrystalSpace), it's important to be able to save and load them to/from files.
Since the navigation structures are composed of one navigation mesh for each sector and some extra information (like the name of the sectors contained in the navstruct and the high level graph), I decided the best way to save them was creating a zip file, containing one file for each navmesh and one file with some general information. These files are all serialized to XML.
To save each navmesh, the iCelNavMesh::SaveToFile() method is used. It receives as a parameter a iFile, which is where it will save the data to. To save navigation structures, iCelHNavStruct::SaveToFile() is used. This is the method that will probably be called by the user. It receives a pointer to the virtual file system and a char array as parameters, and will save the zip file in the current directory of the VFS, with the name described by the char array.
To load navigation structures, iCelHNavStrucBuilder::LoadHNavStruct() is used. It receives a pointer to the VFS and a file name, just like the SaveToFile method, and expects the file to be on the current directory of the iVFS received. It will open the zip file, read the main file and then call iCelNavMeshBuilder::LoadNavMesh() for each navigation mesh. It's important to notice that the load methods are in the builder classes, so the only way to create (either new or load) both navigation meshes and structures is from the builder classes.
In the demo application, loading the navigation mesh for the castle map is significantly faster then building it.
|<< <||Current||> >>|