6.8 - Chaining Transformations - Adding a 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:
*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:
*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.
/**
* simple_transform_example_render.js, By Wayne Brown, Spring 2016*
*/
/**
* The MIT License (MIT)
*
* Copyright (c) 2015 C. Wayne Brown
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
"use strict";
//-------------------------------------------------------------------------
/**
* Initialize and render a scene.
* @param learn Learn_webgl An instance of Learn_webgl
* @param vshaders_dictionary Object a set of vertex shaders
* @param fshaders_dictionary Object a set of fragment shaders
* @param models Object a set of models
* @param controls Array a list of control id's
* @constructor
*/
window.SceneSimpleExampleRender2 = function (learn, vshaders_dictionary,
fshaders_dictionary, models, controls) {
// Private variables
var self = this; // Store a local reference to the new object.
var out = learn.out;
var events;
var canvas;
var gl = null;
var program = null;
var render_models = {};
var matrix = new Learn_webgl_matrix();
var transform = matrix.create();
var projection = matrix.createOrthographic(-10, 10, -2, 18, -20, 20);
var view = matrix.create();
var base_y_rotate = matrix.create();
var forearm_rotate = matrix.create();
var forearm_translate = matrix.create();
// Set the forearm_translate to a constant translation along the Y axis
matrix.translate(forearm_translate, 0, 2, 0);
// We don't have a real view transform at this time, but we want to look
// down on the model, so we will rotate everything by 10 degrees.
matrix.rotate(view, 10, 1, 0, 0);
// Public variables that will be changed by event handlers or that
// the event handlers need access to.
self.canvas_id = learn.canvas_id;
self.base_y_angle = 0.0;
self.forearm_angle = 0.0;
self.animate_active = true;
//-----------------------------------------------------------------------
// Public function to render the scene.
self.render = function () {
// Clear the entire canvas window background with the clear color and
// the depth buffer to perform hidden surface removal.
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// The base is being rotated by the animation callback so the rotation
// about the y axis must be calculated on every frame.
matrix.rotate(base_y_rotate, self.base_y_angle, 0, 1, 0);
// Combine the transforms into a single transformation
matrix.multiplySeries(transform, projection, view, base_y_rotate);
// Draw the base model
render_models.base.render(transform);
// Set the rotation matrix for the forearm; rotate about the Z axis
matrix.rotate(forearm_rotate, self.forearm_angle, 0, 0, 1);
// Calculate the transform for the forearm
matrix.multiplySeries(transform, projection, view, base_y_rotate,
forearm_translate, forearm_rotate);
// Draw the forearm model
render_models.forearm.render(transform);
};
//-----------------------------------------------------------------------
// Public function to delete and reclaim all rendering objects.
self.delete = function () {
// Clean up shader programs
gl.deleteShader(program.vShader);
gl.deleteShader(program.fShader);
gl.deleteProgram(program);
program = null;
// Delete each model's buffer objects
render_models.base.delete();
render_models.forearm.delete();
render_models = null;
// Disable any animation
self.animate_active = false;
// Remove all event handlers
events.removeAllEventHandlers();
events = null;
// Release the GL graphics context
gl = null;
};
//-----------------------------------------------------------------------
// Object constructor. One-time initialization of the scene.
// Get the rendering context for the canvas
canvas = learn.getCanvas(self.canvas_id);
if (canvas) {
gl = learn.getWebglContext(canvas);
if (!gl) {
return;
}
}
// Enable hidden-surface removal
gl.enable(gl.DEPTH_TEST);
// Set up the rendering shader program and make it the active shader program
program = learn.createProgram(gl, vshaders_dictionary.shader05, fshaders_dictionary.shader05);
gl.useProgram(program);
// Create the Buffer Objects needed for this model and copy
// the model data to the GPU.
render_models.base = new Learn_webgl_model_render_05(gl, program, models.Base, out);
render_models.forearm = new Learn_webgl_model_render_05(gl, program, models.Forearm, out);
// Set up callbacks for the user and timer events
events = new SimpleTransformExampleEvents2(self, controls);
events.animate();
};
Adding a forearm to a robot base.
-90.0 +90.0 Forearm angle: 0.00 :
Animate
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. |