# dpLib

__dpLib__ is a software library for applying image correction based on the correction data generated by domeprojection.com ProjectionTools. __dpLib__ supports __DirectX 9__, __DirectX 11__, __DirectX 12__, __OpenGL__ and __Vulkan__.

## Dependencies

* [CodeMeter runtime](www.codemeter.de/de/service/downloads.html)

## Usage

To create and initialize the library a *dpContext* have to be created, which is necessary for any further function call. To create a *dpContext* call *dpCreateContext* somewhere in initialization part. After the context is created successful the correction settings have to be loaded from a configuartion file. This is done with a call to *dpLoadConfigurationFromFile*. After these both functions are called successful the *dpContext* is configured and ready to use.
Right before the engine starts rendering the scene call *dpSetActiveChannel* with the desired channel id to prepare the *dpContext*. Then call *dpPreDraw* to retrieve the correct projection matrix or frustum for further rendering. After the engine has rendered the whole scene a call to *dpPostDraw* applies the correction to the scene.
The following pseudo code snippet shows the general usage of __dpLib__.

```cpp
dpContext* g_pContext;
dpCreateContext(&g_pContext,...)
dpLoadConfigurationFromFile(g_pContext,...)

while(running)
{
    for each channel
    {
        dpSetActiveChannel(g_pContext,...)
        dpPreDraw(g_pContext,...)

        .. render the scene ..

        dpPostDraw(g_pContext,...)

        .. buffer swap ..
    }
}

dpDestroyContext(g_pContext)
```

### Coordinate System

__dpLib__ internally works with a right handed coordinate system which means **X** goes from left to right, **Y** from bottom to top and **Z** from front to back.

![coordinate system](coordinatesystem.png "dpLib coordinate system")

Heading/Yaw is the clockwise rotation around the Y-axis, pitch is the counter clockwise rotation around the X-axis and bank/roll is the counter clockwise rotation around the Z-axis.

The order of rotation is heading/yaw first, then pitch, then bank/roll. Assuming matY defines the rotation around the y-axis, matP defines the rotation around the x-axis and matR defines the rotation around the z-axis. The resulting rotation matrix gets calculated by matY * matP * matR.

### Configuration File

The following section describes the configuration file used by __dpLib__ in order to configure the correction data to use for each channel.

```xml
<?xml version="1.0" encoding="utf-8"?>
<dpCorrection>
    <!-- type of correction //-->
    <type>0</type>
    <!-- gamma used in ProjectionTools Mapper //-->
    <gamma>2.2</gamma>
    <!-- gamma for source and output //-->
    <input-gamma>2.2</input-gamma>
    <output-gamma>2.2</output-gamma>

    <!-- correction data per channel //-->
    <channel id="0" .../>
    <channel id="1" .../>
</dpCorrection>
```

The number of attributes for a channel tag depends on the selected correction type. __type__ and __gamma__ are set for all channels. To configure static correction the attributes *warpmap* and *frustum* are needed, for dynamic correction *shape* and *target* have to be configured. The attributes *blending*, *blacklevel* and *secondary-blending* are used in both cases.

* static correction
  * warpmap, blending, secondary-blending, blacklevel and frustum
* dynamic correction
  * blending, secondary-blending, blacklevel, shape and target

| tag | description |
| --- | ----------- |
| //__type__ | 0: static correction , 1:dynamic correction |
| //__gamma__ | the gamma value specified in ProjectionTools Mapper |
| //__input-gamma__ | the gamma value of the source image |
| //__output-gamma__ | the desired output gamma value |
| //__channel__[@*id*] | the channel number this tag describes |
| //__channel__[@*warpmap*] | warpmap specifying the correction to apply (for OpenGL only) (for static correction only) |
| //__channel__[@*frustum*] | virtual camera configuration (for static correction only) |
| //__channel__[@*blending*] | blending to apply (for OpenGL only) |
| //__channel__[@*secondary-blending*] | secondary blending to apply (for all graphics engines) |
| //__channel__[@*blacklevel*] | blacklevel to apply (for OpenGL only) |
| //__channel__[@*shape*] | mesh defining the screen shape (for dynamic correction only) |
| //__channel__[@*target*] | mesh defining the target rectangle (for dynamic correction only) |
| //__channel__[@*input-gamma*] | the input gamma value |
| //__channel__[@*output-gamma*] | the output gamma value |
| //__channel__[@*directional-shading*] | color correction texture to apply eyepoint dependent white-point correction (for dynamic correction only) |

All relative file paths are interpreted as relative to configuration file path. When 'c:/file/to/config.xml' is the path the configuration file is located, all relative paths are interpreted as relative to 'c:/file/to'. A relative path 'data/frustum_1.csv' is resolved to 'c:/file/to/data/frustum_1.csv'.

Here are a set of example configurations.

* [3 channel static correction](config_3ch_sta.xml "static correction")
* [3 channel dynamic correction](config_3ch_dyn.xml "dynamic correction")

### Generating Correction Data

Use the __domeprojectionSDK Exporter__ from within __Mapper3D__ or __MapperPM__ to generate valid correction datasets for the dpLib. This will include a configuration file paired with multiple data files for warping, blending etc..


## API Documentation

__dpLib__ provides for each graphics engine the same set of functions. According to the graphics engines specifics different parameters are nessecary.

An engine specific suffix is appended to the function names, which is abbreviated in the following documentation as [API]:

Engine Name | Suffix
----------- | -------
DirectX 9   | D3D9
DirectX 11  | D3D11
DirectX 12  | D3D12
OpenGL      | OpenGL
Vulkan      | Vulkan

In general __dpLib__ provides the following set of functions. Each function returns a *dpResult* indicating success or failure. In case of failure, __dpGetErrorString__ returns a more detailed error description.

### Rendering

* dpResult __dpCreateContext[API]__( *dpContext*** ppContext, ...);
* dpResult __dpDestroyContext[API]__( *dpContext** pContext, ...);
* dpResult __dpLoadConfigurationFromFile[API]__( *dpContext** pContext, ...);
* dpResult __dpSetClippingPlanes[API]__( *dpContext** pContext, *float* cNear, *float* cFar);
* dpResult __dpSetActiveChannel[API]__( *dpContext** pContext, *unsigned int* channelId, ...);
* dpResult __dpSetFlipWarpmeshVerticesY[API]__( *dpContext** pContext, bool flip);
* dpResult __dpSetFlipWarpmeshTexcoordsV[API]__( *dpContext** pContext, bool flip);
* dpResult __dpPreDraw[API]__( *dpContext** pContext, *const dpVec3f* eyepoint, *dpVec3f** pOrientation, *dpMatrix4x4** pProjection, ...);
* dpResult __dpPreDraw1[API]__( *dpContext** pContext,*const dpVec3f* eyepoint, *dpCamera** pCamera);
* dpResult __dpPostDraw[API]__( *dpContext** pContext, ...)

### General

* dpResult __dpGetErrorString__( *dpContext** pContext, *char** pBuffer, *size_t* length);
* dpResult __dpGetVersion__( *int** pMajor, *int** pMinor, *int** pRevision, *int** pBuild);
* dpResult __dpGetVersionString__( *char** pBuffer, *size_t* length);

### Correction passes

__dpLib__ supports the following correction passes:

* dpWarpingPass: correction pass applying the warping
* dpBlendingPass: correction pass applying blending
* dpBlackLevelPass: correction pass applying black level correction
* dpSecondaryBlendingPass: correction pass applying a secondary blending
* dpDirectionalShadingPass: correction pass applying eyepoint-dependent white-point correction

Each pass can be enabled/disabled via:

* dpResult __dpSetCorrectionPass[API]__(*dpContext** pContext, *dpCorrectionPassType* pass, bool enabled);

The intensity of each pass can be gradually changed between 0 and 1 using the following function. This allows for example to crossfade between a daytime and nighttime blend file.

* dpResult __dpSetCorrectionPass1[API]__(*dpContext** pContext, *dpCorrectionPassType* pass, float value);

See [dpLib.h](../include/dpLib.h) for a more detailed function description.

## Licensing and pluginID

dpLib will show a watermark, if no license is present. Please contact VIOSO.com or domeprojection.com to get the correct pluginID.
The __pluginId__ necessary for creating the *dpContext*. It is a specific number to license your plugin via dpLib.
Best way to ensure licensing across all IGs is to use network licensing.
Install the license dongle on your master or operator machine, which also runs protection tools and enable the network server:

### Server Settings

Network based licensing must be activated on the computer with the dongle attached to. 
To enable the license server, open the CodeMeter Control Center and
select the preferences Menu option. A website with options will appear. Open
the Configuration→Server Configuration→Server Access page and enable the network server.

![Server congfiguration](server.png)

Please be aware that the CodeMeter needs to be restarted to activate the server
(reboot the PC if unsure how to achieve this).

### Client Settings

The SDK usually gets the license from the sever through a broadcast mechanism.
Additionally, (or in case of network issues), the location of the license server can
be configured on each PC running SDK. Open the CodeMeter Control center on
each PC and open the preferences webpage from there. Enter the IP-address of
the License Server in Configuration→Network and add it to the Server Search List.
Please note that the Internet Explorer which may be used by default will need an
exception to allow an input dialogue. There will be a notification you when needed.

![Client congfiguration](client.png)

Note: after starting it may take some seconds until the license is found. The
watermark will disappear then (license is checked periodically). Please
check your network and firewall settings, when further problems occur.
The web configuration of CodeMeter may not work with older browsers (typically
Internet Explorer - Windows 7). Use the following command line instead:

    "C:\Program Files (x86)\CodeMeter\Runtime\bin\"cmu32.exe --add-server SERVER-IP

# Building testcases

We use CMake build system with ninja.
We use CMake's find_package() to locate dependencies. So check CMake -S output to see missing dependencies.

## Dependencies

These dependencies are used by the testcases and need to be installed:
* GLFW3
* DXUT (to be removed)
* Vulkan-SDK, including SPIRV compiler

These dependencies come bundled in the external folder, no need to install:
* DirectX 9 SDK 
* glad
* GLM
* STB image

## Windows

Use Visual Studio 2022
In Visual Studio, open the base folder as "CMake project"
Install vcpkg to manage 
See, if there are dependencies missing and install them in case.
Configure a install destination and then install (cmake --install) will create a distributable package.

## Linux

Make sure you got all dependencies installed. Here are the commands to install on Ubuntu 22.
Make sure you have cmake and ninja installed.

```sh
wget -qO- https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo tee /etc/apt/trusted.gpg.d/lunarg.asc
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.3.290-jammy.list https://packages.lunarg.com/vulkan/1.3.290/lunarg-vulkan-1.3.290-jammy.list
sudo apt update
sudo apt upgrade
audo apt install cmake
audo apt install ninja-build
sudo apt install build-essential
sudo apt install vulkan-sdk
sudo apt install vulkan-validationlayers-dev
sudo apt install libglfw3-dev
```

Then cd to your base path and run cmake.

```sh
cmake -S . -B ./out/build/release -G Ninja -DCMAKE_MAKE_PROGRAM=ninja -DCMAKE_BUILD_TYPE=Release
cmake --build ./out/build/release --config Release
```

Demo applications are in:
./out/build/release
All dependencies are copied to allow the applications to run.
