CGPdocs

Move, rotate and scale a shape

Goal: place the same cube at a given position, tilted and resized, by changing only its model transform.

Starting point: the default project template (src/scene.hpp, src/scene.cpp, see Your first scene). It already declares the shapes sphere and cylinder, a timer, and the members camera_control, environment and gui: reuse them, do not declare them again. The first comment of each code block says in which file and where its lines go: add means insert them, replace means replace the whole function by the one shown.

Interactive CGP demo (transform-a-shape). Open in a new tab.

The model transform of a drawable

Every mesh_drawable has a model member of type affine. It is sent to the shader as the model matrix at each draw call and applied to every vertex on the GPU:

p_world = translation + rotation * (scaling * scaling_xyz * p_local)
Member Type Default
model.translation vec3 {0,0,0}
model.rotation rotation_transform identity
model.scaling float (uniform scaling) 1
model.scaling_xyz vec3 (scaling per axis) {1,1,1}
T * p scales, rotates and then translates: affine_rt gives R p + t, affine_rts gives R s p + t, and affine (the type of model) gives R s S p + t, where S = diag(scaling_xyz) stretches the local axes of the shape before the rotation.
T * p scales, rotates and then translates: affine_rt gives R p + t, affine_rts gives R s p + t, and affine (the type of model) gives R s S p + t, where S = diag(scaling_xyz) stretches the local axes of the shape before the rotation. Tap to enlarge.

The mesh on the CPU is never modified: changing model is free and can be done at every frame.

1. Declare the drawable

// scene.hpp - add in scene_structure
mesh_drawable cube;

2. Create it and set its transform (once)

// scene.cpp - add at the end of initialize()
cube.initialize_data_on_gpu(mesh_primitive_cube({0,0,0}, 1.0f));

cube.model.translation = {1.0f, 0.5f, 0.0f};                              // position
cube.model.rotation = rotation_transform::from_axis_angle({0,0,1}, Pi/4); // 45 deg around z
cube.model.scaling = 0.5f;                                                // half size

rotation_transform::from_axis_angle(axis, angle) builds a rotation from an axis (normalized internally) and an angle in radians. The constant Pi is defined by CGP.

3. Draw (every frame)

// scene.cpp - add in display_frame(), before timer.update()
draw(cube, environment);

Common pitfalls

  • The rotation is not around the center of the shape: the rotation is applied around the origin of the local frame of the mesh, before the translation. Build the mesh centered at {0,0,0} (most mesh_primitive_* functions take a center argument) and move it with model.translation.
  • Angle in degrees: from_axis_angle expects radians; write 30 * Pi / 180.
  • scaling_xyz stretches the shape along its own axes: it is applied before the rotation, so a cube scaled with {2,1,1} stays a box when it turns.

Variations

Source: course labs of CSC_43043 (public version, without the solutions): 00_test_installation and 08_project (scene.cpp).

See also

CGP - Computer Graphics Programming.