/* Calculate the face or vertex normals of the current vertex data. */ void VertexData::calculateNormals() { #ifndef NDEBUG int wrongNormals = 0; #endif normals.clear(); normals.reserve(vertices.size()); // initialize all normals to zero for (size_t i = 0; i < vertices.size(); ++i) normals.push_back(Normal(0, 0, 0)); // iterate over all triangles and add their normals to adjacent vertices #pragma omp parallel for for (ssize_t i = 0; i < ssize_t(triangles.size()); ++i) { const Index i0 = triangles[i][0]; const Index i1 = triangles[i][1]; const Index i2 = triangles[i][2]; const Normal normal = vmml::compute_normal(vertices[i0], vertices[i1], vertices[i2]); #ifndef NDEBUG // count emtpy normals in debug mode if (normal.length() == 0.0f) ++wrongNormals; // racy with OpenMP, but not critical #endif normals[i0] += normal; normals[i1] += normal; normals[i2] += normal; } // normalize all the normals #pragma omp parallel for for (ssize_t i = 0; i < ssize_t(vertices.size()); ++i) normals[i].normalize(); #ifndef NDEBUG if (wrongNormals > 0) PLYLIBINFO << wrongNormals << " faces have no valid normal." << std::endl; #endif }
bool TriMesh::computeTangentSpaceBasis() { int zeroArea = 0, zeroNormals = 0; if (!m_texcoords) { bool anisotropic = hasBSDF() && m_bsdf->getType() & BSDF::EAnisotropic; if (anisotropic) Log(EError, "\"%s\": computeTangentSpace(): texture coordinates " "are required to generate tangent vectors. If you want to render with an anisotropic " "material, please make sure that all associated shapes have valid texture coordinates.", getName().c_str()); return false; } if (m_tangents) Log(EError, "Tangent space vectors have already been generated!"); if (!m_normals) { Log(EWarn, "Vertex normals are required to compute a tangent space basis!"); return false; } m_tangents = new TangentSpace[m_vertexCount]; memset(m_tangents, 0, sizeof(TangentSpace)); /* No. of triangles sharing a vertex */ uint32_t *sharers = new uint32_t[m_vertexCount]; for (size_t i=0; i<m_vertexCount; i++) { m_tangents[i].dpdu = Vector(0.0f); m_tangents[i].dpdv = Vector(0.0f); if (m_normals[i].isZero()) { zeroNormals++; m_normals[i] = Normal(1.0f, 0.0f, 0.0f); } sharers[i] = 0; } for (size_t i=0; i<m_triangleCount; i++) { uint32_t idx0 = m_triangles[i].idx[0], idx1 = m_triangles[i].idx[1], idx2 = m_triangles[i].idx[2]; const Point &v0 = m_positions[idx0]; const Point &v1 = m_positions[idx1]; const Point &v2 = m_positions[idx2]; const Point2 &uv0 = m_texcoords[idx0]; const Point2 &uv1 = m_texcoords[idx1]; const Point2 &uv2 = m_texcoords[idx2]; Vector dP1 = v1 - v0, dP2 = v2 - v0; Vector2 dUV1 = uv1 - uv0, dUV2 = uv2 - uv0; Float invDet = 1.0f, determinant = dUV1.x * dUV2.y - dUV1.y * dUV2.x; if (determinant != 0) invDet = 1.0f / determinant; Vector dpdu = ( dUV2.y * dP1 - dUV1.y * dP2) * invDet; Vector dpdv = (-dUV2.x * dP1 + dUV1.x * dP2) * invDet; if (dpdu.length() == 0.0f) { /* Recovery - required to recover from invalid geometry */ Normal n = Normal(cross(v1 - v0, v2 - v0)); Float length = n.length(); if (length != 0) { n /= length; dpdu = cross(n, dpdv); if (dpdu.length() == 0.0f) { /* At least create some kind of tangent space basis (fair enough for isotropic BxDFs) */ coordinateSystem(n, dpdu, dpdv); } } else { zeroArea++; } } if (dpdv.length() == 0.0f) { Normal n = Normal(cross(v1 - v0, v2 - v0)); Float length = n.length(); if (length != 0) { n /= length; dpdv = cross(dpdu, n); if (dpdv.length() == 0.0f) { /* At least create some kind of tangent space basis (fair enough for isotropic BxDFs) */ coordinateSystem(n, dpdu, dpdv); } } else { zeroArea++; } } m_tangents[idx0].dpdu += dpdu; m_tangents[idx1].dpdu += dpdu; m_tangents[idx2].dpdu += dpdu; m_tangents[idx0].dpdv += dpdv; m_tangents[idx1].dpdv += dpdv; m_tangents[idx2].dpdv += dpdv; sharers[idx0]++; sharers[idx1]++; sharers[idx2]++; } /* Orthogonalization + Normalization pass */ for (size_t i=0; i<m_vertexCount; i++) { Vector &dpdu = m_tangents[i].dpdu; Vector &dpdv = m_tangents[i].dpdv; if (dpdu.lengthSquared() == 0.0f || dpdv.lengthSquared() == 0.0f) { /* At least create some kind of tangent space basis (fair enough for isotropic BxDFs) */ coordinateSystem(m_normals[i], dpdu, dpdv); } else { if (sharers[i] > 0) { dpdu /= (Float) sharers[i]; dpdv /= (Float) sharers[i]; } } } delete[] sharers; if (zeroArea > 0 || zeroNormals > 0) Log(EWarn, "\"%s\": computeTangentSpace(): Mesh contains invalid " "geometry: %i zero area triangles and %i zero normals found!", m_name.c_str(), zeroArea, zeroNormals); return true; }