Geometry::Ptr Geometry::computeTangentSpace(bool doNormals) { const unsigned int numVertices = this->numVertices(); if (numVertices == 0) return shared_from_this(); if (!_data->hasProperty("position") || !_data->hasProperty("uv")) throw std::logic_error("Computation of tangent space requires positions and uv."); if (doNormals) computeNormals(); const std::vector<unsigned short>& indices (this->indices()->data()); const unsigned int numFaces = indices.size() / 3; unsigned short vertexIds[3] = { 0, 0, 0 }; std::vector<Vector3::Ptr> xyz(3); std::vector<Vector2::Ptr> uv(3); VertexBuffer::Ptr xyzBuffer = _data->get<VertexBuffer::Ptr>("position"); const unsigned int xyzSize = xyzBuffer->vertexSize(); const unsigned int xyzOffset = std::get<2>(*xyzBuffer->attribute("position")); const std::vector<float>& xyzData = xyzBuffer->data(); VertexBuffer::Ptr uvBuffer = _data->get<VertexBuffer::Ptr>("uv"); const unsigned int uvSize = uvBuffer->vertexSize(); const unsigned int uvOffset = std::get<2>(*uvBuffer->attribute("uv")); const std::vector<float>& uvData = uvBuffer->data(); std::vector<float> tangentsData(3 * numVertices, 0.0f); for (unsigned int i = 0, offset = 0; i < numFaces; ++i) { for (unsigned int k = 0; k < 3; ++k) { vertexIds[k] = indices[offset++]; unsigned int index = xyzOffset + vertexIds[k] * xyzSize; xyz[k] = Vector3::create(xyzData[index], xyzData[index + 1], xyzData[index + 2]); index = uvOffset + vertexIds[k] * uvSize; uv[k] = Vector2::create(uvData[index], uvData[index + 1]); } Vector2::Ptr uv02 = uv[0] - uv[2]; Vector2::Ptr uv12 = uv[1] - uv[2]; const float denom = uv02->x() * uv12->y() - uv12->x() * uv02->y(); const float invDenom = fabsf(denom) > 1e-6f ? 1.0f/denom : 1.0f; Vector3::Ptr faceTangent = ((xyz[0]-xyz[2]) * uv12->y() - (xyz[1]-xyz[2]) * uv02->y()) * invDenom; for (unsigned int k=0; k<3; ++k) { const unsigned int index = 3 * vertexIds[k]; tangentsData[index] += faceTangent->x(); tangentsData[index + 1] += faceTangent->y(); tangentsData[index + 2] += faceTangent->z(); } } for (unsigned int i = 0, index = 0; i < numVertices; ++i, index += 3) { const float x = tangentsData[index]; const float y = tangentsData[index + 1]; const float z = tangentsData[index + 2]; const float lengthSquared = x * x + y * y + z * z; const float invLength = lengthSquared > 1e-6f ? 1.0f / sqrtf(lengthSquared) : 1.0f; tangentsData[index] *= invLength; tangentsData[index + 1] *= invLength; tangentsData[index + 2] *= invLength; } VertexBuffer::Ptr tangentsBuffer = VertexBuffer::create(xyzBuffer->context(), tangentsData); tangentsBuffer->addAttribute("tangent", 3, 0); addVertexBuffer(tangentsBuffer); return shared_from_this(); }
Geometry::Ptr Geometry::computeNormals() { const unsigned int numVertices = this->numVertices(); if (numVertices == 0) return shared_from_this(); if (_data->hasProperty("normal")) throw std::logic_error("The geometry already stores precomputed normals."); if (!_data->hasProperty("position")) throw std::logic_error("Computation of normals requires positions."); const std::vector<unsigned short>& indices = this->indices()->data(); const unsigned int numFaces = indices.size() / 3; unsigned short vertexIds[3] = { 0, 0, 0 }; std::vector<Vector3::Ptr> xyz(3); VertexBuffer::Ptr xyzBuffer = _data->get<VertexBuffer::Ptr>("position"); const unsigned int xyzSize = xyzBuffer->vertexSize(); const unsigned int xyzOffset = std::get<2>(*xyzBuffer->attribute("position")); const std::vector<float>& xyzData = xyzBuffer->data(); std::vector<float> normalsData(3 * numVertices, 0.0f); for (unsigned int i = 0, offset = 0; i < numFaces; ++i) { for (unsigned int k = 0; k < 3; ++k) { vertexIds[k] = indices[offset++]; const unsigned int index = xyzOffset + vertexIds[k] * xyzSize; xyz[k] = Vector3::create(xyzData[index], xyzData[index + 1], xyzData[index + 2]); } Vector3::Ptr faceNormal = Vector3::create() ->copyFrom(xyz[0] - xyz[1]) ->cross(xyz[0] - xyz[2]); for (unsigned int k = 0; k < 3; ++k) { const unsigned int index = 3 * vertexIds[k]; normalsData[index] += faceNormal->x(); normalsData[index + 1] += faceNormal->y(); normalsData[index + 2] += faceNormal->z(); } } for (unsigned int i = 0, index = 0; i < numVertices; ++i, index += 3) { const float x = normalsData[index]; const float y = normalsData[index + 1]; const float z = normalsData[index + 2]; const float lengthSquared = x * x + y * y + z * z; const float invLength = lengthSquared > 1e-6f ? 1.0f / sqrtf(lengthSquared) : 1.0f; normalsData[index] *= invLength; normalsData[index + 1] *= invLength; normalsData[index + 2] *= invLength; } VertexBuffer::Ptr normalsBuffer = VertexBuffer::create(xyzBuffer->context(), normalsData); normalsBuffer->addAttribute("normal", 3, 0); addVertexBuffer(normalsBuffer); return shared_from_this(); }