Made as part of a second year physics paper at Media Design School.
This project was created using DirectX 10, aimed to test and implement Verlet Integration in a cloth simulation.
- When dragging the cloth from various camera angles, the movement directions can be different to what are expected.
- Large framerate loss when using the self-collision option
- When beginning the implementation of the cloth, it was clear that it would merely be an array of points in 3D space, connected by constraints. These constraints are used to both draw the cloth and handle the logic of the cloth, including making sure the points meet the requirements of the constraint (not too far away, not too close, etc.).
- First and foremost, I needed the user to be able to have their mouse inputs interact with the world around them. There were many ways that I could’ve implemented this, however I settled on ray-casting, since it is a tried and true method, plus it is a method that I have wanted to attempt to implement for a long time. All I needed to implement it was to take the screen space coordinates of the mouse, convert it to world space, and also take the direction of the ray from the direction of the camera and the projection settings, etc.
- Applying a force from the ray was fairly simple. I did a point-ray distance test, found points within a certain distance and considered those points as “grabbed”, copying the pointers to those objects into a “grabbed particles” vector. Using these, I could take various mouse movements when the mouse was down and apply forces to the respective particles in the required directions.
- I also, using my newly-created ray-casting functionality, implemented the ability to cut the cloth on mouse clicks. This takes a casted ray and checks against all constraints, this time doing a ray-line segment distance test rather than a point test, seeing I am aiming to cut the constraint. Once a line was close enough to my ray, the index of the constraint in the vector gets added to a clean-up vector called “pending cut”. This is handled after all of the physics logic, so as to not interfere with any of the other functionality. When a link is cut, the constraint is essentially nullified, allowing for positions and indices to be maintained within the base constraint vector.
F2: Cloth torn under some force
- In order to allow the cloth to be torn under force, I had to add changes to my “satisfy constraints” functions, which is what handles the movement of particles to be within the bounds of the constraint. Now, the function also checks to see if the cloth is outside of the resting distance of the constraint. If it is over by a certain percentage, it will start to damage the link, eventually cutting it if the health value gets too low. This implementation allows for a slight strength to my constraints, instead of making them be destroyed at the instant they stretch too far.
F3: Modularity for different settings
- I implemented a dialog box to allow the user accurate control of the various settings within the cloth simulation. This allows the user to change the cloth’s height, width, hook count, and various other elements. Upon applying the new settings, the cloth simulation is reset, implementing the changes. This was fairly simple to implement, as I just needed the cloth to be build based on various parameters and to be able to change said parameters through some sort of interface. It was an interesting opportunity to revisit the Windows API for their dialog box functionality, allowing it to be modeless and have features such as sliders that I have never approached before.
F4: Tightly woven structure
- One of my options within the dialog box was “Cloth Rigidity”, which allows the user to change the structure of the cloth, having more complex connections within the simulation, changing the experience for them. This was simple to implement, and allowed much more control for the user.
- The various options for the user were dictated by the intricacy of the constraints created during the cloth’s initialisation. Default was simply creating constraints between each particle’s horizontal and vertical neighbours, crossed called for diagonal constraints also. The most intricate of rigidities was the “Interwoven” option, which initialised the cloth with horizontal, vertical and diagonal neighbour constraints, with extra constraints created for their secondary neighbours also, meaning that a single particle had the potential to have 16 constraints connected to it.
F5: Collision with another object
- The easiest collision component to implement, all that was needed was to take a point and a radius and test to see whether the distances of each particle to that point were within that radius. I began by generating a simple sphere mesh and providing it with a position and radius.
- From that point, I created a simple for-loop that would check each of the particle’s distances from the sphere origin, then, if they were within the radius, push them away from the sphere in the direction of the delta vector between the two points.
- This feature gave me a bit of hassle, not necessarily due to its complexity, but the fact that I misread the brief at first and began implementing a cylinder collision rather than capsule. However, after my mistake, I realised that my work wasn’t completely worthless, as a capsule is essentially the combination of a cylinder and two hemi-spheres. This made the implementation of it much simpler once I began to look at the collision that way.
- After defining a pyramid by an apex and four base points, I calculated the four normals for each of the pyramid’s face. These normals would be used within the collision calculations, by using them along with the apex to define four planes. I took these four planes and, using dot products, determined what side of the plane each point was on. If the point was on the inside of all four planes, then the point is considered inside of the pyramid. During these calculations, I keep track of the shortest distance for the points to the planes and the normal of the plane they are closest too. When trying to correct the positions, I take the distance from the plane’s surface and the normal of said plane and move the point in that direction based on the distance.
F6: Fan that creates wind at different speeds
- Within my dialog box, users define a wind direction (x, y, z) and a force. This wind is then applied to the cloth every frame. The wind isn’t applied directly to the particles, instead it is applied to the triangles defined by particles and their neighbours. This allows for a much smoother application of wind and giving a much more realistic visualisation.
F7: Collision with itself
- For self-collisions, my cloth looks at each particle and each other particle and sees if the distance between them is too small. If it is, it moves the two particles away from each other, maintaining some sort of realism to the cloth. While it was simple to implement this more rudimental form of self-collision checking, it is extremely inefficient. See Lessons Learned for more details.
F8: Cloth being ignited
- This was one of the more fun features to implement. Since I had already defined my particles with a health value, I merely needed to add a Boolean state to them in order to indicate whether they were ignited or not. Once I did this, I created the functionality to burn cloth based on your mouse click, essentially the same function as cutting or grabbing. Particles that were close enough to the ray casted would be marked as “burning”. When updating the particles, I would look to see if they are burning. If they are, they would get slightly damaged and have a slight upward force applied. When updating the constraints, if one of the particles is burning then it would burn the other particles it is connected to. If any of the particles health was low enough, the constraint would also be considered dead and be cut.
- This implementation worked only slightly as intended, due to the fact that as soon as one particle was ignited, the entire cloth would be. In order to alleviate this issue, I added in a burn timer to each of the particles, which would cause particles to spread their fire only if they’d been on fire long enough. This made for a very smooth transition of flames, and achieved the result I wanted.
- To visually represent this, I would set the colour of the particles appropriately when updating the vertex buffers. Particles at full health would have a solid blue colour, whereas any damage sustained to a particle would be represented by red highlights. If a particle is on fire and their health is low enough, they would turn black.
- When approaching such a large task, it was easy to get overwhelmed at the sheer scale of it all. Implementing the base cloth was fairly straightforward, however each of the extra features took a lot of time, due to the unfamiliarity of it all and the unexpected issues that arose.
- One of the key things that I learned was to approach each of the features one at a time. I have a terrible habit of starting one element, beginning to drift into another element, then losing my train of thought and spending time trying to retrace my steps. This is particularly hard in a project with so many extra elements.
- In my research for self-collision implementations, I found many ways that would be far more efficient, such as dividing the cloth into separate regions, testing for collisions with those larger areas first and only testing for the smaller, more taxing particles if the larger collisions have occurred. If I had time in future, I would definitely look into implementing that technique further, so as to make this simulation as realistic and optimised as possible.
- I’m very happy with how my dialog and interface systems turned out. I feel as though having the ability to quickly alter various settings in a separate window provided a very simple yet effective control to the user.
- The cutting functionality really stressed the importance of managing the order in which things are executed. Initially, I was cutting my constraints as soon as needed, which was causing interference with my other constraints, causing crashes. This caused me to make the clean-up system, much like how Box2D does their clean up after the simulation step. I still had issues, however, as when I would cut constraints, they would also cut random other constraints in the cloth. I initially thought it was an issue to do with my ray-casting, but after a long, long time, it was revealed that when I was handling the erasing of my constraints, it was affecting the future cuts, as all indexes were offset. In order to fix this, I no longer “deleted” constraints from the vector, rather setting their values to null, in order to maintain the positions in the vector.
Overall, I am extremely happy with the result from this assignment. It was a fantastic learning experience, and an opportunity to test and apply various techniques in a visually appealing way.