15-462 Graphics Project 3: Simulating a Jello Cube

Program and animation due Tuesday November 5th midnight

An Overview

For this assignment you will program a physically-based simulation of a jello cube. The jello cube is made of elastic material, much like the jello sold in grocery stores (the one obtained by mixing the jello powder with water and putting it into the fridge for a couple of hours). When the jello cube is stretched, it will try to contract. When squeezed together, it will tend to inflate back to the original shape. As with the previous assignment, you will also create an interesting animation demonstrating your results.

Undeformed jello cube

Deformed jello cube

About physical simulations

Nature is complex and to generate computer graphics simulations of interesting phenomena such as cloth on a virtual character, ocean waves, or trees deforming in the wind, one common approach is to write down the underlying equations of physics and solve them numerically on a computer. Sophisticated algorithms exist for simulations of cloth, fluids or deformable objects, and the results can often be seen in recent computer-generated movies. This assignment will familiarize you with computer animation and physically-based modeling by simulating a simple system, namely the jello cube. You will also be able to expand your knowledge of OpenGL.

What you need to do

You will model a 3D chunk of jello, which, when undeformed, has the shape of a cube of dimensions 1 meter x 1 meter x 1 meter.

The cube will be constrained to only travel in the interior of a bounding box of dimensions 4 meters x 4 meters x 4 meters. The center of the bounding box is located at the origin of the world-coordinate system. The axes of the bounding box are aligned with the world-coordinate system axes. The cube will stretch, contract, oscillate, change velocity, bounce off the walls of the bounding box, based on the physical laws for a mass-spring system. The cube will stay inside the bounding box. When the cube reaches one of the walls of the bounding box, it should bounce off that wall back towards the interior of the bounding box. Consult the lecture material to see how to achieve this effect.

A real-world jello cube is a continuous object, and if we were to simulate every small infinitesimal part of the elastic material, infinite memory would be required. For this reason, we will discretize the jello cube. This approach (discretization) is very common in phyisically-based modeling, and also applied mathematics and engineering in general. The cube will be modeled by 8 * 8 * 8 = 512 discrete points, which will, in the undeformed configuration, form a uniform grid covering the volume of the cube. When the cube is undeformed and positioned such that it occupies the unit cube in R^3, these points will be positioned at (i / 7, j / 7, k / 7) for i=0,...,7, j=0,...,7, k=0,...,7. As the cube deforms, these points will change position, giving us the shape of the cube at any given moment of time. Note that as the cube deforms, these points will no longer be the same distance apart from their neighbors. At places where the jello cube is squeezed, the points will be closer together, and where it is stretched, they will be farther apart than in the undeformed position. The points with i=0 or i=7 or j=0 or j=7 or k=0 or k=7 are the points on the boundary of the cube. We will call these point surface points, since they define the surface of the jello cube. There are a total of 296 surface points. The surface of the cube can be rendered by drawing triangles connecting the surface points. We provided the complete code to do cube rendering - look for it in showCube.cpp. Interior points are not necessary for rendering, as they are invisible. They are, however, necessary for simulating physics.

The 296 surface control points, connected with structural springs (only structural springs connecting two surface points are shown). This is what wireframe mode looks like (see below).

You will model the movement of the cube by numerically solving a system of ordinary differential equations, which sounds perhaps a bit scary, but is actually not that difficult. The equations to be solved incorporate Newton's second law (F=ma), Hook's linear model of elasticity (F=kx), and linear damping (F=-kv). Further clarification on this will be given in class. To solve the differential equations, you will use two methods: Euler integration, and Runge-Kutta 4th order (RK4) integration. We provided the code which implements both the Euler method and the RK4 method - look for it in physics.cpp. All you have to do is appropriately call either of these two methods during the simulation. For a particular simulation, you will typically just use one of the two methods - but you have to make sure that both methods work correctly with your code.

Initially, the cube will be in some prescribed **deformed** position, with some prescribed initial velocities. Your program will read the information about the initial position of the cube, initial velocity, and various other simulation parameters from a file on disk. The movement of the jello cube will depend on the information given in this file. We shall call these files 'world' files, because they describe the initial world (state) of the jello cube. The default extension for these files is .w (example: jello1.w) . Several world files are provided with the assignment, so that you can test your code. Included in the starter code is the routine loadWorld which loads all the information from a world file into processor memory. There is also a routine writeWorld which works in the opposite direction: it saves the current point positions, velocities, and other parameters to a valid world file on disk. See the comments in the input.cpp file for parameters to these two routines. Also, we provided a separate application called createWorld. It can be found in the starting package (createWorld.cpp) . This application allows you to create your own world files without having to manually edit world files. For more info about createWorld and for a description of the structure of a world file, click here.

The cube will deform as a result of different velocities at different locations inside the cube. These differences in velocities will happen for three reasons:

To obtain a good view of the scene, the camera has to be positioned appropriately. We provide a complete code to do this (see jello.cpp). It allows the user to move the camera around in real-time, and observe the jello-cube from different angles. The description of the method that we use to position the camera is here.

Your program will allow two rendering modes: wireframe and triangle. In the wireframe mode, only the surface points (no filled triangles) are shown, together with the structural springs connecting them. Structural springs are one of the three kinds of springs that are used with the mass-spring system. You can learn more about this from the lecture slides (see Background info section). In the triangle mode, surface of the cube is rendered as triangles (and the surface points are vertices of these triangles). We provided the code to support both the wireframe and triangle rendering mode. It is possible for the user to switch among the two rendering methods at run-time, by pressing 'v' (see also showCube.cpp). Also, in wireframe mode, user can independently turn on/off the display of structural springs, shear springs and bend springs. Thus, any of the 8 on/off combinations can be selected. Pressing 's','h','b' toggles the display of structural, shear and bend springs, respectively. Note that to avoid screen clutter, only the springs connecting two surface points are shown, but the simulation of course has to simulate the structural, shear and bend springs appropriately connecting all the 512 simulation points.

In the triangle mode, the jello is illuminated using proper OpenGL lighting. We provide a complete lighting environment in jello.cpp . You are free to change lighting parameters, or make other rendering improvements/modifications. This way, you can personalize your assignment - otherwise all the cubes for the entire class will look the same (except for any differences in implementation of physics of course), which would make for a quite boring final assignment movie.

You have to implement collision detection and response for the jello cube hitting any of the six walls of the bounding box. In addition, you have to implement collision detection and bouncing response for the jello cube impacting an inclined plane. An inclined plane is a static planar surface, positioned in a certain way inside the bounding box. In the world structure, there are special fields to specify the position of the inclined plane. Use these fields to determine if an inclined plane is present. If present, use the a,b,c,d fields to render the plane and implement collision detection and response. For the inclined plane, use the same collision spring parameters (collision elasticity, collision damping) as for collision with the bounding box. Any position and orientation of the inclined plane could occur and should be supported. When the inclined plane is present in the simulation, assume that the initial position of the cube is such that it completely lies on one side of the plane.

The world file might also specify an external non-homogeneous time-independent (i.e. possibly varying with location, but not with time) force field. If the force field is present, you must model its effect on the cube. The force field is given as an array of vectors, each specifying the 3D force vector (in Newtons) at a node of a 3D discrete grid. Note that this is a different grid than the jello cube grid; this force field grid covers the entire scene bounding box. Use interpolation to obtain values of the force field at an arbitrary position inside the bounding box. The grid is uniform, covers precisely the volume of the bounding box, and consists of n x n x n equally spaced points, where the parameter n is the resolution of the grid. It is specified in the world file. For this assignment, you need to support values of n <= 30.

You will have to implement the acceleration function (computeAcceleration, see physics.cpp). This function takes as input the positions of the 512 nodes, the velocities of the 512 nodes, plus the physical parameters of the model. It returns the acceleration for each of the 512 points. It also adds any effects of an external force field, if such a field is present of course. In general, the acceleration will of course be different for each of the 512 simulation points. To compute the acceleration, the function must take into account the forces due to

Extra credit

The following are some suggestions:

Also, you might be awarded credit for any other creative or interesting solution to an issue in your implementation, as long as it is well-documented in your readme file. So, if you can think of any interesting feature, feel free to implement it.

Please note that the amount of extra credit awarded will not exceed 10% of this assignment's total value.

Background Information

The lecture slides contain information on: The slides are here: ppt, ps, pdf (black & white - good for printing).

Starter code

This assignment consists of two applications: jello and createWorld. We provide complete implementation for createWorld, which is a tool to design your own physical environments. createWorld is a single-file application, and independent from the rest of the assignment. Its implementation is createWorld.cpp .

You will be developing the application called 'jello'. It consists of the following source-code files: jello.cpp, jello.h, input.cpp, input.h, physics.cpp, physics.h, showCube.cpp and showCube.h . The main file is jello.cpp.

In the starter package, all of these files already contain code for pre-implemented functionality such as lighting, camera positions, integrators, etc. You can modify any of these files in any way you want and you can add your own files, if you need to. We provide a makefile which builds both applications: createWorld and jello. Also in the starter package, there are some examples of world files. You can download the complete starter package here. To unpack the starter package, copy the zip file to an empty directory, and run 'unzip starter.zip'.

Steps you need to take

The following list is one suggestion to pull off the assignment. You can of course take alternative routes. At some point down the pipeline you will need to study the spring model and understand how it applies to the jello cube.

  1. Make sure starter and createWorld code compiles.
  2. Add code to setup a window (640x640). Note:This window size is not the same as for the last assignments.
  3. Understand the createWorld/loadWorld/saveWorld functionality. Create a couple of your own world files.
  4. Familiarize yourself with the provided routines which, given a world structure, render the surface of the cube. Be able to use both the wireframe and the triangle mode. See how the camera and lighting work. Experiment with different world files. Personalize your assignment by changing the lighting parameters, or change/improve the lighting environment in some other interesting way.
  5. Do a test: move the points of the cube according to a prescribed made-up (non-physical) sequence of deformations, and observe if the cube on the screen changes accordingly. To do this, hard-code the modifications of the world structure as time evolves (i.e. don't use the physical model, but just model some simple movement, e.g. by manipulating positions and velocities in some simple way as the time evolves). For example, increment the z-value of all positions. See if the cube is moving appropriately on the screen.
  6. Implement the acceleration function (computeAcceleration, see physics.c, and description above). For now, however, don't implement any collision or the force field.
  7. Test the acceleration function:
    Make up a world structure, which you will use for the test: Position the points at some position, assign certain velocities, use the Euler or RK4 integrator, assign spring constants, ... As a starting point, you should definitely use one of the world structures we provided. Selecting good values for elasticity and damping factors can be tricky. See the tips below.
  8. Implement the force field. Test if it works. Make up some force fields which create really cool effects.
  9. Implement collision detection and response for bouncing off the wall. Test it.
  10. Render the inclined plane (careful: intersection of the plane and the bounding box can take several shapes, some of which might be easy to miss). Implement collision detection and response for the inclined plane.
  11. Extra credit
  12. Make the animation jpegs.
  13. Submit assignment. Congratulations!!

Grading Criteria

Your program must:

Animation Requirement

As last time, you are required to submit an animation, represented by a series of JPEG images which are screenshots from your program. Do not exceed 300 frames. The frame rate for the animation, again, is 15fps, which corresponds to 20 seconds of animation running time maximum. Please name your JPEG frames 000.jpg, 001.jpg, and so on. Be warned that the animate program installed in the cluster may have problems with excessively large numbers of images--it may be wise to use it to test subsequences of 100 frames or so.

The animation represents a smaller portion of your grade than in the assignment 1, and about the same portion as for assignment 2. As with assignment 2, the implementation of the program itself is much more important and involves much more thinking. You will still receive points, however, for showing off your jello cube in a creative manner.

A note for those of you who have had trouble dealing with storage for all your images: It is wise not to try to store your frames on your Andrew account, as this will likely fill your quota very quickly. If you have your program write its frames to ./movie, then you can submit it to the correct handin directory for the assignment, run your program, and write your frames directly into the movie handin directory. This avoids worries about intermediate storage.


Please submit your code along with your makefile and readme to /afs/andrew/scs/cs/15-462/students/your_andrew_id/, in a sub-directory called asst3. Running "make" in this directory should compile your program successfully--if not, you've left out a necessary file. Within this directory, make a sub-directory called movie, and place the frames for your animation within, numbered as described above.


written by Jernej Barbic