Skip to content

carnegierobotics/vephor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

vephor

A header-only headless visualization library with an OpenGL backend

Summary

Vephor allows for interactive visualizations in situations where visualizations are infeasible, such as on headless systems, or where it is inconvenient to include graphics libraries.

Vephor creates graphics library agnostic visualization definitions before either saving them to disk for later viewing or sending them to a visualizer elsewhere for immediate viewing.

When running in server mode, Vephor visualization code is meant to be as dormant as possible, only using resources when a client connects and requests the visualization.

Vephor supports both C++ and Python.

Dependencies

Vephor requires the following dependencies

Dependency [version] Core OpenGL
Eigen [≥ 3.3.0] ✔️ ✔️
manif ✔️ ✔️
GLEW ❌ ✔️
GLFW ❌ ✔️

See core/README.md and opengl/README.md for more details on separated dependencies.

vcpkg

All required dependencies can be installed via vcpkg if you opt to build the software with their toolchain.

This requires that you have vcpkg installed on your system. If not yet installed, you may install as follows. See the official documentation for additional details and instructions for Windows systems. Install vcpkg in a persistent directory on your system.

cd <persistent-directory>

# Download and bootstrap vcpkg
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh

# Set VCPKG_ROOT and prepend it to your path
vcpkg_root=$(pwd)
echo "export VCPKG_ROOT=$vcpkg_root" >> ~/.bashrc
echo "export PATH=\${VCPKG_ROOT}\${PATH:+:\${PATH}}" >> ~/.bashrc

Linux

Refer to the following instructions to manually install dependencies on an Ubuntu system. Feel free to adapt to your distro and package manager of choice.

Eigen

sudo apt install libeigen3-dev

manif

Note that manif depends on Eigen. If Eigen is installed in a non-standard location, be sure to provide a CMAKE_PREFIX_PATH when configuring.

git clone https://github.com/artivis/manif.git
cd manif
cmake -S . -B build
cmake --build build
sudo cmake --install build

GLEW

sudo apt install libglew-dev

GLFW

sudo apt install libglfw3-dev

C++ Install

Below are supported methods for building and installing the Vephor C++ library.

vcpkg

Build and install as described in the following sections but with the additional CMake configuration flag -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake. Alternately, for Linux systems, build with the --preset=vcpkg flag. See the following example configure steps.

cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake ...
cmake -S . -B build --preset=vcpkg ...  # Linux only

Linux

cd <vephor-root>

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
sudo cmake --install build

Windows

cd <vephor-root>

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=c:/msys64/mingw64
cd build
mingw32-make
sudo mingw32-make install

Python Install

Install the Vephor library Python bindings in the active Python environment as follows.

Linux

pip install --upgrade pip
pip install .

Windows

pip install . --config-settings=cmake.generator="MinGW Makefiles"

Without OpenGL

pip install . --config-settings=cmake.define.VEPHOR_BUILD_OPENGL=OFF

or

pip install python/no-opengl

Troubleshooting

Refer to the contained sections for common Python installation issues.

Conda Environment

Conda employs their own set of compiler tools over those installed on the system. There is a known compatability bug between these tools and modern versions of the GCC compiler (see ContinuumIO/anaconda-issues#11152). If you encounter linker errors when installing (e.g. those shown below), then attempt one of the solutions described here.

In particular, this solution is recommended:

conda install -c conda-forge ld_impl_linux-64  # Modify the suffix to correspond with your platform

If, on importing vephor, you have an error complaining about a missing GLIBCXX version, try this:

conda install -c conda-forge libstdcxx-ng
Example linker error
/home/username/conda/envs/my_env/compiler_compat/ld: warning: libGLdispatch.so.0, needed by /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libGL.so, not found (try using -rpath or -rpath-link)
/home/username/conda/envs/my_env/compiler_compat/ld: warning: libGLX.so.0, needed by /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libGL.so, not found (try using -rpath or -rpath-link)
/home/username/conda/envs/my_env/compiler_compat/ld: warning: libxcb.so.1, needed by /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so, not found (try using -rpath or -rpath-link)
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_get_maximum_request_length'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libGL.so: undefined reference to `__glDispatchRegisterStubCallbacks'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libGL.so: undefined reference to `__glXGLLoadGLXFunction'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_poll_for_queued_event'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_wait_for_event'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_get_setup'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_poll_for_event'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_connection_has_error'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libGL.so: undefined reference to `__glDispatchFini'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_take_socket'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_disconnect'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_generate_id'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libGL.so: undefined reference to `__glDispatchUnregisterStubCallbacks'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libGL.so: undefined reference to `__GLXGL_CORE_FUNCTIONS'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libGL.so: undefined reference to `_glapi_tls_Current'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_parse_display'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libGL.so: undefined reference to `__glDispatchInit'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_wait_for_reply64'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_poll_for_reply64'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_connect'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_writev'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_connect_to_display_with_auth_info'
/home/username/conda/envs/my_env/compiler_compat/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/libX11.so: undefined reference to `xcb_get_file_descriptor'
collect2: error: ld returned 1 exit status

API - TLDR

The Window class is the primary way to interact with the library. Objects that implement certain render functions can be added to the Window, yielding a RenderNode that can be used to control the object's pose, scale, visibility, and which allows object destruction.

w = Window()
s = Sphere()
node = w.add(s)
node.setScale(2.0)
w.render()

For 2d plotting, the Plot class is the recommended way to interact with the library. This presents an interface similar to matplotlib, though the underlying Window can also be accessed.

p = Plot()
p.plot([0,10],[0,10])
p.scatter([0,10],[10,0])
p.show()

The list of supported objects and function calls can be found farther down.

Examples

Note that the following examples alternate between C++ and Python, but all features are available in both.

Viz by process creation

This is the default mode for viewing Vephor visualizations - your visualization will be written to a temporary location on disk, and a vephor_show process will be created and pointed to that location. Note that these visualizations are not interactive apart from allowing camera movement - only network based visualizations allow the creator to respond to callbacks.

#include "vephor_ext.h"

using namespace vephor;

int main()
{
	Window window;
	
	window.add(make_shared<Axes>(), Vec3(500,-1000,2000));
	window.add(make_shared<Sphere>(), Vec3(495,-1000,2000));
	window.add(make_shared<Cone>(), Vec3(505,-1000,2000));
	window.add(make_shared<Cylinder>(), Vec3(500,-995,2000));
	window.add(make_shared<Cube>(), Vec3(500,-1005,2000));
	
	window.render();

	return 0;
}

Viz by saving to a file

#! /usr/bin/env python3

import vephor as v4
import numpy as np

w = v4.Window()

points = []
for i in range(0,100):
    for j in range(0,100):
        points.append((i,j,0))
points = np.array(points)

p = v4.Particle(points)
p.setScreenSpaceMode()
p.setSize(0.001)
w.add(p)
w.save("test_scene")

This will create a folder called test_scene. The visualization can then be viewed by calling:

vephor_show test_scene

Viz by server

The following code will open a server on the default Vephor port, 5533. The port can be set when enabling server mode.

In addition to server use, this example also shows how to allow the client to continue updating the visualization. The visualization will run separately from any client being connected, and a new client will begin visualizing at the current state of the client.

Note that multiple clients can connect to one server.

#include "vephor_ext.h"

using namespace vephor;

int main()
{
	Window window;
	
	window.setServerMode();
	
	double obj_mesh_dist = 5.0f;
	
	
	// Make a "clock"
	for (int i = 1; i <= 12; i++)
	{
		float angle = i / 12.0 * 2 * M_PI;
		
		Vec3 dir = Vec3(cos(angle), sin(angle), 0);
		auto text = make_shared<Text>(std::to_string(i));
		text->setAnchorCentered();
		window.add(text, dir * obj_mesh_dist * 1.5);
		
		vector<Vec3> verts = {
			Vec3(0,0,0),
			dir * obj_mesh_dist * 1.5
		};
		window.add(make_shared<Lines>(verts), Vec3::Zero());
	}
	
	// Use a mesh for the clock hand
	string base_asset_dir = getBaseAssetDir();
	auto obj_mesh = make_shared<ObjMesh>(base_asset_dir+"/assets/pyramid.obj");
	auto obj_mesh_node = window.add(obj_mesh, Transform3(Vec3(obj_mesh_dist,0,0)));
	
	float angle = 0;
	while (true)
	{
		angle += 0.01;
		obj_mesh_node->setPos(Vec3(cos(angle), sin(angle), 0) * obj_mesh_dist);
		obj_mesh_node->setOrient(Vec3(0,0,angle+M_PI));
		
		window.render(false /*wait*/);
		
		std::this_thread::sleep_for(std::chrono::milliseconds((int)(33)));
	}

	return 0;
}

Once this server is running, connect to it using:

vephor_show -m client

Viz by server (bring your own client)

"Bring your own client" mode allows you to create interactive visualizations without needing to separately call vephor_show. It will create a server and also create a client process to talk to that server.

TODO

Transform tree

TODO

2d plotting

TODO

Image plotting

#! /usr/bin/env python3

import os

import vephor as v4
import numpy as np

image = np.zeros((16,16,3))
image[:8,8:] = np.array((0.5,0.5,0.5))
image[8:,:8] = np.array((0.75,0.75,0.75))
image[8:,8:] = np.array((1,1,1))

plt = v4.Plot()
plt.imshow(image)
plt.show()

Image plotting - OpenCV images in C++

TODO

API - Exhaustive

Here are, as compactly as possible, all the relevant functions this library provides. This is meant to give an overview - see source for explicit data types.

WindowWindowCreate a window. Width/height of (-1,-1) means the window should fill the screen.(width=-1, height=-1, title="show")
addAdd an object to the window for rendering.(obj, transform=identity, overlay=false, layer=0)
canRenderCheck if rendering is possible, for example when a client is connected in server mode.
renderRender current window contents.(wait_close=true, wait_key=false)
saveSave current window contents to a file.(path)
clearRemove all current window contents.
setTitleSet window title.(title)
setTrackballModeSet up the standard 3d camera mode.(to=(0,0,0), from=(-1,0,-1), up=(0,0,-1), use_3d=false)
setPlotModeSet up the 2d plotting camera mode.equal=false
setServerModePut the window in server mode.(wait=false, port=5533, record_also=false, record_path="", metadata=default)
setServerModeBYOCPut the window in server mode, and spawns a client to connect.(record_also=false,record_path="")
setRecordModePut the window in record mode.(path="")
checkAndConsumeFlagCheck a server metadata flag, and consume it if it is a one-shot flag.(flag)
setKeyPressCallbackSet key press callback function.(callback)
RenderNode
getPosGet position of node in parent frame.
setPosSet position of node in parent frame.(pos_in_world)
getOrientGet rotation that rotates node frame to parent frame.
setOrientSet rotation that rotates node frame to parent frame.(parent_from_node_rotation)
getTransformGet transform that transforms node frame to parent frame.
setTransformSet transform that transforms node frame to parent frame.(parent_from_node)
getScaleGet scale of the node.
setScaleSet scale of the node.(scale)
setParentSet the transform parent of this node. Replaces world frame as parent.(parent)
getShowGet whether node should be shown.
setShowSet whether node should be shown.(show)
getDestroyGet whether node has been destroyed.
setDestroySet node to be destroyed.
PlotPlotCreate a plot.(title)
plotPlot a continuous line.(x_list, y_list, options)
scatterPlot a set of points.(x_list, y_list, options)
showRender current plot contents.(wait_close=true, wait_key=false)
saveSave current window contents to a file.(path)
clearRemove all current plot contents.
titleSet plot title.(title)
xlabelSet x axis label.(label)
ylabelSet y axis label.(label)
equalSet plot axes to have equal scales.(is_equal)
limitsSet plot limits.(min_x, max_x, min_y, max_y)
textAdd text to the plot.(text, height, pos, color)
polygonAdd a polygon to the plot. 0 thickness for a thin line border, -1 for filled.(verts, color, thickness=0)
circleAdd a circle to the plot. 0 thickness for a thin line border, -1 for filled.(center, rad, color, thickness=0, slices=16)
rectAdd a rectangle to the plot. 0 thickness for a thin line border, -1 for filled.(center, width, height, color, thickness=0
lineAdd a line to the plot.(vert_list, color, thickness)
imshowAdd an image to the plot.(image, nearest_filtering=false, pos=(0,0))
ArrowArrowCreate an arrow.(start, end, rad=1.0, slices=16)
setColorSet color.(color)
AxesAxesCreate a set of axes.(size=1.0)
CircleCircleCreate a circular disc with a z axis normal. Outer rim of the disc is set by rad, inner rim is thickness units inwards.(rad=1.0, thickness=1.0, slices=16)
setColorSet color.(color)
ConeConeCreate a cone with the flat surface in the z=0 plane, with the top height units along the z axis.(rad=1.0, height=1.0, slices=16)
setColorSet color.(color)
CubeCubeCreate a cube.(rad)
setColorSet color.(color)
CylinderCylinderCreate a cylinder along the z axis, with half of height on either side.(rad=1.0, height=1.0, slices=16)
setColorSet color.(color)
GridGridCreate a grid.(rad, normal=(0,0,1), right=(1,0,0), cell_size=1.0)
setColorSet color.(color)
LinesLinesCreate a line.(vert_list, color_list)
setColorSet color.(color)
setLineStripSet if verts form a continuous line - if false, each pair is a separate line. Initial state is a continuous line.(is_strip)
MeshMeshCreate a mesh.(mesh_data)
setTextureSet texture for this mesh.(image, nearest_filtering=false)
setColorSet color.(color)
setSpecularSet whether specular highlights should be used.(specular)
setCullSet whether faces should be culled if facing away from the camera.(cull)
ObjMeshObjMeshCreate geometry using a .obj mesh file.(path)
setColorSet color.(color)
ParticleParticleCreate a point cloud.(vert_list, color_list)
setSizeSet size of each particle.(size)
setColorSet color.(color)
setTextureSet texture for each particle.(image, nearest_filtering=false)
setScreenSpaceModeSet whether particle size is in metric space or screen space. If in screen space, size is taken as a screen space portion from 0 to 1.(is_screen_space)
PlanePlaneCreate a rectangular plane segment.(rads)
setColorSet color.(color)
setTextureSet texture for each particle.(image, nearest_filtering=false)
SphereSphereCreate a sphere.(rad=1.0, slices=16, stacks=16)
setColorSet color.(color)
SpriteSpriteCreate a screen-facing rectangle bearing an image.(image, nearest_filtering=false)
setColorSet color.(color)
setFlipSet whether image should be flipped vertically.(flip)
setNormalSpriteSheetSet normal texture for the sprite.(image, nearest_filtering=false)
TextTextCreate text. Initial zero point is at the bottom left.(text)
setColorSet color.(color)
setAnchorBottomLeftSet zero point of text to the bottom left.
setAnchorLeftSet zero point of text to the left.
setAnchorTopLeftSet zero point of text to the top left.
setAnchorBottomSet zero point of text to the bottom middle.
setAnchorCenteredSet zero point of text to the middle.
setAnchorTopSet zero point of text to the top middle.
setAnchorBottomRightSet zero point of text to the bottom right.
setAnchorRightSet zero point of text to the right.
setAnchorTopRightSet zero point of text to the top right.
ThickLinesThickLinesCreate a set of lines with consistent screen space thickness.(vert_list, color_list)
setColorSet color.(color)
setLineWidthSet line width in screen space portion.(line_width)
Loose functionsTODO
MeshDataTODO
formLineMake mesh data for a 3d line with some thickness.(vert_list, rad)
formLineLoopMake mesh data for a 3d line with some thickness. Same as formLine, but makes the line into a loop by repeating the first vertex.(vert_list, rad)
formPolygonMake mesh data for an XY plane polygon.(vert_list)
formCubeMake mesh data for a cube.
formSphereMake mesh data for a sphere.(slices, stacks)
formConeMake mesh data for a cone.(rad, height, slices, smooth=true)
formCylinderMake mesh data for a cylinder.(rad, height, slices, smooth=true)
formPlaneMake mesh data for an XY plane segment.(rads)
formCircleMake mesh data for an XY plane circle.(rad, thickness, slices)
formHeightMapMake mesh data for a height map.(height, res, uv_callback=none)
ImageTODO
generateSimpleImage
generateGradientImage
generateCheckerboardImage
loadImage
saveImage

About

Vephor, a headless visualization library

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •