The biggest undertakings or features that I added moving forward from the check-in were one-way interactions and texturing. Both of which had their complications.
For the interactions, I started with adding collisions with the floor before moving onto sphere collisions. Instead of just checking the y coordinates of the Nodes to be below a hardcoded y value, I wanted to make the interaction with the floor more arbitrary. I had an idea to use the size vector that each of my WorldObjects have to make this more arbitrary, but had stability issues where things would blow up or Nodes would get stuck in the floor and such. I ended up just assuming my floor would have a normal in the positive y direction to try to get falling off the edges to work, but that turned out to be more difficult that I would've expected. Since I am only checking Node positions and not the springs between them, the floor ends up intersecting the springs and causing Nodes to get stuck below the floor.
After I settled on a simpler floor interaction than I originally wanted, I moved onto sphere-cloth interactions. My first idea was to once again use the size vector that describes the x, y, and z sizes of a sphere as a means of checking for a collision. However, that implementation was buggy and incorrect, so I once again settled for a simpler approach that assumed equal x, y, and z sizes and checked for collision against the x component only.
Texturing my cloth was the most difficult feature of my submission. I spent several hours this past weekend hand-coding the implementation I had in mind in an attempt to be extremely careful and also in an attempt to avoid blowing everything up when I tried to actually do it. The idea I had was this:
After finally diving into incorporating this method, I had a small triangle showing up that was perpendicular to the initial Camera and therefore I had to move around to see it. This little triangle wouldn't even move when I used the arrow keys to translate the cloth. After combing through and checking the values I had stored in my buffers, I was completely stuck as to what was wrong. I asked Liam for help debugging and after explaining my method and after looking through my code for hour, we finally had the idea to shift some lines of code around in my setupGraphics() function, which is partially shown below. It turned out that in order to correctly build the VAO mapping of the buffer values to the shader inputs, the correct attribute pointers needed to be set up when their corresponding buffer was bound to the current context. I had previously made the attribute function calls after binding and loading all of my buffers, which I now understand and know to be completely incorrect.
//1. check with floor
Vec3D f_size = floor->getSize();
Vec3D f_pos = floor->getPos();
float COR = 0.2;
Vec3D f_dist = in_pos - f_pos;
float distalong_n = dotProduct(f_dist, floor_normal);
float sizealong_n = dotProduct(f_size, floor_normal);
if (distalong_n < sizealong_n + 0.1
&& fabs(f_dist.getX()) < f_size.getX()/2
&& fabs(f_dist.getZ()) < f_size.getZ()/2)
{
out_vel = util::calcCollisionVel(in_vel, floor_normal,
COR);
out_pos = in_pos + (dt+0.01)*out_vel;
return;
}
//2. check with sphere
Vec3D s_size = sphere->getSize();
Vec3D s_pos = sphere->getPos();
Vec3D s_dist = in_pos - s_pos;
float dist_sq = dotProduct(s_dist, s_dist);
//float r_sq = dotProduct(s_size, s_size);
float r_sq = s_size.getX();
if (dist_sq < r_sq)
{
s_dist.normalize();//radial vector normalized
out_vel = util::calcCollisionVel(in_vel, s_dist, COR);
//move outside of sphere along radial vector
out_pos = s_pos + (sqrt(r_sq) + 0.0001)*s_dist;
return;
}
//no collisions
out_vel = in_vel;
out_pos = in_pos;
#pragma omp parallel for schedule(dynamic)
/////////////////////////////////
//BUILD TEXTURED VAO + VBO
/////////////////////////////////
glGenVertexArrays(1, &textured_vao);
glBindVertexArray(textured_vao);
glGenBuffers(2, textured_vbos); //get 2 ids for the 2 VBOs
//1.1 POSITIONS + NORMALS --> textured_vbos[0]
glBindBuffer(GL_ARRAY_BUFFER, textured_vbos[0]);
glBufferData(GL_ARRAY_BUFFER, num_nodes * 6 * sizeof(float), texturedData, GL_STREAM_DRAW); //dynamic
//1.2 setup position and normal attributes --> need to set now while textured_vbos[0] is bound
GLint tex_posAttrib = glGetAttribLocation(phongProgram, "position");
glVertexAttribPointer(tex_posAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), 0); //positions first
glEnableVertexAttribArray(tex_posAttrib);
GLint tex_normAttrib = glGetAttribLocation(phongProgram, "inNormal");
glVertexAttribPointer(tex_normAttrib, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); //normals second
glEnableVertexAttribArray(tex_normAttrib);
//2.1 TEX COORDS --> textured_vbos[1]
glBindBuffer(GL_ARRAY_BUFFER, textured_vbos[1]);
glBufferData(GL_ARRAY_BUFFER, num_nodes * 2 * sizeof(float), texturedCoords, GL_STATIC_DRAW);
//2.2 setup texture coord attributes --> need to set now while textured_vbos[1] is bound
GLint tex_texAttrib = glGetAttribLocation(phongProgram, "inTexcoord");
glVertexAttribPointer(tex_texAttrib, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);
glEnableVertexAttribArray(tex_texAttrib);
//3. INDICES --> textured_ibo
glGenBuffers(1, textured_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, textured_ibo[0]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, total_triangles * 3 * sizeof(unsigned int), texturedIndices, GL_STATIC_DRAW);
Video 1: 20 x 20 node cloth rendered at 60 FPS. Video demonstrates various controls (can be seen through outputs to the terminal).
Video 2 : 30 x 30 node cloth rendered at 60 FPS