Introduction to VTS-Browser-CPP¶
This guide shows how to build a simple C++ front-end application build on VTS.
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 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.
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
.
Next, 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/log.hpp>
#include <vts-browser/map.hpp>
#include <vts-browser/mapOptions.hpp>
#include <vts-browser/camera.hpp>
#include <vts-browser/navigation.hpp>
#include <vts-renderer/renderer.hpp>
#include <thread>
#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>
SDL_Window *window;
SDL_GLContext renderContext;
SDL_GLContext dataContext;
std::shared_ptr<vts::Map> map;
std::shared_ptr<vts::Camera> cam;
std::shared_ptr<vts::Navigation> nav;
std::shared_ptr<vts::renderer::RenderContext> context;
std::shared_ptr<vts::renderer::RenderView> view;
std::thread dataThread;
bool shouldClose = false;
void dataEntry()
{
vts::setLogThreadName("data");
// the browser uses separate thread for uploading resources to gpu memory
// this thread must have access to an OpenGL context
// and the context must be shared with the one used for rendering
SDL_GL_MakeCurrent(window, dataContext);
vts::renderer::installGlDebugCallback();
// this will block until map->renderFinalize
// is called in the rendering thread
map->dataAllRun();
SDL_GL_DeleteContext(dataContext);
dataContext = nullptr;
}
void updateResolution()
{
int w = 0, h = 0;
SDL_GL_GetDrawableSize(window, &w, &h);
auto &ro = view->options();
ro.width = w;
ro.height = h;
cam->setViewportSize(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);
// enable sharing resources between multiple OpenGL contexts
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
// 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 contexts
vts::log(vts::LogLevel::info3, "Creating OpenGL contexts");
dataContext = SDL_GL_CreateContext(window);
renderContext = SDL_GL_CreateContext(window);
SDL_GL_SetSwapInterval(1); // enable v-sync
// create instance of the vts::Map class
map = std::make_shared<vts::Map>();
// make vts renderer library load OpenGL function pointers
// this calls installGlDebugCallback for the current context too
vts::renderer::loadGlFunctions(&SDL_GL_GetProcAddress);
// create the renderer library context
context = std::make_shared<vts::renderer::RenderContext>();
// set required callbacks for creating mesh and texture resources
context->bindLoadFunctions(map.get());
// launch the data thread
dataThread = std::thread(&dataEntry);
// create a camera and acquire its navigation handle
cam = map->createCamera();
nav = cam->createNavigation();
// create renderer view
view = context->createView(cam.get());
// initialize the map for rendering
updateResolution();
map->renderInitialize();
// pass a mapconfig url to the map
map->setMapconfigPath("https://cdn.melown.com/mario/store/melown2015/"
"map-config/melown/Melown-Earth-Intergeo-2017/mapConfig.json");
// acquire current time (for measuring how long each frame takes)
uint32 lastRenderTime = SDL_GetTicks();
// main event loop
while (!shouldClose)
{
// process events
{
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))
nav->pan(p);
if (event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
nav->rotate(p);
} break;
case SDL_MOUSEWHEEL:
nav->zoom(event.wheel.y);
break;
}
}
}
// update navigation etc.
updateResolution();
uint32 currentRenderTime = SDL_GetTicks();
map->renderUpdate((currentRenderTime - lastRenderTime) * 1e-3);
cam->renderUpdate();
lastRenderTime = currentRenderTime;
// actually render the map
view->render();
SDL_GL_SwapWindow(window);
}
// release all
view.reset();
nav.reset();
cam.reset();
map->renderFinalize();
dataThread.join();
context.reset();
map.reset();
SDL_GL_DeleteContext(renderContext);
renderContext = nullptr;
SDL_DestroyWindow(window);
window = nullptr;
return 0;
}
You may download the source: srcs/frontend-cpp/main.cpp
.