inline void InsertRecord (const String& name, uint elements) { Record& r = g_uniforms.Expand(); r.name = name; r.elements = elements; }
void Mesh::_CalculateNormalsAndTangents() { if (mT.IsValid() || mV.IsEmpty()) return; // Don't bother with points or lines if (mPrimitive == IGraphics::Primitive::Point || mPrimitive == IGraphics::Primitive::Line || mPrimitive == IGraphics::Primitive::LineStrip) return; // Allocate a temporary buffer to store binormals Array<Vector3f> binormals (mV.GetSize()); // Remember whether we already have normals to work with bool calculateNormals = (mV.GetSize() != mN.GetSize()); // We can only calculate tangents if the texture coordinates are available bool calculateTangents = (mV.GetSize() == mTc0.GetSize()); // If we should calculate normals, clear the existing normal array if (calculateNormals) { mN.Clear(); mN.ExpandTo(mV.GetSize()); } // Expand the tangent array if (calculateTangents) mT.ExpandTo(mV.GetSize()); // The number of indices uint size = mIndices.IsValid() ? mIndices.GetSize() : GetNumberOfVertices(); // Triangles if (size > 2) { bool even = true; uint i0, i1, i2; for (uint i = 0; i + 2 < size; ) { i0 = i; i1 = i+1; i2 = i+2; if (mIndices.IsValid()) { i0 = mIndices[i0]; i1 = mIndices[i1]; i2 = mIndices[i2]; } ASSERT(i0 < mV.GetSize(), "Index out of bounds!"); ASSERT(i1 < mV.GetSize(), "Index out of bounds!"); ASSERT(i2 < mV.GetSize(), "Index out of bounds!"); const Vector3f& v0 ( mV[i0] ); const Vector3f& v1 ( mV[i1] ); const Vector3f& v2 ( mV[i2] ); Vector3f v10 (v1 - v0); Vector3f v20 (v2 - v0); if (calculateNormals) { Vector3f normal (Cross(v10, v20)); mN[i0] += normal; mN[i1] += normal; mN[i2] += normal; } if (calculateTangents) { const Vector2f& t0 ( mTc0[i0] ); const Vector2f& t1 ( mTc0[i1] ); const Vector2f& t2 ( mTc0[i2] ); Vector2f t10 (t1 - t0); Vector2f t20 (t2 - t0); float denominator = t10.x * t20.y - t20.x * t10.y; if ( Float::IsNotZero(denominator) ) { float scale = 1.0f / denominator; Vector3f tangent ((v10.x * t20.y - v20.x * t10.y) * scale, (v10.y * t20.y - v20.y * t10.y) * scale, (v10.z * t20.y - v20.z * t10.y) * scale); Vector3f binormal((v20.x * t10.x - v10.x * t20.x) * scale, (v20.y * t10.x - v10.y * t20.x) * scale, (v20.z * t10.x - v10.z * t20.x) * scale); mT[i0] += tangent; mT[i1] += tangent; mT[i2] += tangent; binormals[i0] += binormal; binormals[i1] += binormal; binormals[i2] += binormal; } } if (mPrimitive == IGraphics::Primitive::Triangle) { i += 3; } else if (mPrimitive == IGraphics::Primitive::Quad) { if (even) ++i; else i += 3; even = !even; } else ++i; } // If we're calculating normals we need to match all normals with identical vertices if (calculateNormals) { Array<uint> matches; for (uint i = 0; i < mV.GetSize(); ++i) { matches.Clear(); matches.Expand() = i; Vector3f N = mN[i]; const Vector3f& V (mV[i]); for (uint b = 0; b < mV.GetSize(); ++b) { if (i != b && V == mV[b]) { matches.Expand() = b; N += mN[b]; } } N.Normalize(); for (uint b = 0; b < matches.GetSize(); ++b) { mN[ matches[b] ] = N; } } } } if (calculateTangents) { // Normalize all tangents for (uint i = 0; i < mV.GetSize(); ++i) { Vector3f& T (mT[i]); Vector3f& B (binormals[i]); Vector3f& N (mN[i]); // In order to avoid visible seams, the tangent should be 90 degrees to the normal // Note to self: Gram-Schmidt formula for the cross product below: T = T - N * Dot(N, T); T = Cross(B, N); T.Normalize(); // Flip the tangent if the handedness is incorrect if (Dot(Cross(T, B), N) < 0.0f) T.Flip(); } } }