I've been spending a fair amount of my "wow time" lately designing a navigation system for a bot project I am involved with. I am (hopefully) nearing the end of the development process and thought that the community could benefit from some of the experience I've gained in the process. I have outlined this process below.
A brief outline of our development journey thus far. I realize that some posts are vague and skip a huge chunk of the development cycle. I am attempting to skim through the relatively common sense and/or arbitrary design decisions to move on to the more in-depth ones which offer more potential value to another brave soul taking this journey now or in the future. I understand that this road has probably been traveled by many before me, so if the information presented here is not relevant to you, please feel free to skip it.
- Initially, it was decided that a breadcrumb (waypoint) system was not powerful enough for what we may want to do later. Specifically, our direction may (read: will) change as the world around us changes.
- A navmesh system is the ideal solution, but requires immense amounts of work to do correctly.
- If you consider the phrase "re-inventing the wheel", we have several wheels at our disposal to make things easier on ourselves. The two main operations we will need to do here are reading terrain data from our WoW installation and ultimately creating a mesh of a given region inside the WoW universe. For these two operations, we use StormLib to read the MPQ data and Recast and Detour to generate both our mesh, and for using the mesh to find an arbitrary path.
- The intermediary operation here is creating a parser for the data inside the MPQs (ADTs, WMOs, etc.) and building a composite of all available geometry for a specific region. As far as I know, there is no "wheel" to do this that is both public and functional.
- We ran into our first problem at this stage. WMOs inside a given ADT do not obey the boundaries defined by an ADT. That is, a house or cave can lie on the border between two ADTs. Fortunately, such a WMO is referenced by both ADTs, so we need only trim the WMO and rely on the fact that we will mesh the other half of it when we mesh the neighbor tile. Note that this requires creating some new vertices and triangles as there will undoubtedly be some that cross the boundary. The geometry for this is trivial.
- Detour gave us an option for either a "tiled" mesh or a "static" mesh. A static mesh is used for levels where the terrain information is... (*gasp*)... static! A tile mesh is for levels where terrain information will be added and subtracted. Though our terrain doesn't change much, which meshed tiles we want to have loaded does change depending on our position. Also, we may want more than one tile loaded at once depending on our "patrol area". Note that since we have started this project, the author of Recast and Detour has begun to combine the static and tiled meshes into one structure in his API.
- Detour also gives us an easy way to dump the mesh structure (for future use in path finding) to a file and read in later without concerning ourselves with the geometry used to create it. This means we can dump only the mesh, and not take up extra disk space by saving the source geometry.
- Once you reach this point, it is only a matter of traversing the WoW universe automatically and mesh everything! I say only because the code for this is relatively simple, though it can take 10+ hours to do the whole thing. We had experimented with creating two mesh-generating threads, but we ran into situations where having the right two ADTs loaded uses up more than 2Gb of RAM, so we went back to just one. Since on the disk our data is only coming from a few select files (the MPQs), this also gave us some concurrency issues that just aren't worth it in my opinion.
- When designing your meshing app, it is advisable (and perhaps self-evident) to group all of the MPQ reading, parsing and Recast and Detour functions into a library you can use inside WoW, then make a separate app to do the mesh generation which uses this same lib.
- At this point, you have a mesh that is loadable by your "bot" and can be used to find your way from point A to point B. Note that nothing we have done here thus far requires you to be using injection, but for simplicity sake we assume you are.
- Once we reached this step, we were faced with several design decisions and several potential problems. I am therefore splitting this up into a separate post.
Edit: After checking with them and receiving their blessings, I would like to be specific and say that MaIN was good enough to supply a parser and when I say 'we' I mean Apoc and I.