From 60826b4c85aca34ebf4fa8e80a0109eb884c1593 Mon Sep 17 00:00:00 2001 From: Matt Williams Date: Tue, 16 Apr 2013 23:18:16 +0100 Subject: [PATCH] Add documentation and a tutorial for the Python bindings This should cover most questions about the Python bindings but of course, some things in the bindings is still subject to change. --- documentation/index.rst | 2 +- documentation/python-bindings.rst | 139 ++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 documentation/python-bindings.rst diff --git a/documentation/index.rst b/documentation/index.rst index 4512d077..0cec613c 100644 --- a/documentation/index.rst +++ b/documentation/index.rst @@ -14,7 +14,6 @@ User Guide: ModifyingTerrain LevelOfDetail Threading - Examples: @@ -22,6 +21,7 @@ Examples: :maxdepth: 1 tutorial1 + python-bindings Other Information: diff --git a/documentation/python-bindings.rst b/documentation/python-bindings.rst new file mode 100644 index 00000000..0e2d739e --- /dev/null +++ b/documentation/python-bindings.rst @@ -0,0 +1,139 @@ +*************** +Python bindings +*************** + +Introduction +============ + +PolyVox itself is a C++ library but in order to make it useful to as many people as possible, we also provide bindings to a number of other languages. +These bindings are all generated by `SWIG `_ and provide a bridge to a compiled PolyVox library (*DLL* or *.so*) via an interafce which is native to the language. +This allows you in a Python script to simpy call ``import PolyVoxCore`` and get access to the whole library's functionality. + +The Python bindings are available for Python 3. + +Comparison with C++ +------------------- + +All the bindings available for PolyVox (so both the Python bindings and any future supported bindings such as for C♯) follow the PolyVox C++ API quite closely. +This means that many PolyVox code examples written in C++ are mostly applicable also to Python. +Classes and functions are named the same and take the same set of arguments. +The main place this falls down is with templated C++ classes. +Since C++ templates are essentially a code-generation system built into the C++ compiler there is no way for a user of the Python bindings to request, at run-time, a specific piece of code to be generated. +The way we work around this is by, as part of the bindings generation process, pre-compiling a number of different versions of each templated class. +For example, in C++ a 3D vector containing 32-bit integers would be declared as + +.. code-block:: c++ + + PolyVox::Vector3D my_vec(0,1,4); + +but in Python it would be accessed as + +.. code-block:: python + + my_vec = PolyVoxCore.Vector3Dint32_t(0,1,4) + +As a rule, any time in C++ where you see a template instantiation with ``<>``, just remove the angle brackets and it will yield the Python class name. + +The choice of which C++ templates to instantiate is made by the developers of PolyVox in order to try to cover the main use-cases that library users would have. +If, however, you want to add your own versions, this can be done by editing the SWIG interface files and recompiling PolyVox. + +Buildings the bindings +====================== + +The bindings are built as part of the standard PolyVox build process. For details on this please follow the instructions at :doc:`install`. +The important things to note there are the requirements for building the bindings: *Python development libraries* and *SWIG*. +During the CMake phase of building, it should tell you whether or not the bindings will be built. + +Compiling the whole PolyVox library should then give you two files inside the ``build/library/bindings`` directory: + +``PolyVoxCore.py`` + This is the main entry point to the library. + +``_PolyVoxCore.so`` (or ``.dll`` on Windows) + This contains the compiled code of the PolyVox library. + + This file has a link dependency the main ``libPolyVoxCore.so`` library as well as the Python shared library. + + +Using the bindings +================== + +As discussed above, the Python API is very similar to the C++ one but none-the-less we'll go through an example to see how it works. +All the code in this section is taken from ``PythonExample.py`` found in the source distribution of PolyVox in the ``examples/Python`` folder. + +Seting up the volume +-------------------- + +The first this we do is import the ``PolyVoxCore`` module. We rename it as ``pv`` to make our life easier. + +.. literalinclude:: ../examples/Python/PythonExample.py + :language: python + :lines: 29 + +We create a ``Region`` from two vectors defining the bounds of the area - a volume 64×64×64. +Remember that ``pv.Vector3Dint32_t`` refers to a 3D :polyvox:`PolyVox::Vector` templated on a 32-bit integer. + +The second line creates a :polyvox:`SimpleVolume` of the same size as the :polyvox:`Region` where each voxel in the volume is defined an unsigned 8-bit integer. + +.. literalinclude:: ../examples/Python/PythonExample.py + :language: python + :lines: 31-33 + +We're going to fill our volume with a sphere and so we start by finding out where the centre of the volume is and defining the radius of our desired shape. + +.. literalinclude:: ../examples/Python/PythonExample.py + :language: python + :lines: 35-37 + +Then we actually loop over each of the dimensions of the volume such that inside the loop, ``x``, ``y`` and ``z`` refer to the current location. + +.. literalinclude:: ../examples/Python/PythonExample.py + :language: python + :lines: 38-41 + +All we do inside the loop is set all the voxels inside the sphere to have a value of ``255`` and all those outside to have a value of ``0``. + +.. literalinclude:: ../examples/Python/PythonExample.py + :language: python + :lines: 42-53 + +Getting the mesh data +--------------------- + +Extracting the surface mesh for the volume is a two-step process. +First we tell PolyVox to generate the appropriate mesh, then we have to convert the PolyVox mesh data to something that our rendering library can understand. + +First we define the sort of mesh that we want. For this example we want a mesh with information on the position, material and normal of each vertex. +Once we have out mesh object ready to be filled, we pass it to our surface extractor of choice. +PolyVox comes with a number of different surface extractors but for our example here, we want a cubic mesh. + +You should also note that the ungainly looking ``CubicSurfaceExtractorWithNormalsSimpleVolumeuint8`` refers to the C++ class ``CubicSurfaceExtractorWithNormals>``. +The ``execute()`` call is when PolyVox actually goes off and generates the requested mesh based on the data contained in the volume. + +.. literalinclude:: ../examples/Python/PythonExample.py + :language: python + :lines: 55-58 + +Up until this point, the Python code has been totally generic with respect to your choice of rendering engine. +For this example we will be using `PyOpenGL `_ as it provides a nice pythonic API for many OpenGL functions. + +Regardless of which rendering engine you are using, you will need to be able to wrangle the PolyVox mesh output into something you can insert into your engine. +In our case, we want two lists: + +1. All the vertices along with their normals +2. The vertex indices which describes how the vertices are put together to make triangles. + +PyOpenGL undersands `NumPy `_ arrays and so we are going to copy our vertex data into two of these. +:polyvox:`SurfaceMesh` provides two useful functions here, ``getIndices()`` and ``getVertices()``, the Python versions of which return Python tuples. + +The indices we can pass directly to NumPy as long as we make sure we specify the correct type for the data inside. +For the vertices, we want to rearange the data so that OpenGL can read it more efficiently. +To this end we explicitly retrieve the vertex positions and normals for each vertex and place them +such that the vertex ``x``, ``y`` and ``z`` positions are placed contiguously in memory followed by the normal vector's ``x``, ``y`` and ``z`` values. + +.. literalinclude:: ../examples/Python/PythonExample.py + :language: python + :lines: 62-69 + +From this point on in the example, PolyVox is no longer used directly and all the code is standard PyOpenGL. +I won't go through every line here but the source code in ``PythonExample.py`` is commented and should be sufficient to understand how things work.