6.8 - Chaining Transformations - Adding a Forearm

Robot forearm

The previous lesson demonstrated the initialization and use of matrix transforms. This lesson will demonstrate how to chain transforms together to produce complex motion.

A Robot Arm - The forearm

The previous demo rendered an object that represents the base of a robot arm. Let’s add a “forearm” linkage to the base. The forearm will pivot about the red pin in the base. For the forearm to pivot, its axis of rotation needs to be at the origin, so we design the model in Blender and place the position where the red pin will go through the forearm at the origin. This will be true of all the models you create. You design them so that they can be easily scaled and rotated about their local origin. Then the models can be moved to their desired location in a scene.

For the forearm, we want it to pivot about the red pin in the base. Since rotation is always about an axis, the rotation needs to come first. Then the arm can be moved to the location of the pin. Using a right-to-left ordering of the transformations, we have:

modelTransform =translateToPin
*rotateForearm
Eq1

But wait. The base is spinning. So we can’t simply move the forearm to the base. The same rotation that is happening to the base needs to happen to the forearm. So we apply the base rotation to the forearm as well, but after it has been rotated and translated. So our modelTransform looks like this:

modelTransform =baseRotation
*translateToPin
*rotateForearm
Eq2

Scene Rendering Initialization

The demo program below is a modified version of the code from the previous lesson. It adds a forearm model to the robot arm. We need two new transform matrices to manipulate the forearm. One transform will rotate the arm about the base’s pin. The rotation will possibly change on each new frame, so this matrix will be created in the initialization code but assigned its value in the frame rendering function. The translation matrix can be create and assigned its value once, since the distance to the pivot pin is constant and never changes. Study the example code and then review the code description below. If you want to experiment, the demo code is modifiable.

Show: Code Canvas Run Info
 
1
/**
2
 * simple_transform_example_render.js, By Wayne Brown, Spring 2016*
3
 */
4
5
/**
6
 * The MIT License (MIT)
7
 *
8
 * Copyright (c) 2015 C. Wayne Brown
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a copy
11
 * of this software and associated documentation files (the "Software"), to deal
12
 * in the Software without restriction, including without limitation the rights
13
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
 * copies of the Software, and to permit persons to whom the Software is
15
 * furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included in all
18
 * copies or substantial portions of the Software.
19
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
 * SOFTWARE.
27
 */
28
29
"use strict";
30
31
//-------------------------------------------------------------------------
32
/**
33
 * Initialize and render a scene.
34
 * @param learn Learn_webgl An instance of Learn_webgl
35
 * @param vshaders_dictionary Object a set of vertex shaders
36
 * @param fshaders_dictionary Object a set of fragment shaders
37
 * @param models Object a set of models
38
 * @param controls Array a list of control id's
39
 * @constructor
40
 */
41
window.SceneSimpleExampleRender2 = function (learn, vshaders_dictionary,
42
                                         fshaders_dictionary, models, controls) {
43
44
  // Private variables
45
  var self = this; // Store a local reference to the new object.
46
47
  var out = learn.out;
48
  var events;
49
  var canvas;
50
51
  var gl = null;
52
  var program = null;
53
  var render_models = {};
54
55
  var matrix = new Learn_webgl_matrix();
56
  var transform = matrix.create();
57
  var projection = matrix.createOrthographic(-10, 10, -2, 18, -20, 20);
58
  var view = matrix.create();
59
  var base_y_rotate = matrix.create();
60
  var forearm_rotate = matrix.create();
61
  var forearm_translate = matrix.create();
62
63
  // Set the forearm_translate to a constant translation along the Y axis
64
  matrix.translate(forearm_translate, 0, 2, 0);
65
66
  // We don't have a real view transform at this time, but we want to look
67
  // down on the model, so we will rotate everything by 10 degrees.
68
  matrix.rotate(view, 10, 1, 0, 0);
69
70
  // Public variables that will be changed by event handlers or that
71
  // the event handlers need access to.
72
  self.canvas_id = learn.canvas_id;
73
  self.base_y_angle = 0.0;
74
  self.forearm_angle = 0.0;
75
  self.animate_active = true;
76
77
  //-----------------------------------------------------------------------
78
  // Public function to render the scene.
79
  self.render = function () {
80
81
    // Clear the entire canvas window background with the clear color and
82
    // the depth buffer to perform hidden surface removal.
83
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
84
85
    // The base is being rotated by the animation callback so the rotation
86
    // about the y axis must be calculated on every frame.
87
    matrix.rotate(base_y_rotate, self.base_y_angle, 0, 1, 0);
88
89
    // Combine the transforms into a single transformation
90
    matrix.multiplySeries(transform, projection, view, base_y_rotate);
91
92
    // Draw the base model
93
    render_models.base.render(transform);
94
95
    // Set the rotation matrix for the forearm; rotate about the Z axis
96
    matrix.rotate(forearm_rotate, self.forearm_angle, 0, 0, 1);
97
98
    // Calculate the transform for the forearm
99
    matrix.multiplySeries(transform, projection, view, base_y_rotate,
100
        forearm_translate, forearm_rotate);
101
102
    // Draw the forearm model
103
    render_models.forearm.render(transform);
104
  };
105
106
  //-----------------------------------------------------------------------
107
  // Public function to delete and reclaim all rendering objects.
108
  self.delete = function () {
109
    // Clean up shader programs
110
    gl.deleteShader(program.vShader);
111
    gl.deleteShader(program.fShader);
112
    gl.deleteProgram(program);
113
    program = null;
114
115
    // Delete each model's buffer objects
116
    render_models.base.delete();
117
    render_models.forearm.delete();
118
    render_models = null;
119
120
    // Disable any animation
121
    self.animate_active = false;
122
123
    // Remove all event handlers
124
    events.removeAllEventHandlers();
125
    events = null;
126
127
    // Release the GL graphics context
128
    gl = null;
129
  };
130
131
  //-----------------------------------------------------------------------
132
  // Object constructor. One-time initialization of the scene.
133
134
  // Get the rendering context for the canvas
135
  canvas = learn.getCanvas(self.canvas_id);
136
  if (canvas) {
137
    gl = learn.getWebglContext(canvas);
138
    if (!gl) {
139
      return;
140
    }
141
  }
142
143
  // Enable hidden-surface removal
144
  gl.enable(gl.DEPTH_TEST);
145
146
  // Set up the rendering shader program and make it the active shader program
147
  program = learn.createProgram(gl, vshaders_dictionary.shader05, fshaders_dictionary.shader05);
148
  gl.useProgram(program);
149
150
  // Create the Buffer Objects needed for this model and copy
151
  // the model data to the GPU.
152
  render_models.base = new Learn_webgl_model_render_05(gl, program, models.Base, out);
153
  render_models.forearm = new Learn_webgl_model_render_05(gl, program, models.Forearm, out);
154
155
  // Set up callbacks for the user and timer events
156
  events = new SimpleTransformExampleEvents2(self, controls);
157
  events.animate();
158
};
159
160
161
./simple_transform_example2/simple_transform_example2.html

Adding a forearm to a robot base.

Please use a browser that supports "canvas"
-90.0 +90.0 Forearm angle: 0.00 :
Animate
Open this webgl program in a new tab or window

Concerning the pre-processing actions that happen once when the constructor code is executed:

Lines Description
60-61 Two new transforms are created to manipulate the forearm.
64 The translation for the forearm is set to a constant 2 units along Y.
74 A class variable is created to store the angle of the forearm. It is made public so the HTML slider event handler can change its value.
153 Buffer objects in the GPU are created and the model data is copied to the GPU for the forearm model.

Rendering a Single Frame

Each time the scene needs to rendered, the render function in lines 79-103 is called. This function is identical to the previous demo version with the following exceptions:

Lines Description
96 The rotation matrix for the forearm is set because the rotation of the forearm can change on each frame.
99-100 The transform for the forearm is calculated. Notice that the base rotation is included. Also notice the ordering of the transforms from right to left. The order of the transforms is critical.
103 The forearm model is rendered using the calculated transform.
Next Section - 6.9 - Chaining Transformations - Adding an Upper Arm