Quick Tour of the CGP Library base elements

Importing and using CGP

Import the header file "cgp/cgp.hpp" to access all functions and structure of CGP.
#include "cgp/cgp.hpp"

// All cgp functions are in the namespace cgp::
using namespace cgp;

int main()
{
    // declare two 3D vectors and display their sum on command line
    vec3 a = {1,2,3};
    vec3 b = {4,5,6};

    std::cout<< a + b <<std::endl; // Displays 5 7 9
}

Vectors and Matrices

vec

Basic structure and functions associated to 2/3/4D vectors are provided as vec2, vec3 and vec4 (following mostly GLSL naming convention). Components of vec are stored as float.
vec3 is conceptually similar to a struct { float x,y,z; } with direct access to its parameter (similarily for vec2/4).
struct vec2
float x
float y
struct vec3
float x
float y
float z
struct vec4
float x
float y
float z
float w

Conceptual structure of vec2/3/4

Declaring and accessing components

vec3 p = {1.1f, 2.0f, -2.5f}; // declaring a vector
// or vec3 p = vec3(1.1f, 2.0f, -2.5f);
// or vec3 p = vec3{1.1f, 2.0f, -2.5f};

p.x = 0.5f;   // Access component as .x, .y, .z
p[1] = -2.5f; // Access component as [0]/[1]/[2]
p.z = p[0]+1;

std::cout<<p<<std::endl; // Displays 0.5 -2.5 1.5

Standard vector operations (+, -, *, /)

vec3 p = {1.0f, -1.5f, 2.0f};
p = 4 * p;           // p = {4,-6,8}
p = p / 2.0f;        // p = {2,-3,4}
p = p + vec3(1,1,1); // p = {3,-2,5}
p = -p;              // p = {-3,2,-5}
p = p * vec3(2,1,-1) // p = {-6,2,5} componentwise multiplication

// Operators can be chained
vec3 p2 = ( 2*p + vec3(1,0,2)/2.0f ) / 1.2f;

Norm

vec3 a = {1,2,3};
float f = norm(a);      // norm ||a|| = sqrt(dot(a,a))
vec3  g = normalize(a); // return the unit norm vector a / ||a|| (doesn't change vector a)

Dot and cross products

vec3 a = {1,2,3};
vec3 b = {1,2,-1};

float d = dot(a, b);    // dot product a.b = 2
vec3  e = cross(a,b);   // cross product a x b = {-8,4,0}

mat

Similarily to vec, mat2 (2 \(\times\) 2 matrix), mat3 (3 \(\times\) 3), and mat4 (4 \(\times\) 4) structures are provided.

Initialization

mat3 A = { 1.1f, 2.5f, 2.0f,
          -2.1f, 4.1f, 1.5f,
            3.0f, 1.0f, 3.5f};

// Display matrix components
std::cout<< A <<std::endl;
Note that the initialization orders set the rows from left to right similarily to usual mathematical writing (at the opposite of glsl using column vectors).

Component access

mat can be accessed using (col,row) indexing similarily to usual mathematical notation.
// A(i,j) = A(column, row)
std::cout<< A(2,1) <<std::endl; // Displays 1.0

A(0,0) =  2.0f;
A(1,0) =  3.0f;
A(0,1) = -1.0f;
// Now A is the matrix
// A = { 2.0f, -1.0f, 2.0f,
//       3.0f,  4.1f, 1.5f,
//       3.0f,  1.0f, 3.5f};

// Indexing matrix as a contiguous vector using mat[i]
// A[0] == 2.0f
// A[1] == -1.0f
// ...
// A[8] == 3.5f

Access to row and col as vectors

// Access to row and column
vec3 c0 = A.col(0); // first column = {2,3,3}
vec3 r1 = A.row(1); // second row   = {3, 4.1, 1.5}

Products

// Matrix-vector product
vec3 x = {1,2,3};
vec3 y = A * x;   // = {12.1, 10.6, 15.5}

mat3 B; // default initialization as matrix identity

// Matrix product
mat3 C = A * B;

Linear Algebra

// Helper function
mat3 At = transpose(A); // matrix transpose
mat3 iA = inverse(A);   // matrix inverse: A*iA = identity
float d = det(A);       // matrix determinant = 13.1

Mesh and Display

Mesh

A structure mesh contains buffers of data to represent a triangulated shape with per vertex position, normal, color, and uv-coordinates. The data are stored as buffers on the CPU and can be directly accessed and modified in the code.
struct mesh
buffer<vec3> position
buffer<vec3> normal
buffer<vec3> color
buffer<vec2> uv

buffer<int3> connectivity

Data structure of a mesh
(uint3 is a triplet of positive integers)

Primitives

Basic triangulated primitives are pre-coded
mesh sphere_mesh = mesh_primitive_sphere();
mesh cylinder_mesh = mesh_primitive_cylinder();
mesh cube_mesh = mesh_primitive_cube();
...

Buffer access

mesh quad;
quad.position = { {0,0,0}, {1,0,0}, {1,1,0}, {0,1,0} };
quad.connectivity = { {0,1,2}, {0,2,3} };

quad.fill_empty_field();  // Fill all the buffers (normals, color, etc) with default values.

Mesh_drawable

The structure mesh_drawable automatically converts the buffers of mesh into GPU compatible VBOs. mesh_drawable can be displayed using the draw() function.
struct mesh_drawable
string name

// OpenGL IDs for display
map<string,integer> vbo
integer: vao, shader, texture, nbr_triangles

// Uniform parameters
affine_rts transform
shading_parameters shading

Conceptual structure of a mesh_drawable

Displaying a primitive

// Initialization
mesh_drawable sphere;
sphere.initialize( mesh_primitive_sphere() ); // Send the buffers of the mesh structure to the GPU

// In Animation Loop - call the display
draw(sphere, environment);
  // environment is a variable that contains the scene parameter (camera, light, etc.)

Uniform parameters

The structure mesh_drawable also stores a set of variables used for the position, orientation, and shading of the display shape. These variables are automatically sent as uniform variables to the shader when the function draw() is called.
// Display the sphere at its position translated by {3, -1, 2} 
sphere.transform.translate = { 3, -1, 2 };

// Set a uniform color to the sphere to red
sphere.shading.color = { 1, 0, 0 };

draw(sphere, environment);

Transformation

CGP provides a set of struct helping to parameterize affine transformation beyond the direct use of a \(4\times 4\) matrix.

Rotation

The struct rotation_transform acts as a helper to set and manipulate rotations. It stores internally a unit quaternion, but can be applied to a vector using the semantic of the matrix-vector product.
// Parameterize two rotations by an axis and an angle
rotation_transform R1 = rotation_transform::from_axis_angle({ 1,0,0 }, Pi / 6);
rotation_transform R2 = rotation_transform::from_axis_angle({ 0,1,0 }, Pi / 4);

// Product between rotation is the composition of the rotation
rotation_transform R = R1 * R2;

// Product rotation_structure * vec3 applies the rotation to the vector
vec3 rotated = R * vec3({ 1,1,1 });

// The 3x3 matrix of the rotation can also be extracted at any time from a rotation_transform
mat3 M = R.matrix();

Initializing a rotation

The rotation can be initialized using various parameterization:
// From a given axis (vec3) and angle (float)
rotation_transform::from_axis_angle(vec3{x,y,z}, float{angle}); 

// From a quaternion
rotation_transform::from_quaternion(quaternion{x,y,z,w}); 

// From a 3x3 matrix
rotation_transform::from_matrix(mat3{xx,xy,xz, yx,yy, ..., zz});
In addition, helper functions provides easy construction of rotation as mapping between unit vectors

rotation_transform::between_vector(vec3{ex,ey,ez}, vec3{etx,ety,etz});
rotation_transform::between_vector(e1, e2, e1t, e2t);

Affine transform

CGP provide also a set of helper structure to handle the combination of rotation, translation and scaling. Each transformation type is store and can be set individually, while the general structures can be combined together and apply to vectors as if it was a matrix.
The structure affine_rt (for Rotation-Translation) and affine_rts (for Rotation-Translation-Scaling) are available to this effect.
struct affine_rt
rotation_transform rotation
vec3 translation
struct affine_rts
rotation_transform rotation
vec3 translation
float scaling
affine_rts A;
A.rotation = rotation_transform::from_axis_angle({ 1,0,0 }, Pi / 4);
A.translation = { 1,1,2 };
A.scaling = 1.1f;

affine_rts B;
B.rotation = rotation_transform::from_axis_angle({ 0,1,0 }, Pi / 6);
B.translation = { 2,1,0 };
B.scaling = 2.0f;

// Combine the affine transforms using operator *
affine_rts C = A * B;

// The corresponding components of C can be obtained as
// C.rotation, C.translation, C.scaling

// Apply an affine transform to a vec3
vec3 p = C* vec3{ -3,2,0 };
//  assume that the vector is (-3,2,0,1) in homogeneous coordinates

// Retrieve the 4x4 matrix corresponding to the transform
mat4 T = C.matrix();
A possible use of rotation_rt is to encode in a concise way a rotation with respect to a point (different from the origin).
rotation_transform R; // Some rotation
vec3 c = { 1,2,3 }; // A position around which the rotation is applied

// Encode a "rotation around a given center"
affine_rt T = rotation_around_center(R, c);

// Apply the transformation to some coordinates
vec3 p = T * vec3{ 1.1f, 2.1f, 1.5f };

Containers

CGP library propose the use of generic containers for contiguous data in memory cgp::buffer (and cgp::buffer_stack) that are simple wrappers above std::vector (resp. std::array). The use of buffer and buffer_stack are similar to their std equivalent, but provide additional helpers such as enforced bound check when accessing an element, and operators *+-/ implemented by default over all the elements.
struct buffer<T>
std::vector<T> data
struct buffer_stack<T,int>
std::array<T,int> data

buffer and buffer_stack are similar to contiguous containers of std, but propose additional helper functions and error checkings.
  buffer<vec3> C;         // Similar to std::vector<vec3>
  C.push_back({ 0,3,1 }); // push-back similar to std::vector
  C.push_back({ { 2,3,1 }, { 1,2,0 }, { 2,1,1 } }); // Can add multiple elements at once
  std::cout << C << std::endl; // Default print-out on the terminal
  C[2] = { 3,2,1 }; // Element access (assert-check that the index 2 exists)
  C.resize(8);      // Resize similar to std::vector


  buffer_stack<vec2, 2> D; // Similar to std::array<vec2,2>
  D[0] = { 2,1 }; // Element access with bound check
  D[1] = { 4,5 };
  std::cout << D << std::endl; // Default print-out on the terminal