Undergraduates will construct a physics-based animation system, based on a simple spring-mass model of cloth-like materials. We will use the framework described in class, including our rasterization and modeling components. To compute the simulation, we will take a simple variant of Euler integration known as simplectic Euler integration.

This assignment is designed to teach students the central concepts of a physics-based animation pipeline for rendering time-varying graphics. Specifically, students will:

  • Implement a basic algorithm for simulating spring-mass systems
  • Use this algorithm to model cloth-like materials, accumulating forces and then performing a simple numerical integration of the resulting ODE
  • Develop a simple rendering system based on treating the input spring-mass mesh as a triangle mesh.
  • Render the resulting simulation in real time.

Part 1: Setting up the Animation Pipeline

Instructor’s Note: You are welcome to use your rasterizer from A06, but this is not required for this assignment. Instead, I have provided an updated gl-rasterizer.js in your default repo, and what follows discusses setting up the animation pipeline relative to that.

In the default repo, you’ll see I’ve provided a collection of javascript files for scaffolding, including: a07.js, filereader.js, gl-rasterizer.js, and mesh.js. These interact with index.html in the following ways:

  • index.html loads all javascript files. It also provides a multiple file loader input, as in A06, where you will need to specify a .js scene file and associated .obj.
  • Upon selecting a scene, filereader.js will parse input files for you. Notably, you’ll see that the function allFilesLoaded() parses only the first mesh object, using the Mesh class from mesh.js. Next, it calls the function setupAnimation() which is defined in a07.js
    • The Mesh class I’ve provided is a simplified, skeleton data structure that knows how to go from a string containing the contents of an .obj file to a list of positions and faces. It also provides the functions getVertList() and getFaceList() used by the gl-rasterizer.js
  • In a07.js, the function setupAnimation() makes a few calls. First, it configures the camera and canvas using the function configureRasterizer() (defined in gl-rasterizer.js). Next, it calls startRender() with the input mesh, similar to A05. Finally, it also provides a default implementation for the function animate(), which can be turned on/off with the toggle button.
  • In a07.js the animate() function is your animation pipeline workhorse. This function will be called repeatedly when animation is enabled, and will attempt to update the screen at 60 frames per second. Your animation code should update any information it needs and then call updateMesh() to update the visual information in gl-rasterizer.js
  • Finally, index.html also has a button to toggle the animation on and off. You’ll see in a07.js that this function enables or disables the animate() callback.

Thus, I envision that your task in this assignment is to make updates in three places:

  1. Modify allFilesLoaded() in filereader.js, as necessary.
  2. Modify the Mesh class in mesh.js, as necessary.
  3. Modify the animate() function in a07.js to make calls to perform the spring-mass animation.

As described below, many of these updates make use of information that will be stored in the Mesh, so you may want to consider developing a function nextStep() which updates the mesh in preparation for the eventual updateMesh() call to gl-rasterizer.js.

Part 2: Computing Spring-Mass Systems

Your code will read in an input triangle mesh. All edges in the mesh will be treated as springs, and their rest lengths should be computed from their original lengths.

We will use a scene file format similar to the one used in previous assignments. You need only support scene files that have one mesh specified. Additionally, parsing your scene files should support:

  • Reading the time step increment, \(h\), specified in the scene file as timestep followed by a float.
  • Reading a mass of each vertex, \(m\), specified in the scene file as mass followed by a float.
  • Reading the spring constant (used for all springs), \(k_s\), specified as spring_constant followed by a float.
  • Reading an accumulated external acceleration vector, specified as acceleration followed by a list of three floats.

Finally, each mesh will also have a list of constraint vertices that should not be simulated by the system. Their positions should stay fixed regardless of the forces they feel. These are listed in the mesh object itself as:

  • A list of indices for constraint vertices, specified as constraints followed by a list of integers which are vertex indices. For example "constraints": [0, 1, 2, 3] specifies four constraints: the vertices with indices 0, 1, 2, and 3.

To do an update of the physics, it is helpful to think of separating the functionality into a force computation step, and a state update step.

For the force computation step, for each vertex \(v_i\), you should accumulate:

  1. Any external forces, such as gravity (be sure to account for mass by multiplying it into this force);
  2. A basic, default drag force, defined in the direction opposite of the current velocity of the particle. I used a drag constant of 0.01 and multiplied this by the negated current velocity vector;
  3. All spring forces due the any other vertex \(v_j\) that shares an edge with \(v_i\). You can treat springs as standard Hookean springs, and compute a force based on how much their length differs from rest length, multiplied by the spring constant \(k_s\).

For the state update step, you will use the accumulated forces from the previous step. We will use a simplectic Euler update of velocity and position. Simplectic Euler offers us some additional stability without the need for extremely small time steps.

To update velocity, you should divide the forces by the mass \(m\) of the particle and then multiply by the input \(h\), Next, to update the position of the particle, you should multiply the new velocity by \(h\) and step the position. This means that for simulation step \(i\),

\[v_{i+1} = v_i + h*f_i / m\] \[x_{i+1} = x_i + h*v_{i+1}\]

Note that the position update step uses \(v_{i+1}\) instead of \(v_{i}\) (using \(v_{i}\) would be standard explicit Euler). After computing new velocities and new positions for all particles, you should update them for the next iteration (this requires tracking both the current and future velocities).

Finally, when updating the state you can employ the constraints. Constraints are specified in the scene file as a list of vertex indices. All constraint vertices will not have their positions updated, nor will they accumulate velocity, during the position update step. I handled this by a post-process as the last step of the state update: After doing the spring-mass update for all vertices, I go through the list of constraints and set \(v_{i+1} = v_{i}\) and \(x_{i+1} = x_{i}\).

Part 3: Rendering Mass-Spring Meshes

To render, I’ve provided the gl-rasterizer.js code which takes the input scene information and sets up the camera. This rasterizer ignores any lights or color information on the mesh, but you’re welcome to modify it if you would like to add additional effects (particularly, using your knowledge from A06, modifying the vsSource and fsSource are equivalent to performing your vertex processing and fragment processing).

You are also welcome to remove the gl-rasterizer.js code and replace it with your rasterizer from A06. Please document any information for this in your README.

After each update of positions, you should render the triangle mesh. To do so, you will need to update the positions of all vertices in the mesh, and then recompute any necessary information for your rasterizer (such as triangle and vertex normals). Luckily, connectivity of the mesh will not change from one time step to the next, but any geometric information that you use for rendering with your rasterizer will need to be updated. As a result, you should not have to modify faces in any way and you can use getVertList() and getFaceList() from the provided mesh class after updating the positions of the vertices.

As we will treat cloth simulations using meshes that have boundary, we need functionality to render the mesh by potentially coloring both sides of it. Thus, if you’re using your A06 renderer, you may need to modify your lighting and shading equations to appropriately compute colors even if the light is on the wrong side of the mesh. One way to do this is to replace the \(\max(0,...)\) terms using \(\textrm{abs}(...)\) for certain dot products.

Part 4: Execution and Testing

Your program should be able to load all scene files that I’ve provided and display the resulting simulation. You do not need to provide any additional interface elements, but you may want to consider creating sliders for adjusting \(h\), \(m\), and/or \(k_s\) to see different effects.

There is no need to save any information about the simulation, but if you’d like you could employ your screen shot saving technique from previous assignments.

Include in your README specific instructions on how to execute your animation.

Finally, you are also required to provide a sample scene file with a spring-mass simulation of your own creation. Be creative! Make sure that you include a file myscene.js with any other .obj files that are necessary for it.

Part 5: Written Questions

Please answer the following written questions. You are not required to typeset these questions in any particular format, but you may want to take the opportunity to include images (either photographed hand-drawings or produced using an image editing tool).

These questions are both intended to provide you additional material to consider the conceptual aspects of the course as well as to provide sample questions in a similar format to the questions on the midterm and final exam. Most questions should able to be answered in 100 words or less of text.

Note that there are 8 questions included, you need only choose 5. Particularly, the last three cover material to be described later in class, and included as extra credit to study for the final. If you complete all 8, you can receive a maximum of 32/20 for the written portion of this assignment.

Please create a separate directory in your repo called written and post all files (text answers and written) to this directory. Recall that the written component is due BEFORE the programming component.

  1. What additional information do you plan to store for computing the spring-mass mesh update, and where precisely will you store it. Briefly describe why you think this will make your update step more efficient.

  2. Explain what gimbal lock is and what causes it.

  3. Explain the difference between explicit and implicit integration.

  4. Give one reason to and one reason not to use physically-based animation techniques instead of using character animation.

  5. In class, we discussed multiple options for handling collisions. Briefly (in one sentence) describe one technique as well its advantages and disadvantages.

  6. Explain the difference between an approximating and an interpolating curve formulation. Give an example of a type of curve of each.

  7. Using higher degree polynomials to model curves with many constraints can lead to many issues. Describe one problem with using them.

  8. What is the difference between \(C^0\) and \(C^1\) continuity?

Grading

Deductions

Reason Value
Program crashes due to bugs -10 each bug at grader's discretion to fix


Point Breakdown of Features

Requirement Value
Consistent modular coding style 10
External documentation (README.md) 5
Class documentation, Internal documentation (Block and Inline). Wherever applicable / for all files 15
Expected output / behavior based on the assignment specification, including

Correctly reading in all inputs10
Correctly applying the external forces and drag10
Correctly computing the accumulated force update due to springs10
Correctly implementing vertex-based constraints10
Computing the simplectic Euler step10
Updating the cloth geometry in preparation for rasterization10
Producing a cloth simulation of your choice10

70
Total 100