diff --git a/programming/index.rst b/programming/index.rst index 07e7d157..22db40b4 100644 --- a/programming/index.rst +++ b/programming/index.rst @@ -59,4 +59,5 @@ manual by studying the :ref:`Reference Section `. threading subclassing pandai/index + navigation/index using-cpp/index diff --git a/programming/navigation/bam-serialization.rst b/programming/navigation/bam-serialization.rst new file mode 100644 index 00000000..916c6536 --- /dev/null +++ b/programming/navigation/bam-serialization.rst @@ -0,0 +1,66 @@ +.. _bam-serialization: + +BAM serialization +================= + +Panda3D provides the feature to write the Panda3D objects to the disk +using the BAM operations. In the navigation library, the BAM operations have +been implemented over the NavMeshNode class. + +.. only:: python + + .. code-block:: python + + navmeshnode = navigation.NavMeshNode("firstnavmeshnode", navmesh) + navmeshnodepath = scene.attach_new_node(navmeshnode) + +.. only:: cpp + + .. code-block:: cpp + + NavMeshNode navmeshnode = new NavMeshNode("firstnavmeshnode", navmesh); + NodePath navmeshnodepath = scene.attach_new_node(navmeshnode); + + +Write BAM +~~~~~~~~~ + +You can write the 'navmeshnodepath' to the disk as a BAM file. + +.. only:: python + + .. code-block:: python + + navmeshnodepath.write_bam_file("firstnavmeshnode.bam") + +.. only:: cpp + + .. code-block:: cpp + + navmeshnodepath.write_bam_file("firstnavmeshnode.bam"); + +The BAM File 'firstnavmeshnode.bam' is stored on the disk and can be used directly +in the games or the programs without building and navigation mesh again. This +helps in saving time and computation power. + +Read BAM +~~~~~~~~ + +You can read the BAM File from the disk and load it as NodePath to NavMeshNode. + +.. only:: python + + .. code-block:: python + + navmesh = loader.load_model("firstnavmeshnode.bam") + +.. only:: cpp + + .. code-block:: cpp + + PandaFramework framework; + framework.open_framework(argc, argv); + framework.set_window_title("My Panda3D Window"); + WindowFramework *window = framework.open_window(); + NodePath navmesh = window->load_model(framework.get_models(), "firstnavmeshnode.bam"); + diff --git a/programming/navigation/build-mesh.rst b/programming/navigation/build-mesh.rst new file mode 100644 index 00000000..ef5ef14b --- /dev/null +++ b/programming/navigation/build-mesh.rst @@ -0,0 +1,111 @@ +.. _build-mesh: + +Building Navigation Mesh +======================== + +Implementation: + +Following are the steps to build a basic navigation mesh. + +Step 1: + +.. only:: python + + To use the navmeshgen library into your game you need to import it into your game code. + The generated mesh is stored as an object of class NavMesh, which can be accessed by importing navigation. + + .. code-block:: python + + from panda3d import navmeshgen + from panda3d import navigation + +.. only:: cpp + + The headers required to be included for building mesh are navMeshBuilder and navMesh. + + .. code-block:: cpp + + #include "pandaFramework.h" + #include "pandaSystem.h" + #include "navMesh.h" + #include "navMeshBuilder.h" + +Step 2: + +Create a geom primitive or load an external model. Here we load the model called 'village.obj' + +.. only:: python + + .. code-block:: python + + scene = loader.load_model("samples/navigation/models/village.obj") + +.. only:: cpp + + .. code-block:: cpp + + PandaFramework framework; + framework.open_framework(argc, argv); + framework.set_window_title("My Panda3D Window"); + WindowFramework *window = framework.open_window(); + NodePath scene = window->load_model(framework.get_models(), "samples/navigation/models/village.obj"); + +Now the model is loaded in the NodePath 'scene' + +Step 3: + +Create an object for the class NavMeshBuilder via: + +.. only:: python + + .. code-block:: python + + builder = navmeshgen.NavMeshBuilder() + +.. only:: cpp + + .. code-block:: cpp + + NavMeshBuilder builder = new NavMeshBuilder() + +Step 4: + +Now, we shall build the navigation mesh for our model stored in NodePath 'scene'. + +.. only:: python + + .. code-block:: python + + builder.from_node_path(scene) + +.. only:: cpp + + .. code-block:: cpp + + builder.from_node_path(scene) + +Step 5: + +Finally, we build the navigation mesh using the build function. +The output mesh is stored as an object of class NavMesh. + +.. only:: python + + .. code-block:: python + + navmesh = builder.build() + +.. only:: cpp + + .. code-block:: cpp + + PT(NavMesh) navmesh = builder.build() + +Here, 'navmesh' is the object of class NavMesh and has the generated mesh. + +This is how easy it is to get a basic navigation mesh generated! + +Next Step: + +Now that you have a basic working program, you should proceed to the +parameters page and see how navigation mesh varies with parameters. diff --git a/programming/navigation/getting-started.rst b/programming/navigation/getting-started.rst new file mode 100644 index 00000000..b94e11ff --- /dev/null +++ b/programming/navigation/getting-started.rst @@ -0,0 +1,14 @@ +.. _getting-started: + +Getting Started +=============== + +Navigation and Navmeshgen are two libraries which can be used together to perform fast pathfinding operations on Panda3d geoms. + +To use the navmeshgen library into your game you need to import it into your game +code. +The generated mesh is stored as an object of class NavMesh, which can be accessed by importing navigation. + +The navmeshgen library has the class NavMeshBuilder, the main operation of which is to build the navigation mesh. +The navigation library has the class NavMesh, which stores the mesh build by NavMeshBuilder or some other library. +It has also has class NavMeshQuery, which is responsible for handling the query operations like pathfinding over the mesh. diff --git a/programming/navigation/index.rst b/programming/navigation/index.rst new file mode 100644 index 00000000..074aa8ff --- /dev/null +++ b/programming/navigation/index.rst @@ -0,0 +1,21 @@ +.. _navigation: + +Pathfinding using Navigation +============================ + +Panda3D is integrated with the Recast-Detour library as navmeshgen and navigation libraries. + +Navmeshgen is an AI library for Panda3D which, for a given geom, generates a navigation mesh, a mesh containing only the walkable surfaces. + +Navigation is an AI library for Panda3D which operates on an input navigation mesh. The input navigation can be generated using navmeshgen library or by the user's preferred external library. The navigation library lets us query over the navigation mesh and find the path between any two points over the mesh. + + +.. toctree:: + :maxdepth: 2 + + getting-started + build-mesh + set-actor-parameters + visualize-mesh + path-query + bam-serialization diff --git a/programming/navigation/navmesh.png b/programming/navigation/navmesh.png new file mode 100644 index 00000000..66331384 Binary files /dev/null and b/programming/navigation/navmesh.png differ diff --git a/programming/navigation/path-query.rst b/programming/navigation/path-query.rst new file mode 100644 index 00000000..7a72f88c --- /dev/null +++ b/programming/navigation/path-query.rst @@ -0,0 +1,231 @@ +.. _path-query: + +Pathfinding on NavMesh +====================== + +The most important feature of navigation library is pathfinding. +The library uses A* pathfinding algorithm over polygons of navigation +mesh, though you do not need to worry about those stuffs. + +Update the Code +~~~~~~~~~~~~~~~ + +.. only:: cpp + + Include the header required for making queries on navmesh: + + .. code-block:: cpp + + #include "navMeshQuery.h" + +Make a object of NavMeshQuery Class which will then do query operations +for pathfinding. + +.. only:: python + + .. code-block:: python + + query = navigation.NavMeshQuery(navmesh) + +.. only:: cpp + + .. code-block:: cpp + + NavMeshQuery query = new NavMeshQuery(navmesh); + +Now, you should define the two points between which path has to be found. + +.. only:: python + + .. code-block:: python + + from panda3d.core import LPoint3 + pos1 = LPoint3(0, 0, 0); + pos2 = LPoint3(-50, -60, 3); + +.. only:: cpp + + .. code-block:: cpp + + #include "lpoint3.h" + LPoint3 pos1 = new Lpoint3(0,0,0); + LPoint3 pos2 = new Lpoint3(-50,-60,3); + + +To find the path between the two positions, you can simple use the following: + +.. only:: python + + .. code-block:: python + + path = query.find_path(pos1, pos2) + +.. only:: cpp + + .. code-block:: cpp + + PTA_LVecBase3 path = query.find_path(pos1, pos2); + +Here, find_path function finds the points on the navigation mesh closest to +the input positions. So, you need not worry about them not being exactly over +the navigation mesh. + +If you find / print the point on the navigation mesh closest to the position, you can +do it in the following way: + +.. only:: python + + .. code-block:: python + + pos = LPoint3(0, 1, 5) + query.nearest_point(pos) + print(pos) + +.. only:: cpp + + .. code-block:: cpp + + LPoint3 pos = new LPoint3(0, 1, 5); + query.nearest_point(pos); + +You have the path stored in the 'path' variable, which has array of points joining +the path. You can use LineSegs to visualize the path as follows: + +.. only:: python + + .. code-block:: python + + from panda3d.core import LineSegs + + pathLine = LineSegs() + pathLine.set_color(0, 1, 0) + pathLine.set_thickness(5) + for i in range(len(path)): + pathLine.draw_to(path[i]) + + lineNode = pathLine.create() + lineNodePath = scene.attach_new_node(lineNode) + +.. only:: cpp + + .. code-block:: cpp + + #include "lineSegs.h" + + LineSegs pathLine = new LineSegs(); + pathLine.set_color(0, 1, 0); + pathLine.set_thickness(5); + for(int i=0 ; i < path.size() ; i++) { + pathLine.draw_to(path[i]); + } + + GeomNode *lineNode = pathLine.create(); + NodePath lineNodePath = scene.attach_new_node(lineNode); + +Run the Program +~~~~~~~~~~~~~~~ + +Go ahead and run the program. You should see this: + +.. image:: path1.png + +The green lines show the path between the positions. + +Using straight path +~~~~~~~~~~~~~~~~~~~ + +You can also a different path querying function. Update the code as follows +by replacing the definition of 'path' before visualization using LineSegs: + +.. only:: python + + .. code-block:: python + + path = query.find_straight_path(pos1, pos2) + +.. only:: cpp + + .. code-block:: cpp + + PTA_LVecBase3 path = query.find_straight_path(pos1, pos2); + +After running the program, you should see this: + +.. image:: path2.png + + +.. only:: python + + A sample program with all the functions explained in this section can be found below. + + .. code-block:: python + + from direct.showbase.ShowBase import ShowBase + from panda3d import navigation + from panda3d import navmeshgen + from panda3d.core import PointLight,DirectionalLight + from panda3d.core import LPoint3 + from panda3d.core import LineSegs + from panda3d.core import NodePath + + class MyApp(ShowBase): + + def __init__(self): + ShowBase.__init__(self) + + # Setting up light for better view. + plight = PointLight('plight') + plight.setColor((0.9, 0.9, 0.9, 0.5)) + plnp = render.attachNewNode(plight) + plnp.setPos(10, 20, 0) + render.setLight(plnp) + dlight = DirectionalLight('dlight') + dlight.setColor((0.8, 0.5, 0.5, 1)) + dlnp = render.attachNewNode(dlight) + dlnp.setHpr(0, -60, 0) + render.setLight(dlnp) + + # Loading the model + self.scene = self.loader.loadModel("untitled.obj") + self.scene.reparentTo(self.render) + self.scene.setP(90) + + self.scene.setScale(0.25, 0.25, 0.25) + self.scene.flatten_light() + self.scene.setPos(-8, 42, 0) + + #NavMeshBuilder is a class that is responsible for building the polygon meshes and navigation meshes. + self.builder = navmeshgen.NavMeshBuilder() + # Take Nodepath as input. Nodepath should contain the required geometry. + self.builder.fromNodePath(self.scene) + + self.builder.setActorRadius(1) + self.builder.set_actor_height(10) + #self.builder.set_partition_type(2) + self.navmesh = self.builder.build() + + # Code to attach the polymesh generated to the scene graph + self.node1 = self.navmesh.drawNavMeshGeom() + self.node = self.scene.attachNewNode(self.node1) + self.node.setColor(0,0,1) + self.node.setPos(0,0,0.5) + + self.navmeshnode = navigation.NavMeshNode("firstnavmeshnode",self.navmesh) + self.navmeshnodepath = self.scene.attachNewNode(self.navmeshnode) + + self.query = navigation.NavMeshQuery(self.navmesh) + pos1 = LPoint3(0, 0, 0); + pos2 = LPoint3(-50, -60, 3); + self.path = self.query.find_straight_path(pos1, pos2) + + self.pathLine = LineSegs() + self.pathLine.set_color(0, 1, 0) + self.pathLine.set_thickness(5) + for i in range(len(self.path)): + self.pathLine.draw_to(self.path[i]) + self.lineNode = self.pathLine.create() + self.lineNodePath = self.scene.attach_new_node(self.lineNode) + self.lineNodePath.setPos(0,0,1) + + app = MyApp() + app.run() diff --git a/programming/navigation/path1.png b/programming/navigation/path1.png new file mode 100644 index 00000000..52cd8200 Binary files /dev/null and b/programming/navigation/path1.png differ diff --git a/programming/navigation/path2.png b/programming/navigation/path2.png new file mode 100644 index 00000000..a4161202 Binary files /dev/null and b/programming/navigation/path2.png differ diff --git a/programming/navigation/set-actor-parameters.rst b/programming/navigation/set-actor-parameters.rst new file mode 100644 index 00000000..a612d5c4 --- /dev/null +++ b/programming/navigation/set-actor-parameters.rst @@ -0,0 +1,125 @@ +.. _set-actor-parameters: + +Setting Actors Parameters +========================= + +The navigation mesh is built based on the parameters set for the actor, or say, +the capabilities of the actors. The navigation mesh varies to accomodate +the size of actor, the climbing capabilities. + +Implementation: + +First we need to create an object for the class NavMeshBuilder via: + +.. only:: python + + .. code-block:: python + + builder = navmeshgen.NavMeshBuilder() + +.. only:: cpp + + .. code-block:: cpp + + NavMeshBuilder builder = new NavMeshBuilder(); + +Now, you can set parameters for a navigation mesh in the following way. +Remember to set parameters before building the mesh by calling the build() function. + +Actor Height: + +The default value of actor height is 2 units. You can set the height of actor to 3.5 units as: + +.. only:: python + + .. code-block:: python + + builder.set_actor_height(3.5) + +.. only:: cpp + + .. code-block:: cpp + + builder.set_actor_height(3.5); + +Actor Radius: + +The default value of actor radius is 0.6 units. You can set the radius of actor to 1.0 unit as: + +.. only:: python + + .. code-block:: python + + builder.set_actor_radius(1) + +.. only:: cpp + + .. code-block:: cpp + + builder.set_actor_radius(1); + +Actor Climb: + +The default value of actor's climbing capability is 0.9 units. You can set the radius of actor to 1.5 unit as: + +.. only:: python + + .. code-block:: python + + builder.set_actor_climb(1.5) + +.. only:: cpp + + .. code-block:: cpp + + builder.set_actor_climb(1.5); + +You can also get the parameters value as: + +.. only:: python + + .. code-block:: python + + height = builder.get_actor_height() + radius = builder.get_actor_radius() + climb = builder.get_actor_climb() + +.. only:: cpp + + .. code-block:: cpp + + float height = builder.get_actor_height(); + float radius = builder.get_actor_radius(); + float climb = builder.get_actor_climb(); + +Apart from setting actor's parameters, we can also decide on the partition type for navigation mesh. +The default partition type is 'watershed' but can be set to 'monotone' or 'layer'. +The input arguments are of 'enum' type, so can be accessed by integers as well, 0 for watershed, 1 for monotone and 2 for layer. + +.. only:: python + + .. code-block:: python + + builder.set_partition_type(1) + +.. only:: cpp + + .. code-block:: cpp + + builder.set_partition_type(1); + +Reset Parameters: + +The parameters can be reset to default as: + +.. only:: python + + .. code-block:: python + + builder.reset_common_settings() + +.. only:: cpp + + .. code-block:: cpp + + builder.reset_common_settings(); diff --git a/programming/navigation/visualize-mesh.rst b/programming/navigation/visualize-mesh.rst new file mode 100644 index 00000000..cdcd4495 --- /dev/null +++ b/programming/navigation/visualize-mesh.rst @@ -0,0 +1,127 @@ +.. _visualize-mesh: + +Visualize NavMesh +================= + +Update the Code +~~~~~~~~~~~~~~~ + +After you have built the navigation mesh, you migh also wish to visualize it. +Let's say you used the following line of code to build the navigation mesh. + +.. only:: python + + .. code-block:: python + + builder.set_actor_radius(1.0) + navmesh = builder.build() + +.. only:: cpp + + .. code-block:: cpp + + builder.set_actor_radius(1.0); + PT(NavMesh) navmesh = builder.build(); + +You do not want you actor to move along the walls, hence the radius has been +set to a value of 1 unit using set_actor_radius(1.0) function. You should set the +radius as per your requirements. +Now, in order to visualize the mesh, you can do it in the following way: + +.. only:: python + + .. code-block:: python + + node = navmesh.draw_nav_mesh_geom() + +.. only:: cpp + + .. code-block:: cpp + + PT(GeomNode) node = navmesh->draw_nav_mesh_geom(); + +draw_nav_mesh_geom() returns the navigation mesh as a GeomNode object. + +Before visualizing navigation mesh, you might also want to visualize the +the object node over which navigation mesh is built. + +You should first set up the lighting of the environment. Here, a combination of +directional and point light is used. + +.. only:: python + + .. code-block:: python + + from panda3d.core import PointLight,DirectionalLight + + plight = PointLight('plight') + plight.set_color((0.9, 0.9, 0.9, 0.5)) + plnp = render.attach_new_node(plight) + plnp.set_pos(10, 20, 0) + render.set_light(plnp) + + dlight = DirectionalLight('dlight') + dlight.set_color((0.8, 0.5, 0.5, 1)) + dlnp = render.attach_new_node(dlight) + dlnp.set_hpr(0, -60, 0) + render.set_light(dlnp) + +.. only:: cpp + + .. code-block:: cpp + + #include "pointLight.h" + #include "directionalLight.h" + + PointLight plight = new PointLight('plight'); + plight.setColor((0.9, 0.9, 0.9, 0.5)); + NodePath plnp = render.attach_new_node(plight); + plnp.set_pos(10, 20, 0); + render.set_light(plnp); + + DirectionalLight dlight = new DirectionalLight('dlight'); + dlight.set_color((0.8, 0.5, 0.5, 1)); + NodePath dlnp = render.attach_new_node(dlight); + dlnp.set_hpr(0, -60, 0); + render.set_light(dlnp); + +To visualize the object: + +.. only:: python + + .. code-block:: python + + scene.reparent_to(render) + +.. only:: cpp + + .. code-block:: cpp + + scene.reparent_to(window->get_render()); + +Now in order to visualize GeomNode, you can attach the GeomNode to render +or some other NodePath already attached to render, like here it has been +attached to 'scene'. + +.. only:: python + + .. code-block:: python + + nodepath = scene.attach_new_node(node) + nodepath.set_color(0, 0, 1) + +.. only:: cpp + + .. code-block:: cpp + + NodePath nodepath = scene.attach_new_node(node); + nodepath.set_color(0, 0, 1); + +Run the Program +~~~~~~~~~~~~~~~ + +Go ahead and run the program. You should see this: + +.. image:: navmesh.png + +You should see navigation mesh in blue color over the object surface.