In most projects, it is expected that you will want to integrate the generation of your NavData directly into your own level design tools or game editor. This integration is to ensure that up-to-date NavData is always generated automatically and rolled into your builds whenever artists or designers make changes to your levels.
This topic introduces the main steps for generating the NavData programmatically.
Follow the instructions given for your runtime game in Integration Phase 1a: Includes, Libraries and Preprocessor Defines. In addition, you must link against the gwnavgeneration library, which is available in the same directories as gwnavruntime.
You must link the libraries to the level editor and game runtime as shown in the following figure:
In this tutorial, the DefaultGeneratorGlue class is used to provide parallel processing services for the generation system. This class relies on the Threaded Building Blocks (TBB) libraries from Intel to distribute calculations across multiple processors. To avoid introducing third-party dependencies within the core libraries of the Gameware Navigation SDK, the headers and source for this class and its related classes are kept in the integration directory. To use DefaultGeneratorGlue, you must:
You must write an implementation of the GeneratorInputProducer class. The purpose of this class is to provide the NavData generation system with the triangles that make up your terrain.
It contains one key virtual method that you must implement: GeneratorInputProducer::Produce(). Each time the generation system generates the NavData for a sector of your terrain, it calls this method passing a description of the sector to be treated and a ClientInputConsumer object that you feed with triangles. Your implementation of GeneratorInputProducer::Produce() can use any data maintained in the GeneratorSector parameter to determine what triangles to feed, including the name or GUID you assigned to the sector when you created it.
For more information about dividing your NavData into sectors, see Sectorizing the Terrain. If you want to create a single sector of NavData that reflects your entire game terrain, which might be the case for your initial integration, your implementation of GeneratorInputProducer::Produce() can simply feed the ClientInputConsumer object with all triangles in your terrain mesh.
#include "gwnavgeneration.h" class MyProducer : public Kaim::GeneratorInputProducer { public: virtual KyResult Produce( const Kaim::GeneratorSector& sector, Kaim::ClientInputConsumer& inputConsumer); }; KyResult MyProducer::Produce( const Kaim::GeneratorSector& /* sector */, Kaim::ClientInputConsumer& inputConsumer ) { // A sector can contain your whole level, or a part of a level that you plan // to stream in and out at runtime. You can set up as many sectors as you need. // See the section on running a generation below. // A NavTag is an array of blind data that you can add on each triangle. // You can use this data to distinguish different materials that you want to // take into account during your runtime path finding and path following. Kaim::DynamicNavTag aNavTag; aNavTag.m_blindDataArray.PushBack(42); // Set up a CoordSystem object to match the system used in your editor. KyFloat32 oneMeterInClientUnits = 1.0f; Kaim::CoordSystem::ClientAxis clientRightAxis = Kaim::CLIENT_X; Kaim::CoordSystem::ClientAxis clienFrontAxis = Kaim::CLIENT_Y; Kaim::CoordSystem::ClientAxis clientUpAxis = Kaim::CLIENT_Z; Kaim::CoordSystem myCoordSystem; myCoordSystem.Setup( oneMeterInClientUnits, clientRightAxis, clienFrontAxis, clientUpAxis ); // For each triangle in the sector, feed it to the input consumer. // How you retrieve the triangles is specific to your system. // If you have set up a CoordSystem as shown above, you can send your // triangles in your own coordinate system. The conversion is done internally // by the generation system. for(KyUInt32 index=0; index < myTriangleCount; index++) { inputConsumer.ConsumeTriangle( myTriangle.Vertex[0], myTriangle.Vertex[1], myTriangle.Vertex[2], aNavTag, myCoordSystem); } return KY_SUCCESS; }
The ClientInputConsumer object also contains a list of seed points and a list of TagVolumes that are shared among the various sectors. If you need to provide the generation system with seed points or tag the NavMesh within a 3D volume, it is recommended to add your seed points and volumes to these lists. For details, see Identifying the Navigable Areas.
You need to initialize and close the BaseSystem in your application, the same way you do in your runtime game. See Integration Phase 1b: Setting up the BaseSystem.
You can use the GeneratorBaseSystem class for correctly initializing the BaseSystem for generating the NavData. Instead of calling Kaim::BaseSystem::Init(config), you can simply call Kaim::GeneratorBaseSystem::Init(config). During the generation process, Gameware Navigation uses data structures that are only useful for NavData generation. Therefore, it is recommended to use GeneratorBaseSystem to correctly register the generation only classes.
The key components involved in launching a generation run are the Generator class and its GeneratorInputOutput configuration class.
You create a GeneratorInputOutput object, set it up with configuration parameters, and add a definition for each sector or block of terrain for which you want to generate the NavData. You then pass this object in a call to Generator::Generate(), which launches the generation process.
#include "gwnavgenerationglue/defaultgeneratorglue.h" bool Generate() { // Create an instance of your GeneratorInputProducer class. Kaim::Ptr<MyProducer> producer = *KY_NEW MyProducer; // Create an instance of the DefaultGeneratorGlue. KyGlue::DefaultGeneratorGlue glue; // Set up a Generator to use your producer. Kaim::Generator generator(producer, &glue); // Set the output directory within which the .NavData and other output files // are created. You must use the same absolute root directory every time // you create a Generator, but you might set up a different relative directory for // each different level. This keeps all of your NavData easily accessible during // visual debugging. generator.SetOutputDirectory("c:/MyGame/NavData", "MyTestLevel"); // Create a GeneratorInputOutput Kaim::GeneratorInputOutput generatorInputOutput; // Alternatively, you can create a GeneratorInputOutput by loading it from a // .GenIO file created during a previous generation run. This retains all // settings from the last run. You can also explicitly save your GeneratorInputOutput // object at any time by calling Kaim::GeneratorInputOutput::Save(). // Use the Kaim::GeneratorParameters and Kaim::GeneratorAdvancedParameters // classes that are accessible through generatorInputOutput.m_params and // generatorInputOutput.m_params.m_advancedParameters to configure settings // such as, the dimensions and movemement capabilities of your characters. generatorInputOutput.m_params.m_entityRadius = 0.8f; generatorInputOutput.m_params.m_advancedParameters.m_navTagMinPixelArea = 4; // Use the Kaim::GeneratorRunOptions class that is accessible through // generatorInputOutput.m_runOptions to configure the operation of the // generation process itself. generatorInputOutput.m_runOptions. m_doMultiCore = true; // Add a sector. // It is recommended to create a unique GUID for each sector and // even to save it along with your own level definition. This ensures that you // always generate with the same GUID for a given level or block of terrain. // If you want to generate a random GUID, you can use the // KyGlue::DefaultGuidGeneratorInterface class. Kaim::Ptr<Kaim::GeneratorSector> sector = *KY_NEW Kaim::GeneratorSector( Kaim::KyGuid("AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA"), "MyGameLevel"); generatorInputOutput.AddSector(sector); // Run a generation using the configuration we have set up. if (Kaim::Result::Fail(generator.Generate(generatorInputOutput))) { return false; } return true; }
For each sector, the generation system provides the same set of output files as the Navigation Lab. They are placed in a sub-directory under the root directory you have set for the Generator, named according to the name you set for the sector in its GeneratorSectorConfig. You can retrieve the .NavData file from there for inclusion into your game.
As an alternative to using the output files, you can retrieve the NavData as a raw data buffer, which you can package along with other data for your level to ease the process of streaming the data into and out of memory in your runtime game. See Integration Phase 6a: Integrating NavData into your Asset Pipeline.