Hierarchical Model Construction and Rendering

The model on the right can be constructed, articulated, and rendered using a strategy like that shown below.

(You can run a Java-JOGL program to see how the articulation works by clicking on the image. Press a numeric key between 0 and 9 followed by the left and/or right arrow keys to articulate the various joints.)

  1. At initialization time: glBufferData, et al. for "generic upper arm", "generic lower arm", "generic upper leg", "generic lower leg", "torso", and "head".
  2. During display callbacks, the following general approach might be used to render an instance of the robot positioned using a 4x4 matrix, MInit:
    void Robot::renderRobot(MInit)
    	M = MInit * MtorsoRotation
    	renderTorso(M)
    	Mlocal = M * Mgenericarm-leftarm * MleftShoulderRotation
    	renderArm(Mlocal, MleftElbowRotation)
    	Mlocal = M * Mgenericarm-rightarm * MrightShoulderRotation
    	renderArm(Mlocal, MrightElbowRotation)
    	Mlocal = M * Mgenericleg-leftleg * MleftHipRotation
    	renderLeg(Mlocal, MleftKneeRotation)
    	Mlocal = M * Mgenericleg-rightleg * MrightHipRotation
    	renderLeg(Mlocal, MrightKneeRotation)
    	Mlocal = M * Mhead-headPos * MheadRotation
    	renderHead(Mlocal)
    
    void Robot::renderArm(M, MelbowRotation)
    	renderUpperArm(M)
    	Mlocal = M * Mlowerarm-arm * MelbowRotation
    	renderLowerArm(Mlocal)
    
    void Robot::renderLowerArm(M)
    	float m[16];
    	glUniformMatrix4fv(shaderIF->ppuLoc("mc_ec"), 1, false, M.extractColMajor(m));
    	one or more calls to establishXxx(…) as desired
    	glDrawArrays(…);
    	…
    

    Depending on how elaborate you want the model to be, lower level functions would have the same structure, in particular, continuing to define and use more transformation matrices while calling lower level functions to produce finer and finer gometric detail. At the lowest levels, the implementation would finally use glUniformMatrix to copy the matrix, M, passed to them to the mc_ec matrix, and then issue required calls to functions like glDrawArrays and/or glDrawElements.

Recall that the mc_ec matrix returned from getMatrices will be the product:

mc_ec = Mdynamic_view * MECu

Hence, if I want a single robot in its native location, the Robot render method might be structured something like:

void Robot::render()
{
    …
    cryph::Matrix4x4 mc_ec, ec_lds;
    getMatrices(mc_ec, ec_lds);
    float m[16];
    glUniformMatrix4fv(shaderIF->ppuLoc("ec_lds"), 1, false, ec_lds.extractColMajor(m));
    // Don't set matrix for mc_ec here. Instead:
    renderRobot(mc_ec);
    …
}

Alternatively, if I want an army of robots (say, in class RobotArmy), I could do something like:

void RobotArmy::render()
{
    …
    cryph::Matrix4x4 mc_ec, ec_lds;
    getMatrices(mc_ec, ec_lds);
    float m[16];
    glUniformMatrix4fv(shaderIF->ppuLoc("ec_lds"), 1, false, ec_lds.extractColMajor(m));
    // Don't set matrix for mc_ec here. Instead:
    for (int i=0 ; i<numRobotsInArmy ; i++)
        robot[i]->renderRobot(mc_ec * robotPlacementMatrix[i]);
    …
}