Introduction to VTS-Browser-CPP

This guide shows how to build a simple C++ front-end application build on VTS.

../_images/frontend-cpp.png

What we should have in the end.

The application will create a window and show a single map configuration. You may navigate on the map with mouse.

This tutorial uses source code from vts-browser-minimal example, which is part of the git repository.

Dependencies

VTS Browser

The library is available as a package on some popular linux distributions. Instructions to add the source-lists are at OSS. After that, install the developer files for the library and optionally the debug symbols.

apt-get install libvts-browser-dev libvts-browser-dbg

If the package is not available for your distribution, you may build the library from source code. See Building for more information. After that just install the library locally.

sudo cmake --build . --target install

Source code for the library is available at GitHub.

SDL

SDL is portable library for window creation, OpenGL context creation and event handling.

For instructions on installation see SDL.

Note

We use SDL in this example to keep it simple. However, you are free to use the browser library with any OpenGL context, no matter where you get it.

Cmake

Cmake is platform-independent project configuration tool that generates platform (or IDE) specific project files.

For installation instructions see Cmake.

Building

First, we write a simple cmake script called CMakeLists.txt. It will search for the actual paths to all the required libraries. Next it specifies to build an executable program called vts-example. The program uses single source file called main.cpp and is linked with all the libraries.

CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)
project(vts-example CXX)
set(CMAKE_CXX_STANDARD 11)

find_package(VtsBrowser REQUIRED)
include_directories(${VtsBrowser_INCLUDE_DIR})
find_package(VtsRenderer REQUIRED)
include_directories(${VtsRenderer_INCLUDE_DIR})
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIR})

add_executable(vts-example main.cpp)
target_link_libraries(vts-example ${VtsBrowser_LIBRARIES} ${VtsRenderer_LIBRARIES} SDL2)

You may download the file: srcs/frontend-cpp/CMakeLists.txt.

Now, we let cmake generate the platform specific build files (usually a makefile on linux). This step is only done once.

Moreover, we do not want to clutter the directory with numerous temporary files, therefore, we instruct cmake to build in a separate directory.

mkdir build
cd build
cmake ..

After that, just call the following command every time you want to rebuild the application (from inside the build directory).

cmake --build .

This cmake call will use the generated build files. Alternatively, you may use them directly.

Source Code

main.cpp:

#include <vts-browser/map.hpp>
#include <vts-browser/options.hpp>
#include <vts-browser/log.hpp>
#include <vts-browser/fetcher.hpp>
#include <vts-renderer/renderer.hpp>

#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>

SDL_Window *window;
SDL_GLContext renderContext;
vts::renderer::Renderer render;
std::shared_ptr<vts::Map> map;
bool shouldClose = false;

void updateResolution()
{
    vts::renderer::RenderOptions &ro = render.options();
    SDL_GL_GetDrawableSize(window, (int*)&ro.width,
                                   (int*)&ro.height);
    map->setWindowSize(ro.width, ro.height);
}

int main(int, char *[])
{
    // initialize SDL
    vts::log(vts::LogLevel::info3, "Initializing SDL library");
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0)
    {
        vts::log(vts::LogLevel::err4, SDL_GetError());
        throw std::runtime_error("Failed to initialize SDL");
    }

    // configure parameters for OpenGL context
    // we do not need default depth buffer, the rendering library uses its own
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
    SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
    SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    // use OpenGL version 3.3 core profile
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
                        SDL_GL_CONTEXT_PROFILE_CORE);

    // create window
    vts::log(vts::LogLevel::info3, "Creating window");
    {
        window = SDL_CreateWindow("vts-browser-minimal-cpp",
            SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
            800, 600,
            SDL_WINDOW_MAXIMIZED | SDL_WINDOW_OPENGL
            | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN);
    }
    if (!window)
    {
        vts::log(vts::LogLevel::err4, SDL_GetError());
        throw std::runtime_error("Failed to create window");
    }

    // create OpenGL context
    vts::log(vts::LogLevel::info3, "Creating OpenGL context");
    renderContext = SDL_GL_CreateContext(window);
    // bind the OpenGL context to current thread
    SDL_GL_MakeCurrent(window, renderContext);
    SDL_GL_SetSwapInterval(1); // enable v-sync

    // notify the vts renderer library on how to load OpenGL function pointers
    vts::renderer::loadGlFunctions(&SDL_GL_GetProcAddress);

    // and initialize the renderer library
    // this will load required shaders and other local files
    render.initialize();

    // create instance of the vts::Map class
    map = std::make_shared<vts::Map>(vts::MapCreateOptions(),
                vts::Fetcher::create(vts::FetcherOptions()));

    // set required callbacks for creating mesh and texture resources
    render.bindLoadFunctions(map.get());

    // initialize the render preparation component of the map
    updateResolution();
    map->renderInitialize();
    map->dataInitialize();

    // configure an url to the map that should be displayed
    map->setMapConfigPath("https://cdn.melown.com/mario/store/melown2015/"
            "map-config/melown/Melown-Earth-Intergeo-2017/mapConfig.json");

    // acquire current time to measure how long each frame takes
    uint32 lastRenderTime = SDL_GetTicks();

    // keep processing window events
    while (!shouldClose)
    {
        {
            SDL_Event event;
            while (SDL_PollEvent(&event))
            {
                switch (event.type)
                {
                // handle window close
                case SDL_APP_TERMINATING:
                case SDL_QUIT:
                    shouldClose = true;
                    break;
                // handle mouse events
                case SDL_MOUSEMOTION:
                {
                    // relative mouse position
                    double p[3] = { (double)event.motion.xrel,
                                (double)event.motion.yrel, 0 };
                    if (event.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT))
                        map->pan(p);
                    if (event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
                        map->rotate(p);
                } break;
                case SDL_MOUSEWHEEL:
                    map->zoom(event.wheel.y);
                    break;
                }
            }
        }

        // update downloads
        map->dataTick();

        // update navigation etc.
        uint32 currentRenderTime = SDL_GetTicks();
        map->renderTickPrepare((currentRenderTime - lastRenderTime) * 1e-3);
        lastRenderTime = currentRenderTime;

        // prepare the rendering data
        updateResolution();
        map->renderTickRender();

        // actually render the map
        render.render(map.get());

        // present the rendered image to the screen
        SDL_GL_SwapWindow(window);
    }

    // release all rendering related data
    render.finalize();

    // release the map
    if (map)
    {
        map->dataFinalize();
        map->renderFinalize();
        map.reset();
    }

    // free the OpenGL context
    if (renderContext)
    {
        SDL_GL_DeleteContext(renderContext);
        renderContext = nullptr;
    }

    // release the window
    if (window)
    {
        SDL_DestroyWindow(window);
        window = nullptr;
    }

    return 0;
}

You may download the source: srcs/frontend-cpp/main.cpp.

Conclusion

Complete documentation for the browser library is at wiki.