Team Members: Crystal Wang (3034709952), Arushi Somani (3034899544), Ashley Chu (3034858776), Manaal Siddqui (3034654585)
The goal of this project is to create a realistic simulation of an inflatable tube man. Our goal was to:
As we will elaborate in this report, we soon realized that the mesh structure engine that we'd defined and used in earlier projects was simply not expressive enough to be able to create an inflatable tube man. Our efforts to extend the kind of structures that the physics relied on— adding "rod" structures modelling rigidity alongside the spring structures modelling flexiblity— had to be tabled for pausity of time.
We still wanted to be able to model particle simulation and random motion through inflatable structures in our project. As such, we pivoted to making a windsock, which let us model similar interactions with a simpler mesh structure. Thus, our final project goal pivoted to rendering a simulation of windsock movement in air based on wind-cloth physics principles.
Building off of the infrastructure from Project 4, ClothSim, we added wind features, including a GUI that allows users to control the direction and speed of wind using x, y, and z dimensions. With polar coordinates and triangle sorting, we were able to create a tube-like mesh which forms the body of our windsock. We also added streamers to our mesh by dividing the tube into two core parts, head and streamers, and skipping over rows during triangulation to account for the division and separation of streamers. For our windsock appearance, we created new textures using illutrator, and also implemented a Skybox for environment aesthetics.
We started with our implementation from ClothSim, since we wanted to create an inflatable tube man. The ClothSim project provided a way to create a mesh and apply forces upon it, and we figured we could build on that to include wind and internal particle forces and pressure
The first part was creating wind! To do so, in cloth->simulate we build off of the loop from the previous
project in which the point mass positions are updated at each time step. We use the point's velocity, find
its normal with pointMass.normal()
, set a wind strength constant C, and simply follow the
formula below to calculate wind force. Then it's just a matter of adding it to
pointMass.forces
This next part involved editing the buildGrid function so that the mesh that was created was cylindrical rather than flat and rectangular. We did this by using polar coordinates, essentially iterating over the height points and at each height, creating a circle. This meant creating a double nested for loop, with the first loop being over height points and the second being over angles between 0 and 2PI. Then we can create Point Masses at
$$(x,y,z) = (h offset * h, r * sin(θ), r * cos(θ)) $$We also have to account for a weird error that came up in which the cylinder had almost an "arc" subsection
missing, and we did this by accounting for the case that angle >= 2 * PI - incr_amt + 1
, in
which case y is r * sin(0) + 0.0001 and z is r * cos(0) + 0.0001 so that it wraps around the cylinder enough
to enclose it
Moreover, we pin the first 10 "circles" (where h < 10) going up the cylinder to best model our fish wind sock
In order to simulate streamers we needed to essentially divide the mesh into two parts -- the first part is the tube and the second part is the streamers. Then, we edit spring creation by essentially skipping every row and delete triangles within those rows. We do this by first converting all the x-y-z coordinates of the original cloth grid as described above, but also decrement the radius of the circle with each iteration of the circle to give a tapering off effect. Once we've decided on how long we want our streamers, we stop decrementing the radius of the cylinder being created (ie. 2/3 of the original width is a tapered tube, and the last 1/3 is held at constant radius) For spring building, we loop through all the rows of the cylinder and create springs based on whether the rows/columns are even/odd (ie. structural left springs exist for all rows and columns, but structural above springs exist for every other row). That takes care of the wireframe mesh, but in order to make the streamers look plausible in shaders, we add an extra loop after the creation of all the triangles to only have the shader render and shade every other row of triangles after the cutoff point for the cylinder (ie. shade the first 2/3 of the width's triangles, but the last 1/3 should remove every other row of triangles).
Damping, spring, and other constants are labelled within the GUI to make it user-interactive and able to change how the forces apply. We wanted to expand this to include our new wind simulation, so that the GUI has labels for the x, y, and z components of wind velocity/direction. we make a few changes in clothSimulator.cpp. We add wind in the vector of external_accelerations and create a label in the GUI that changes the x,y,and z components of wind with 3 different floatboxes. Then we edit cloth->simulate function to pull from external_accelerations the wind vector, which gets applied as was explained in the Wind Simulation section.
We also wanted to give some aesthetic quality to the wind sock. We created a texture file in Adobe Illustrator that was mapped over the mesh.
In addition, we added a pole so the wind sock is not flying in empty space. We did this by creating a very thin sphere in a sphere.json and positioning it with the sock
For the skybox, we added in texture files over the cube. we created new vertex and fragment shaders and loaded the cubemap images to create a new surrounding environment
The skybox also features a timing feature in which the background changes over time (and thus our windsock can travel to new places!)
So…how does a tube man work? When the air speed within the tube decreases, pressure increases, causing the tube to expand and straighten. When the air speed increases, pressure decreases, causing folds or creases. But air particles are difficult to simulate. So is a physically accurate version of pressure. So instead we brainstormed an approach that would approximate this simulation through torque.
We can model the movement through torque and apply it towards a rig of rods, each parametrized by a start and end pointmass and a pivot.
If you look at a video of a tube man, you'll notice that there's a fold that pulses through different parts of the tube. So we're essentially trying to model that pulse movement with torque where the “fold angle” is defined by a pinned and unpinned rod (ie an axis and a pivot). As the fold goes up (or down) we pin in the opposite direction. When all the rods are pinned, then we can “flip” the direction and bring it down. Torque is proportional to the angle between the axis and the pivot and the forces currently acting on that point.
We translated this to code by creating Rod objects, each with a start and end pointmass and a pivot. It included functions to calculate torque, angle, and other values we’d need to find force. Then we tried adding a vector of rods as a parameter to cloth (similar to how it’s done with springs), filling it during buildgrid(), and then editing simulate() to render that force. In simulate, we apply external forces to each of the endpoints on the rods like we do on the other pointmasses. After applying the spring correction forces, we calculate the wind forces acting on the points on the rods and add it to the pointmass parameters. We keep track of our direction, and depending on that, we iterate over the rods up or down. If the rod is pinned, then we calculate the torque and add it. We change the pivot point and axis according to whether we're going up or down. Based on them, we find the force on that pivot point, multiply it by the sin of the angle and the radius of the pivot (which is just h_inc). If we find that there are no unpinned rods in our loop, that means the fold has traversed the tube, and we need to go down. Then, we add the torque force found on all the points as we iterate over the point_masses and update their positions.
Here's a little diagram that gives a high-level overview of the motion and how it translates to our code!
Unfortunately, we had to pivot due to time constraints though :(
Below are a few photos demonstrating our results at the midpoint status.
By our milestone, we had completed our basic mesh creation and added wind simulation to our project 4 codebase.
We encountered a lot of challenges, from infinite loops to segfaults to git issues to physics concepts and all in between
Here are the final results of our windsock simulation!
Final mesh representation with streamers! |
Windsock simulation – randomness in movement from wind input! |
GUI wind values for x, y, and z wind forces. |
Full mesh with stick! |
It was a pleasure to work together as a team, and though our inital dream of tube-man wasn't able to come into fruition, the journey to creating our wind-sock was a fun adventure! We haven't given up on our tube-man and plan to continue looking into the construction and simulation of tube-man after successfully creating our windsock after this course.
Manaal worked on wind simulation and the physics behind the tube man! Manaal also helped with the proposal, milestone, and final write-up documents, along with the milestone and final presentation video.
Crystal worked on implementing streamers and editing the mesh to be a tube, as well as the GUI for users to control wind speed! Crystal also helped with the project proposal document and parts of the milestone and final write-up.
Arushi worked on tube point pinning, indexing, and spring exceptions. Arushi also helped with the project proposal, milestone, and final write-up documents as well as the final project video.
Ashley worked on creating a skybox and texture files. She also helped record videos for the milestone and final presentation and helped write the project proposal, milestone, and final write-up.
Windsock team!
Hear about the tale of reggie the windsock!