.. _ogn_extension_tutorial:

Example Setup - Creating An OGN-enabled Extension
=================================================
This document assumes familiarity with the extension 2.0 implementation described in the Extensions doc.
Implementation details that relate to the extension system but not to .ogn files in particular will not be described
in detail. Refer to the above document for more information.

.. only:: internal

    Internal: link to Extensions / exts-main-doc broken until kit_repo_docs migration completed (bidirectional dependency bad)

For the purposes of illustration an extension names `omni.my.extension` will be created, with a single node `OgnMyNode`.

.. contents::

.. _ogn_recommended_directory_structure:

Recommended Directory Structure
-------------------------------
The OGN helper scripts presume a certain directory structure, setting up helper variables that default to values supporting
that structure. You're free to use any other structure, you will just have more work to do in your build file to support
them.

To start with are the directories.

.. code-block:: text
    :emphasize-lines: 1,2,4,6,8,12,15,19,21,23,26

    omni.my.extension/
    ├── bindings/
    |   └── BindingsPython.cpp
    ├── config/
    |   └── extension.toml
    ├── data/
    |   └── preview.png
    ├── docs/
    |   ├── README.md
    |   ├── CHANGELOG.md
    |   └── index.rst
    ├── nodes/
    |   ├── OgnMyNode.cpp
    |   └── OgnMyNode.ogn
    ├── plugins/
    |   ├── PluginInterface.cpp
    |   └── IMyExtension.h
    ├── premake5.lua
    └── python/
        ├── __init__.py
        ├── _impl/
        |   extension.py
        |   └── nodes/
        |       ├── OgnMyPythonNode.py
        |       └── OgnMyPythonNode.ogn
        └── tests/
            └── test_my_extension.py

**omni.my.extension** is the top level directory for the extension, keeping all related files together.

**config** is the common location for the extension configuration file.

**data** is the common location for data files used by your extension.

**docs** is the common location for published documentation for your extension.

**nodes** is a directory you can put all of your node files. If there is a large number of nodes for a single extension then this
directory can be subdivided (e.g. **nodes/math**, **nodes/core**, **nodes/vectors**...)

**plugins** is for the common C++ code. In particular the files connecting to the Carbonite plugin ABI, but also other files used
by the plugin that aren't specifically node files.

**python** contains just the __init__.py script used to load the extension. The main feature of this file is that it
is used to import everything in the **python/_impl** subdirectory that will be exposed as the public API. Although this
is just a convention, as every function will always be accessible for the user that digs deep enough, setting up your
intended API in this way makes it more clear exactly what parts of your module are intended for public use, and hence
expected to be more stable.

**python/_impl** contains all of the Python scripts. At the top level this can be UI files, commands, and helper scripts.

**python/_impl/nodes** contains all of your nodes implemented in Python. Separating them into their own directory makes
it easier for the OGN processor to find them.

**python/tests** is for the scripts used to test the functionality in your extension. These are your armour, keeping
your extension safe from changes others make, and providing confidence when you refactor.


premake5.lua
------------
The *.lua* file containing an extension 2.0 implementation of an extension. The OGN-specific additions are commented. (Other lines would
normally be commented as well in good build code; omitted here for clarity.)

.. literalinclude:: premake5.lua
    :linenos:
    :language: lua

Here is that same file with appropriate comments that can be used as a starting template.

.. literalinclude:: premake5-template.lua
    :linenos:
    :language: lua

Changing Type Definitions
+++++++++++++++++++++++++
By default the code generator uses certain C++, CUDA, and Python data types for the code it produces. Sometimes you may
wish to use alternative types, e.g. your favourite math library's types. This information can be modified through a
type definition file. To modify the type definitions for an entire project you add an extra parameter to the call to
`get_ogn_project_information()` in your ``premake5.lua`` file, with details described in
:ref:`ogn_type_definition_overrides`.

bindings/BindingsPython.cpp
---------------------------
This file is set up as per the regular extension set up you can find in the Extensions doc with no OGN-specific changes
required.

.. only:: internal

    Internal: link to Extensions / exts-main-doc broken until kit_repo_docs migration completed (bidirectional dependency bad)

config/extension.toml
---------------------
In addition to the other required dependencies, the OmniGraph dependencies need to be added for the extension loading to work properly.
These are the OmniGraph core, and the Kit async engine used for running tests.

.. code-block:: toml
    :linenos:
    :emphasize-lines: 8-11,15-16

    [package]
    title = "My Feature"

    # Main module for the Python interface
    [[python.module]]
    name = "omni.my.feature"

    # Watch the .ogn files for hot reloading (only works for Python files)
    [fswatcher.patterns]
    include = ["*.ogn", "*.py"]
    exclude = ["Ogn*Database.py"]

    # Other extensions that need to load before this one
    [dependencies]
    "omni.graph" = {}
    "omni.graph.tools" = {}

    [[native.plugin]]
    path = "bin/${platform}/${config}/*.plugin"
    recursive = false

data/preview.png
----------------
This file is an image that will be displayed in the extension manager. Nothing extra needs to be done to make that
happen; just create the file and ensure that the **data/** directory is installed into the extension's destination
directory.

docs/README.md
--------------
Markdown format documentation for your extension that will appear in the extension manager. It should be a short
description of what the extension does. Here is a minimal version of a readme.

.. code-block:: md

    # My Extension [omni.my.extension]

    My extension can cook your breakfast, mow your lawn, and fix the clog in your kitchen sink.

docs/CHANGELOG.md
-----------------
Markdown format documentation for your extension changes that will appear in the extension manager. Here is a simple
template that uses a standard approach to maintaining change logs.

.. code-block:: md

    # Changelog

    All notable changes to this project will be documented in this file.

    The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
    and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

    ## [Unreleased]

    ## [1.0.0] - 2021-03-01
    ### Initial Version

docs/index.rst
--------------
ReStructuredText format documentation for your extension. The node's documentation will be generated automatically
by the OGN processor.

nodes/OgnMyNode.cpp,OgnMyNode.ogn
---------------------------------
These files contain the description and implementation of your node. See the detailed examples in
:ref:`ogn_tutorial_nodes` for information on what can go into these files.

The naming of the files is important. They must both have the same base name (``OgnMyNode`` in this case). It is also
a good idea to have the common prefix `Ogn` as that makes nodes easier to locate.

plugins/PluginInterface.cpp
---------------------------
This file sets up your plugin. The OGN processing requires a couple of small additions to enable the automatic
registration and deregistration of nodes when your plugin starts up and shuts down.

.. code-block:: c++
    :linenos:
    :emphasize-lines: 6,7,16-18,28,33

    #include "IOmniMyFeature.h"

    #include <carb/Framework.h>
    #include <carb/PluginUtils.h>

    #include <omni/graph/core/iComputeGraph.h>
    #include <omni/graph/core/ogn/Registration.h>
    #include <omni/kit/IEditor.h>
    #include <omni/kit/IMinimal.h>

    const struct carb::PluginImplDesc pluginDesc = { "omni.my.feature.plugin", "My Feature", "NVIDIA",
                                                    carb::PluginHotReload::eEnabled, "dev" };

    CARB_PLUGIN_IMPL(pluginDesc, omni::my::feature::IOmniMyFeature)
    CARB_PLUGIN_IMPL_DEPS(omni::kit::IEditor,
                        omni::graph::core::IGraphRegistry,
                        carb::flatcache::IToken)
    DECLARE_OGN_NODES()

    // carbonite interface for this plugin (may contain multiple compute nodes)
    void fillInterface(omni::my::feature::IOmniMyFeature& iface)
    {
        iface = {};
    }

    CARB_EXPORT void carbOnPluginStartup()
    {
        INITIALIZE_OGN_NODES()
    }

    CARB_EXPORT void carbOnPluginShutdown()
    {
        RELEASE_OGN_NODES()
    }


plugins/IMyExtension.h
----------------------
This file is set up as per the extension description with no OGN-specific changes required.

python/__init__.py
------------------
This file initializes the extension's Python module as per the extension model. This will import your intended Python
API for general use.

.. code-block:: python

    from ._impl.utilities import useful_utility
    from ._impl.utilities import other_utility

    from ._impl.commands import cmds

With this setup the extension user can access your Python API through the main module (similarly to how popular
public packages such as `numpy` and `pandas` are structured).

.. code-block:: python

    import omni.my.feature as mf
    mf.useful_utility(["Bart", "Lisa", "Maggie", "Marge", "Homer"])

python/tests/__init__.py
------------------------
This file may contain setup code for your manually written test scripts that appear in this directory.  It should
contain the snippet below, which will enable automatic registration of your tests at runtime.

.. code-block:: python

    """
    Presence of this file allows the tests directory to be imported as a module so that all of its contents
    can be scanned to automatically add tests that are placed into this directory.
    """
    scan_for_test_modules = True

Test files should all have a PEP8-compliant name of the form ``test_my_good_test_file.py``. The `test` prefix ensures
that the files will be automatically recognized as containing Python unit tests.

Sample Skeleton Test
--------------------
Here's an example of a simple test that does nothing more than run a graph evaluation on a graph consisting of
two nodes with a connection between them, verifying the output of a node. While .ogn files have the ability to
specify their own automated test configurations, it is very limited and you'll want to use something like this for
more complex testing.

.. literalinclude:: test_example.py
    :linenos:
    :language: python
