Assignment 4: Linear blend skinning
 Due Oct 12, 2017 by 11:59am
 Points 6
 Submitting a file upload
 File Types zip
 Available after Sep 9, 2017 at 12am
Overview:
In this assignment, you will learn how to implement linear blend skinning for meshes. You will be employing Javascript to render all the components of your assignment within the browser. You will still have to submit a zip file with your code; however, we will grade your assignment during office hours. We will have a look at your code and your results together and grade you in person; this will only take 10 minutes. We'll post information about scheduling a time to be graded later in the week. Please make sure to be on time and bring your laptop.
This assignment comes with a new Javascript basecode, which you can obtain through git. Note that this is a new repository; so you must clone the repo from scratch.
Please refer to the text of assignment 1 if you're unsure how to clone a repository. On the command line, you can achieve this using the following command:
$ git clone https://gitlab.cs.dartmouth.edu/cs77fa17/assignment4_basecode.git
For visual tools such as SourceTree the process is slightly different, but the URL will be the same.
Before you start, open firstnamelastnamereport.html
in your browser. This html file will be both your report and your submitted assignment. Check the developer console of your browser for any error messages (check the xhour slides for how to open the console).
Framework:
Before starting with the assignment, we strongly encourage you to look through the provided framework. The webgl and utility framework is similar to the previous assignments; however, there are some files containing additional classes to which you'll have to add code to complete the assignment. All the locations where you are required to add code have been marked with "TODO" statements for your convenience. Also the provided files contain "NOTE" markers that provide vital information related to the assignment. The provided files have ample documentation to give you an understanding of the major segments of the code.
The joint.js
file contains the Joint
class that represents the actual joint or bone within a skeletal framework. The skeleton.js
file contains the Skeleton
class that stores the joints in a flat array. The skeletal system is traditionally employed in a hierarchical fashion where every joint contains a reference to its parent and the root of the hierarchy has its parent set to null
. Each joint in the hierarchy can be represented by a local transform with respect to its parent. The mPosition
field in the Joint
class stores this position of the joint with respect to its parent. The mJointAxis
represents the axis along which a joint can rotate. The mJointAngle
stores the current rotation angle of the joint. In addition to these fields, the joint also contains a mLength
field that stores the length of the corresponding bone in the skeleton hierarchy. You will be using this length to compute weights for vertices when performing linear blend skinning. Each joint is rendered as a wireframe cube with its corresponding length and orientation.
mLength
is assumed to lie along the '+X' axis starting from origin (the joint's local frame). You will have to keep this in mind while performing Task 3.The skin.js
file contains the SkinMesh
class that represents the mesh to which a skeleton can be bound to. The functions createCylinderSkinX()
and createArmSkin()
are utility functions that create meshes that you will be using to perform rigid and smooth skinning. The original untransformed vertices for the mesh are stored in the mOriginalPositions
. The render code however employs the mTransformedPositions
to correctly render the mesh that has been deformed by a skeleton bound to it. You can use the getVertex()
and setTransformedVertex()
functions to access the original vertex locations and update the transformed vertex locations correspondingly. In the beginning both these positions would be the same since we have not applied any transformations to the underlying geometry. For smooth skinning, the mWeights
array is used to store the weights for each vertex with respect to all the joints in a bound skeleton. The mJointIds
array stores the corresponding joint ids for each weight. The getVertexWeight()
function can be used to retrieve a particular vertex's corresponding weight for a joint.
mWeights[i * 2 + 0]
and mWeights[i * 2 + 1]
respectively. Further the indices are also stored as mJointIds[i* 2 + 0]
and mJointIds[i * 2 + 1]
respectively. In case of the mesh's skeleton having multiple joints, the multiplicative factor will be different.Note that initially the mSkeleton
is set to null
. This is because no skeleton has been bound to the mesh. The skeleton has to be explicitly bound to the mesh when the joints in the mesh are appropriately oriented in the "binding pose". The basecode also has functionality to help you visualize weights for each vertex with respect to each joint. The setShowWeights()
function essentially strips out the weights for a joint 'i' and populates an underlying triangle mesh with the current state of the transformed vertices and weights. This functionality enables you to understand and debug your weighting code correctly.
Figure1: Weight visualizer
Task 1: Rigid Skinning (UG: 2 pts G: 1.5pts)
In the first task, you will implement rigid skinning. For this task you will work in the joint.js
and skin.js
files. The subtasks should be solved in sequential order to complete the entire task.
Subtask 1: Computing Transforms (UG: 1pt G: 0.75pt)
For the first subtask, you will have to compute the local, world and binding transforms of each joint. Remember each joint is specified with a joint location with respect to its parent position and has a rotation axis about which it can rotate.
A local transformation matrix can be computed with a translation component and a rotation component. Compute this transformation in the getLocalMatrix()
function.
Once you are confident that your local transformation is correct, you should implement the computation of the world transform matrix for a joint within the getWorldMatrix()
. Remember that a skeletal system is implemented in a hierarchical fashion with each joint containing a pointer to its parent. The world transform of the root of the hierarchy is the local transform of the root itself. Hence the world transform of a local joint can be computed recursively by moving up the hierarchy by chaining the transforms of the parent of the current node. Hint: You've already implemented something similar as a part of hierarchical transforms in assignment 4.
Once you have computed the world transform of the joints correctly, you can go ahead with computing the binding matrix of each joint. The binding matrix is computed when the joint is aligned with the associated mesh in the "binding pose". Remember the (inverse of the) binding matrix is used to transform a point in worldspace to a point in the local space of a bone. You should implement this in the computeBindingMatrix()
function.
Once you have computed the binding transform, you can move on to the next subtask to perform rigid skinning of the provided cylindrical mesh.
Subtask 2: Computing Rigid Skinning (UG: 1pt G: 0.75pt)
Once you've successfully computed the previous task, you can go ahead and implement the rigid skinning within the rigidSkinning()
function in the skin.js
file. Note that we have already provided the weights for the cylindrical mesh. Each vertex in the mesh has its weight set to one exactly for one joint. You can query this informtion by employing the getRigidlyAttachedJoint()
function. You can use this information to compute the tranformation of the vertex as the joint is transformed.
Figure 2: Rigid skinning
Task 2: Linear Blend Skinning (UG: 3pts G: 2.5pts)
In the second task you will perform linear blended skinning. This task also has subtasks that you will have to implement in a linear fashion. They build upon one another and successfully implementing them in this fashion will help you finish this part of the assignment.
Subtask 1: PointLine Nearest Distance Computation (1 pt)
In this first subtask, you will have to compute the nearest distance from a point to a line segment. You will have to add your code to the computeDistanceToLine()
within the skin.js
file. This function takes in a point and the two vertices that make up the line. Note that there are a few cases you'll need to consider to properly compute the distance to a line segment. If the perpendicular projection of the point onto the line lies outside the segment end points, the shortest distance is to one of the two endpoints. Otherwise, the shortest distance is to the line.
Subtask 2: Computing Linear Blend Skinning (UG: 2pts G: 1.5pts)
Once you've implemented the nearest distance between a point and a line segment, you can go ahead and implement a weighting function for each vertex that employs this distance. As discussed in the class lecture, linear blend skinning is implemented by considering a weight for each joint that influences a vertex. The final transformed vertex is then a linear combination of weighted vertex positions that have been transformed by each joint vertex. You will have to implement a weighting strategy within the computeLinearBlendedWeights()
function that considers the inverse of the distance raised to the fourth power (1/distance^{4}) of each vertex to each joint (the length of the joint and its position can be used to compute two endpoints of the line segment) in the underlying skeleton. You will store all the weights in mWeights
and the corresponding joint id within the mJointIds
as indicated earlier. You can use the previously discussed weight display code to help you debug problems associated with this task.
Figure 3 : Linear blended skinning
Task 3: Skinning A Custom Mesh (1pt)
In this task, you will use all that you've learned in the previous tasks to rig a skeleton to the provided arm mesh. You are required to manually place joints at different locations along the arm. At a minimum, there should at least be 3 bones (the upper arm, middle arm and wrist). We have already provided the locations of the first two bones for your convenience. You can use these positions to get an idea of how to place the additional joints. To get full points for this task you should use additional joints within the fingers to increase the realism of the skinning. Slider elements in the UI are automatically added as you bind a skeleton containing bones to the meshes.
Figure 4 : Skinning a custom mesh
Joint
class to help you do this.Procedural Animation of a Custom Mesh: (G: 1pt)
Graduate students must perform a procedural animation of the arm and undergraduates may do so for extra credit. At each time interval 't' you can update the rotation angle of each joint by a predefined angle increment and you can loop over all these frames. The more the number of joints you employ, the more realistic the arm animation would look like.
What to submit:
You should submit a zip file containing the entire folder for this assignment (including all js files, html files and the resources
folder). Rename firstnamelastnamereport.html
to contain your name. Fill in the report with any problems encountered and comments about the assignment.
Note that we will grade the assignments in person. You will still need to come to office hours for grading. However, you should also submit the zip file so we know you completed your assignment on time, and that you didn't copy source code from others.
Rubric
Criteria  Ratings  Pts  

Task 1
threshold:
pts


pts



Task 2
threshold:
pts


pts



Task 3
threshold:
pts


pts



Animation
threshold:
pts


pts



Deductions
threshold:
pts


pts



Total Points:
7.0
out of 7.0
