# Extending the API

## Overview

It may occour, that you want to extend the API, becouse there are entities with missing wrappers, or you wrote new ApertusVR entity, or feature. We will shortly discuss, what you have to do, to make your existing C++ feature in ApertusVR to work on Android using Java.

In this page, we will go through step-by-step on the extension process using the `ConeGeometry` entity as an example.

There are three places, where you have to make modifications:

1. **ApertusJNI.java**, where you have to register new native functions
2. **apeJNIPlugin** shared library, where you have to include new .cpp files, and implement the registered functions
3. **org.apertusvr** Java-package, where you have to create new wrappers

## ApertusJNI

For each member function in the `ape::IConeGeometry` interface, we have to register a static native Java-function in the **ApertusJNI.java** file. So take a glance at the `IConeGeometry` interface:

```cpp
class IConeGeometry : public ape::Geometry
{
protected:
		IConeGeometry(std::string name, bool replicate, std::string ownerID) : ape::Geometry(name, ape::Entity::GEOMETRY_CONE, replicate, ownerID) {}

		virtual ~IConeGeometry() {};

public:
		virtual void setParameters(float radius, float height, float tile, ape::Vector2 numSeg) = 0;

		virtual ape::GeometryConeParameters getParameters() = 0;

		virtual void setParentNode(ape::NodeWeakPtr parentNode) = 0;

		virtual void setMaterial(ape::MaterialWeakPtr material) = 0;

		virtual ape::MaterialWeakPtr getMaterial() = 0;

		virtual void setOwner(std::string ownerID) = 0;

		virtual std::string getOwner() = 0;
};
```

We are concerned only about the public virtual function. The convention is that we indicate the entity type in the static native function. So in the `ConeGeometry` case, it looks like this:

```cpp
static native void setConeGeometryParameters(String nativeCone, float radius, float height, float tile, float numSegX, float numSegY);

static native @Size(5) float[] getConeGeometryParameters(String nativeCone);

static native void setConeGeometryParentNode(String nativeCone, String parentNode);

static native void setConeGeometryMaterial(String nativeCone, String material);

static native String getConeGeometryMaterial(String nativeCone);

static native void setConeGeometryOwner(String nativeCone, String ownerID);

static native String getConeGeometryOwner(String nativeCone);
```

After the `set`, `get`, `is` etc., comes the entity name, then the "subject" (e.g. `ParentNode`). Comparing the two set of function headers, note that the rules are:

* `std::string` turns into `String`
* `ape::Entity` types turn into `String`, as we access them by their IDs
* `ape::Vector2`, `ape::Color`, etc. turns into separate `float` values, as we pursue to use primitive types whenever it is possible.
* `enum` types turn into `int` variables

Now, we are done with the ApertusJNI file, we can go forward the next step.

## apeJNIPlugin

The JNI-functions are contained in the apeJNIPlugin shared library. For each entity and other component (e.g. `ape::CoreConfig`) there is a corresponding **apeJNI\<entityName>.cpp** file (e.g. **apeJNICoreConfig.cpp**).

This will be the case in the new `ConeGeometry` entity, we create an **apeJNIConeGeometry.cpp** file, where we place the JNI-functions. The rule is that for a static native function in the `packaganame` package, and `className` class with `functionName` name, we have its corresponding JNI-function header as:

```cpp
extern "C"
JNIEXPORT /* ourParam */ JNICALL
Java_packagename_className_functionName(JNIEnv *env, jclass clazz, /* parameters */);
```

E.g. our first function's header will look like:

```cpp
extern "C"
JNIEXPORT void JNICALL
Java_org_apertusvr_ApertusJNI_setConeGeometryParameters(
    JNIEnv *env, jclass clazz, jstring native_cone,
    float radius, float height, float tile, 
    float num_seg_x, float num_seg_y);
```

Next task is to implement the function. We will only show the first two, but you can find all of them on GitHub.

JNI-functions are traditionally C-like functions, as the whole API is designed as a C API (even though we can use any C++ feature in JNI-functions). So how do we access the `ape::ISceneManager`, to query and modify our entities? We use the `ape::JNIPlugin` as a crutch.

This plugin is a special one in the term of that it acts as a singleton. Therefore we can get a pointer pointing to the plugin (which has access to the whole ApertusVR system) with the `ape::JNIPlugin::getPluginPtr()` static function. So we query this pointer, and store it to a variable. Then we convert the entity id into a C-style string with the `env` pointer:

```cpp
const char* name = env->GetStringUTFChars(native_cone, nullptr);
```

At this point we have the id, and we have a pointer pointing to the JNIPlugin, which has access to the sceneManager. This means we can do anything we want!\
Okay let us move on. As we are implementing the `setParameters(...)` function our task is to set the parameters for the cone with the id `name`. This goes naturally, just like we use the ApertusVR C++ API:

```cpp
if(auto coneGeometryShared = std::static_pointer_cast<ape::IConeGeometry>(jniPlugin->getSceneManager()->getEntity(std::string(name)).lock()))
{
    coneGeometryShared->setParameters(radius, height, tile, ape::Vector2(num_seg_x,num_seg_y));
}
```

Great! Now we are finished with the task, then we only have to clean the mess after us, so we just release the UTF string we recently allocated:

```cpp
env->ReleaseStringUTFChars(native_cone, name);
```

{% hint style="danger" %}
Always call the `ReleaseStringUTFChars(...)` function for every `GetStringUTFChars(...)` allocation before leaving the function, otherwise it will cause memory leak, just like when somebody forgot to call `delete` after `new`.
{% endhint %}

What if we have to return some value? Let us see the next function: `getParameters(...)`. The first half of the procedure goes exactly the same as in the case when we are not interested in any returning value. However, when we are, then we should ask for it on the same place where we set the parameters in the previous example:

```cpp
ape::GeometryConeParameters coneParameters;
if(auto coneGeometryShared = std::static_pointer_cast<ape::IConeGeometry>(jniPlugin->getSceneManager()->getEntity(std::string(name)).lock()))
{
    coneParameters = coneGeometryShared->getParameters();
}
```

Good. Then we release the allocated utf chars, and then we can return a value. As it was said, our goal is to always cope with primitve values when it is possible, becouse calling Java constructors from JNI-functions can be really expensive. Thus complex types (like `ape::GeometryConeParameters`) are returned as arrays. So in our case this complex type in C++ looks like the following `struct`:

```cpp
struct GeometryConeParameters
{
	float radius;
	float height;
	float tile;
	ape::Vector2 numSeg;

	/* constructors, etc. ... */
};
```

This means we will return 3 + 2 float value. So first we create a buffer array:

```cpp
float outArrayBuf[] = {
    coneParameters.radius,
    coneParameters.height,
    coneParameters.tile,
    coneParameters.num_seg_x,
    coneParameters.num_seg_y
};
```

Then we create a new `jfloatArray` with the help of the `env` pointer, and then place this buffer into the newly create `jfloatArray`, and then just return it:

```cpp
jfloatArray jOutArray = env->NewFloatArray(5);
env->SetFloatArrayRegion(jOutArray,0,5,outArrayBuf);

return jOutArray;
```

Now do this for each of the member functions. Then if it is done, one last thing remained: we have to include our new **apeJNIConeGeometry.cpp** file into the cmake project.

To do this open the **CMakeLists.txt** file, and add the **apeJNIConeGeometry.cpp** to the `SOURCE` list:

```cpp
set(SOURCES
        /* ...
         * other cpp files
         * ...
         */
        apeJNIConeGeometry.cpp
        )
```

## Creating wrapper class

Wrapper classes are stored in the `org.apertusvr` package. So navigate to the corresponding folder, and create the new file for the wrapper class, as **apeConeGeometry.java**!

In the C++ API the `ape::IConeGeometry` is a subclass of the `ape::Geometry` interface. We have to follow this structure here too. We assume that somebody already created the `apeGeometry` wrapper for us, so we just type:

{% code title="apeConeGeometry.java" %}

```java
package org.apertusvr;

public class apeConeGeometry extends apeGeometry {
    // ...
}
```

{% endcode %}

#### Parameter types

For the complex parameter types in the C++ API, we often create a similar complex type as the subclass of the entity interface. In our case this will look like:

```java
public class GeometryConeParameters
{
    public float radius;
    public float height;
    public float tile;
    public apeVector2 numSeg;

    /* ordinal constructors */ 

    GeometryConeParameters(@Size(5)float[] paramArray) {
        radius = paramArray[0];
        height = paramArray[1];
        tile = paramArray[2];
        numSeg = new apeVector2(paramArray[3],paramArray[4]);
    }
}
```

{% hint style="info" %}
We always make a constructor whose parameter is an array if the values are from the same primitive type. This makes it easier to constructing from the returned array from ApertusJNI.
{% endhint %}

#### Constructor

The interfaces only store the name and the type of the given entity. This two value are also an obligatory, otherwise we could not identify the entities without them. Thus, we only write constructors where this two parameters are given to the `apeEntity` superclass. In our case, we now that we are working with a `ConeGeometry`, so we make a constructor with only one String parameter, and call the superclass's constructor with this String and the `apeEntity.Type.GOMETRY_CONE` value:

```java
public apeConeGeometry(String name) {
    super(name, Type.GEOMETRY_CONE);
}
```

#### Member functions

The wrapper's member functions simply call the corresponding static native function in ApertusJNI, with the parameters they got from the user, and with the name which is stored in the wrapper. So in the case of the two function whose implementation were discussed in the previous section, looks like this:

```java
public void setParameters(float radius, float height, float tile, apeVector2 numSeg) {
    ApertusJNI.setConeGeometryParameters(mName,radius,height,tile,numSeg.x, numSeg.y);
}

public GeometryConeParameters getParameters() {
    return new GeometryConeParameters(ApertusJNI.getConeGeometryParameters(mName));
}
```

The header of the wrapper's member function are always the some (or equivalent) as in the C++ API. This means here we do not use separate `float x` and `float y`, but an `apeVector2` (which is part of the Java API).

#### Builder class

For entities like `apeConeGeometry`, we should always provide a Builder class, which is an implementation of the `apeBuilder` interface. This is really important if we want to cast from an `apeEntity` or other superclass, because we have to instantiate one for it.

This builder class is responsible for creating an instance of the wrapper when it appears as a template parameter. So without further explanation, the builder class looks like this:

```java
public static class apeConeBuilder implements apeBuilder<apeConeGeometry> {

    @Override
    public apeConeGeometry build(String name, Type type) {
        if (type == Type.GEOMETRY_CONE) {
            return new apeConeGeometry(name);
        }

        return null;
    }
}
```
