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(); }
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::computeTangentSpace(bool doNormals) { const unsigned int numVertices = this->numVertices(); if (numVertices == 0) return shared_from_this(); auto xyzBuffer = vertexBuffer("position"); if (!xyzBuffer) throw std::logic_error("Computation of tangent space requires positions."); auto uvBuffer = vertexBuffer("uv"); if (!uvBuffer) throw std::logic_error("Computation of tangent space requires uvs."); if (doNormals) computeNormals(); const auto ushortIndices = indices()->dataPointer<unsigned short>(); const auto uintIndices = indices()->dataPointer<unsigned int>(); const unsigned int numFaces = (ushortIndices ? ushortIndices->size() : uintIndices->size()) / 3u; unsigned int vertexIds[3] = { 0, 0, 0 }; std::vector<math::vec3> xyz(3); std::vector<math::vec2> uv(3); const unsigned int xyzSize = xyzBuffer->vertexSize(); const unsigned int xyzOffset = xyzBuffer->attribute("position").offset; const std::vector<float>& xyzData = xyzBuffer->data(); const unsigned int uvSize = uvBuffer->vertexSize(); const unsigned int uvOffset = uvBuffer->attribute("uv").offset; 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] = ushortIndices ? static_cast<unsigned int>(ushortIndices->at(offset++)) : uintIndices->at(offset++); unsigned int index = xyzOffset + vertexIds[k] * xyzSize; xyz[k] = { xyzData[index], xyzData[index + 1], xyzData[index + 2] }; index = uvOffset + vertexIds[k] * uvSize; uv[k] = { uvData[index], uvData[index + 1] }; } auto uv02 = uv[0] - uv[2]; auto uv12 = uv[1] - uv[2]; const float denom = uv02.x * uv12.y - uv12.x * uv02.y; const float invDenom = fabsf(denom) > 1e-6f ? 1.f / denom : 1.f; math::vec3 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(); }