void Model::calculate_normal(size_t idx) { // Index is assumed ASSERT(!m_shapes[idx].mesh.indices.empty()); const std::vector<unsigned int> & indices = m_shapes[idx].mesh.indices; const std::vector<float> & positions = m_shapes[idx].mesh.positions; // To store normal for each vertex std::vector<Vector3> new_normals(positions.size()/3, Vector3::Zero()); // For each face, calculate face normal, add it to each vertex of the face for ( size_t i=0; i < indices.size(); i += 3 ) { Vector3 a(positions[3*indices[i+1] ]-positions[3*indices[i] ], positions[3*indices[i+1]+1]-positions[3*indices[i]+1], positions[3*indices[i+1]+2]-positions[3*indices[i]+2]); Vector3 b(positions[3*indices[i+2] ]-positions[3*indices[i] ], positions[3*indices[i+2]+1]-positions[3*indices[i]+1], positions[3*indices[i+2]+2]-positions[3*indices[i]+2]); Vector3 n = a.cross(b).normalized(); new_normals[indices[i ]] += n; new_normals[indices[i+1]] += n; new_normals[indices[i+2]] += n; } for ( size_t i=0; i < new_normals.size(); i++ ) { new_normals[i].normalize(); } std::vector<float> & normals = m_shapes[idx].mesh.normals; if ( !normals.empty() ) WARN("Overwriting exisiting normals..."); normals.resize(positions.size()); for ( size_t i=0; i < indices.size(); i++ ) { normals[3*indices[i] ] = new_normals[indices[i]].x(); normals[3*indices[i]+1] = new_normals[indices[i]].y(); normals[3*indices[i]+2] = new_normals[indices[i]].z(); } }
void MeshWithConnectivity::LoopSubdivision() { // generate new (odd) vertices // visited edge -> vertex position information // Note that this is different from the one in computeConnectivity() typedef std::map<std::pair<int, int>, int> edgemap_t; edgemap_t new_vertices; // The new data must be doublebuffered or otherwise some of the calculations below would // not read the original positions but the newly changed ones, which is slightly wrong. std::vector<Vec3f> new_positions(positions.size()); std::vector<Vec3f> new_normals(normals.size()); std::vector<Vec3f> new_colors(colors.size()); for (size_t i = 0; i < indices.size(); ++i) for (int j = 0; j < 3; ++j) { int v0 = indices[i][j]; int v1 = indices[i][(j+1)%3]; // Map the edge endpoint indices to new vertex index. // We use min and max because the edge direction does not matter when we finally // rebuild the new faces (R3); this is how we always get unique indices for the map. auto edge = std::make_pair(min(v0, v1), max(v0, v1)); // With naive iteration, we would find each edge twice, because each is part of two triangles // (if the mesh does not have any holes/empty borders). Thus, we keep track of the already // visited edges in the new_vertices map. That requires the small R3 task below in the 'if' block. if (new_vertices.find(edge) == new_vertices.end()) { // YOUR CODE HERE (R4): compute the position for odd (= new) vertex. Vec3f pos, col, norm; int leftvertex, rightvertex; // You will need to use the neighbor information to find the correct vertices. // Be careful with indexing! // No need to worry about boundaries, though (except for the extra credit!). // Then, use the correct weights for each four corner vertex. // This default implementation just puts the new vertex at the edge midpoint. if (j == 0) leftvertex = indices[i][2]; else if (j == 1) leftvertex = indices[i][0]; else leftvertex = indices[i][1]; Vec3i neigh = neighborTris[i]; bool found = false; for (int k = 0; k < 3; k++) { int kms = neigh[k]; if (kms == -1) continue; int x = indices[kms][0], y= indices[kms][1], z = indices[kms][2]; if ((indices[kms][0] == v0 || indices[kms][1] == v0 || indices[kms][2] == v0) && (indices[kms][0] == v1 || indices[kms][1] == v1 || indices[kms][2] == v1)) { if ((x == v0 && y == v1) || (x == v1 && y == v0)) rightvertex = z; else if ((y == v0 && z == v1) || (z == v0 && y == v1)) rightvertex = x; else rightvertex = y; found = true; break; } } if (found){ pos = ((3.0f * (positions[v0] + positions[v1])) + (positions[leftvertex] + positions[rightvertex])) / 8.0f; col = ((3.0f * (colors[v0] + colors[v1])) + (colors[leftvertex] + colors[rightvertex])) / 8.0f; norm = ((3.0f * (normals[v0] + normals[v1])) + (normals[leftvertex] + normals[rightvertex])) / 8.0f; } else { pos = 0.5f * (positions[v0] + positions[v1]); col = 0.5f * (colors[v0] + colors[v1]); norm = 0.5f * (normals[v0] + normals[v1]); } new_positions.push_back(pos); new_colors.push_back(col); new_normals.push_back(norm); // YOUR CODE HERE (R3): // Map the edge to the correct vertex index. new_vertices[edge] = new_positions.size() - 1; // This is just one line! Use new_vertices and the index of the just added position. } } // compute positions for even (old) vertices std::vector<bool> vertexComputed(new_positions.size(), false); for (int i = 0; i < (int)indices.size(); ++i) { for (int j = 0; j < 3; ++j) { int v0 = indices[i][j]; // don't redo if this one is already done if (vertexComputed[v0]) continue; vertexComputed[v0] = true; Vec3f pos, col, norm; std::vector<int> neighbors; // YOUR CODE HERE (R5): reposition the old vertices // This default implementation just passes the data through unchanged. // You need to replace these three lines with the loop over the 1-ring // around vertex v0, and compute the new position as a weighted average // of the other vertices as described in the handout. for (int k = 0; k < indices.size(); k++){ for (int l = 0; l < 3; l++){ if (indices[k][l] == v0){ neighbors.push_back(indices[k][(l + 1) % 3]); neighbors.push_back(indices[k][(l + 2) % 3]); } } } sort(neighbors.begin(), neighbors.end()); neighbors.erase(std::unique(neighbors.begin(),neighbors.end()), neighbors.end()); int n = neighbors.size(); float b; if (n>3) b = 3.0f / (8.0f * n); else if (n == 3) b = 3.0f / 16.0f; else b = 0.0f; Vec3f rightsum1, rightsum2, rightsum3; for (int k = 0; k < neighbors.size(); k++){ rightsum1 += positions[neighbors[k]]; rightsum2 += colors[neighbors[k]]; rightsum3 += normals[neighbors[k]]; } rightsum1 *= (float)b; rightsum2 *= (float)b; rightsum3 *= (float)b; float ks = 1.0f - (float)n*(float)b; rightsum1 += ks * positions[v0]; rightsum2 += ks * colors[v0]; rightsum3 += ks * normals[v0]; pos = rightsum1; col = rightsum2; norm = rightsum3; new_positions[v0] = pos; new_colors[v0] = col; new_normals[v0] = norm; } } // and then, finally, regenerate topology // every triangle turns into four new ones std::vector<Vec3i> new_indices; new_indices.reserve(indices.size()*4); for (size_t i = 0; i < indices.size(); ++i) { Vec3i even = indices[i]; // start vertices of e_0, e_1, e_2 // YOUR CODE HERE (R3): // fill in X and Y (it's the same for both) auto edge_a = std::make_pair(min(even.x, even.y), max(even.x, even.y)); auto edge_b = std::make_pair(min(even.z, even.y), max(even.z, even.y)); auto edge_c = std::make_pair(min(even.x, even.z), max(even.x, even.z)); // The edges edge_a, edge_b and edge_c now define the vertex indices via new_vertices. // (The mapping is done in the loop above.) // The indices define the smaller triangle inside the indices defined by "even", in order. // Read the vertex indices out of new_vertices to build the small triangle "odd" int a, b, c; a = new_vertices[edge_a]; b = new_vertices[edge_b]; c = new_vertices[edge_c]; // Vec3i odd = ... new_indices.push_back(Vec3i(a,b,c)); new_indices.push_back(Vec3i(a, b, even[1])); new_indices.push_back(Vec3i(a,c, even[0])); new_indices.push_back(Vec3i(b, c, even[2])); // Then, construct the four smaller triangles from the surrounding big triangle "even" // and the inner one, "odd". Push them to "new_indices". // NOTE: REMOVE the following line after you're done with the new triangles. // This just keeps the mesh intact and serves as an example on how to add new triangles. //new_indices.push_back( Vec3i( even[0], even[1], even[2] ) ); } // ADD THESE LINES when R3 is finished. Replace the originals with the repositioned data. indices = std::move(new_indices); positions = std::move(new_positions); normals = std::move(new_normals); colors = std::move(new_colors); neighborTris.size(); }