How to

Display a mesh

File scene.hpp
scene_structure {
    // ...
    mesh_drawable my_shape_drawable;
}
File scene.cpp, function initialize()
void scene_structure::initialize() {
    
    // ...

    // Create a mesh
    mesh my_shape = mesh_primitive_sphere();

    // Initialize the mesh_drawable using the created mesh
    my_shape_drawable.initialize_data_on_gpu(my_shape);

    // Note:
    //   - 'my_shape' is a temporary variable, it will be removed from the memory at the end of the initialize() function
    //   - 'my_shape_drawable' is a variable of the class, it is persistent and can be used in the other methods of the class.

}
File scene.cpp, function display_frame()
void scene_structure::display_frame() {
    
    // ...

    // Create a mesh
    draw(my_shape_drawable, environment);
}

Apply a transformation to a mesh

Possibility 1: On the GPU via uniform parameter

The first possibility is to use the default uniform parameters [mesh_drawable].model associated to the mesh_drawable structure to apply a transformation at the drawing stage.
Example:
my_shape_drawable.model.translation = {1,0,0}; 
my_shape_drawable.model.rotation = rotation_axis_angle({0,1,0}, Pi/4.0f);
my_shape_drawable.model.scale = 2.0f;
[mesh_drawable].model is a structure of type affine allowing to set a value for a translation (vec3), a rotation (rotation_transform), and a homogeneous scaling (float).

Principle

my_shape_drawable.model is a simple variable storing a value for translation/rotation/scaling. It is conceptually similar to writing in a \(4 \times 4\) matrix in a more intuitive way.
When the function draw(my_shape_drawable, ...) is called, the parameter [mesh_drawable].model is used to generate a mat4 transformation which is sent as uniform parameter in the shader. Common shaders receives this parameters as uniform mat4 model; and apply the model matrix to all vertex positions.
Common use in the default shaders:
uniform mat4 model;
// ...
void main(){
    // The position of the vertex in the world space
    vec4 position = model * vec4(vertex_position, 1.0);

    // ...
}

Concept and Usage

Applying a transformation using my_shape_drawable.model is extremely efficient.
Additional remarks:
Example: This code only display my_shape_drawable with a translation of \((0,0,1)\), and not \((1,0,1)\).
my_shape_drawable.model.translation = {1,0,0}; 
my_shape_drawable.model.translation = {0,0,1}; // rewrite on a variable

draw(my_shape_drawable, environment);
Example: This code applies a translation of \((1,0,1)\) to my_shape_drawable.
my_shape_drawable.model.translation  = vec3{1,0,0} + vec3{0,0,1};

// Or alternatively 
my_shape_drawable.model.translation  = {1,0,0}; 
my_shape_drawable.model.translation += {0,0,1}; // add a value to the variable

draw(my_shape_drawable, environment);
Example: In the case of rotations and scaling:
my_shape_drawable.model.rotation  = rotation_1 * rotation_2; 
my_shape_drawable.model.scaling = scale_1 + scale_2;

draw(my_shape_drawable, environment);

Advanced:

For advanced use requiering more degrees of freedom than standard translation/rotation/scaling, a general mat4 structure is also provided as [mesh_drawable].supplementary_model_matrix. A mat4 structure allows more general transformation than a affine_rts, but is less intuitive to use. The final transformation sent to the shader is the matrix resulting from the product:
mat4 M = hierarchy_transform_model.matrix() * supplementary_model_matrix * model.matrix()
// model: the default way to apply a transformation on a mesh_drawable
// supplementary_model_matrix: the generic matrix allowing additional degrees of freedom.
// hierarchy_transform_model: matrix dedicated to handle hierarchical transforms.

Possibility 2: On the CPU, writing new positions

The second possibility is to directly modify the vertices of the mesh structure, on the CPU memory, before converting it to a mesh_drawable.
Example in the initialize() function:
for(int k=0; k<my_mesh.position.size(); ++k) {
	my_mesh.position[k] = //... any value or transformation
}

my_mesh_drawable.initialize_data_on_gpu(camel_mesh);

Concept and Usage

This method is usefull when the transformation is not a simple translation/rotation/scaling, but a more complex process that cannot be expressed as a combination of the standard transformation.
Although very generic, this approach is more costly than the use of the model variable. It requires to loop over all the vertices of the mesh and to perform the transformation on the CPU. This approach can be used at the initialization stage, but should be not be applied in the animation loop ([mesh_drawable].initialize_data_on_gpu should not be called in the animation loop).

Deform vertices of a mesh

Compute the normals

On a mesh structure

The mesh structure stores the set per-vertex normal in the variable [mesh].normal as numarray. mesh provides the function
[mesh].normal_update();
that computes automatically normals based on the position and connectivity of the triangles.
The per-vertex normal \(n_i\) is copmputed as \(n_i = \sum_{t\in \mathcal{N}_i} n_t ,\) with \(\mathcal{N}_i\) the set of triangles indices that share the vertex \(i\), and \(n_t\) is the per-triangle normal. Then \(n_i\) is normalized to have a unit length.

Update on a mesh_drawable

When the normals of a mesh_drawable need to be updated during the animation loop, the function [mesh_drawable].camel.vbo_normal.update([numarray]) can be called.
Example:
void scene_structure::display_frame()
{
    // Update some normals
    //   my_mesh.normal = ...

    // Then update the normals of the mesh_drawable
    my_mesh_drawable.vbo_normal.update(my_mesh.normal);
}
Remark: The numarray storing the normals on the CPU must be stored throughout the scene animation in order to apply such update.

Set a texture

Load a 2D texture from a file in "assets/my_texture.jpg", and assign it to a mesh_drawable:
[mesh_drawable].texture.load_and_initialize_texture_2d_on_gpu(project::path+"assets/my_texture.jpg");

Add an additional texture

Load an additional texture to be used in the shader under the uniform name "texture_2":
[mesh_drawable].supplementary_texture["texture_2"].load_and_initialize_texture_2d_on_gpu(project::path+"assets/your_texture.jpg");
See example Multi-texturing.

Use instancing

Calling the function
draw([mesh_drawable], environment, [int] N_instance);
automatically starts the drawing of N-instances of the mesh_drawable.
Remark: This call is conceptually similar to the following code, but is much more efficient in avoiding the sequential draw calls.
for(int gl_InstanceID=0; gl_InstanceID<N_instance; ++gl_InstanceID)
    draw([mesh_drawable], environment);
Additional links

Controlling instancing

The different instances can be controled within the shader via the built-in GLSL variable gl_InstanceID.
Additional per-instance data can be transfert from the C++ program to the GPU using the supplementary VBO data:
[mesh_drawable].initialize_supplementary_data_on_gpu([numarray<vec2/3/4>] data, /*location*/ 4, /*divisor*/ 1);
With the corresponding parameters in the shader
// Standard inputs
// layout (location = 0) in vec3 vertex_position;
// layout (location = 1) in vec3 vertex_normal;
// layout (location = 2) in vec3 vertex_color; 
// layout (location = 3) in vec2 vertex_uv; 

// Dedicated input for instanced data
layout (location = 4) in vec2/3/4 instanced_data; 
    • The location starts typically at 4 (after the standard position, normal, color, uv), and continue with 5, 6, etc.
    • - divisor = 0: is used when the array entries varies for each vertex (default value).
    • - divisor = 1: is used when the array entries varies for each isntance.

Activate transparency

Semi-transparency

Use the following code structure to activate color blending for semi-transparent objetcs:
void scene_structure::display_frame() {

    /** 1) Display first all non transparent objects: **/
    //   draw(my_shapes, ...);
    //   ... 
    // 

    /** 2) Display at the end semi-transparent objects **/

    // Color blending activation
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // Depth buffer writing de-activation
    glDepthMask(false); 

    // Optional: Set the global transparency value on the shape (applies on top of its color and texture)
    //  transparent_shape.material.alpha = 0.5f; //0:fully transparent, 1:fully opaque

    // Draw transparent shape
    draw(transparent_shape, environment);

    // Re-activate standard drawing parameters
    glDepthMask(true);
    glDisable(GL_BLEND);

}

Impostor effect

Impostor/Billboards with pure opaque and pure transparent textures can be handled in the shader using the discard call.
See the example Transparent Billboards

Load a mesh from a file

A simple default obj file loader is proposed within CGP with the following syntax
Example in the case of a mesh in 'assets/my_mesh.obj'
mesh my_mesh = mesh_load_file_obj(project::path + "assets/my_mesh.obj");
Remark:

Advanced loader

For the cases where multiple objects parts with different textures are in the same file, an advanced loader is also available using the following syntax:
Example with a file in assets/directory/filename.obj and its associated .mtl file:
std::vector<mesh_obj_advanced_loader::shape_element_node> struct_shape = 
  mesh_load_file_obj_advanced(project::path + "assets/directory/", "filename.obj");
std::vector<cgp::mesh_drawable> shapes_drawable = 
  mesh_obj_advanced_loader::convert_to_mesh_drawable(struct_shape);
- obj_advanced.hpp
20_format_parser/mesh_loader/obj_advanced/obj_advanced.hpp
Note: The advanced loader uses the external library tinyobjloader
See example Advanced OBJ Loader

Change the camera

Modify the user input