//-------------------------------------------------------------------------------------------------- // Name: ValidateExtractedOutline // Desc: Performs a final validation pass on the extracted outline //-------------------------------------------------------------------------------------------------- bool CBreakableGlassSystem::ValidateExtractedOutline(SBreakableGlassPhysData& data, SBreakableGlassInitParams& initParams) { bool valid = true; // Check for overlapping points (leads to FPE during triangulation) if (initParams.pInitialFrag && initParams.numInitialFragPts > 0) { const Vec2* pPts = initParams.pInitialFrag; const uint numEdges = initParams.numInitialFragPts-1; const float minEdgeLen = 0.0001f; for (uint i = 0, j = 1; i < numEdges; ++i, ++j) { const Vec2 edge(pPts[i] - pPts[j]); if (edge.GetLength2() < minEdgeLen) { LOG_GLASS_ERROR("Extracted mesh has invalid edges."); valid = false; break; } } } // Check for overlapping UVs (leads to FPE during uv basis calculation) if (valid) { const Vec2 uvPtA(data.uvBasis[0].x, data.uvBasis[0].y); const Vec2 uvPtB(data.uvBasis[1].x, data.uvBasis[1].y); const Vec2 uvPtC(data.uvBasis[2].x, data.uvBasis[2].y); const Vec2 uvEdge0(uvPtC - uvPtA); const Vec2 uvEdge1(uvPtB - uvPtA); const float dot00 = uvEdge0.Dot(uvEdge0); const float dot01 = uvEdge0.Dot(uvEdge1); const float dot11 = uvEdge1.Dot(uvEdge1); const float epsilon = 0.001f; if (fabs_tpl(dot00 * dot11 - dot01 * dot01) < epsilon) { LOG_GLASS_ERROR("Extracted mesh has invalid uv layout."); valid = false; } } return valid; }//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------- // Name: CalculateGlassAnchors // Desc: Ensures mesh is within bounds and calculates anchor points //-------------------------------------------------------------------------------------------------- void CBreakableGlassSystem::CalculateGlassAnchors(SBreakableGlassPhysData& data, SBreakableGlassInitParams& initParams) { // Apply half-size offset to uv basis const Vec2& size = initParams.size; const Vec2 halfSize = size * 0.5f; for (uint i = 0; i < 3; ++i) { data.uvBasis[i].x += halfSize.x; data.uvBasis[i].y += halfSize.y; } // Only continue if non-default mesh used Vec2* pPts = initParams.pInitialFrag; const uint numPts = initParams.numInitialFragPts; if (pPts && numPts >= 3) { Vec2 minPt = size; Vec2 maxPt = Vec2Constants<float>::fVec2_Zero; // Offset fragment data to glass working space for (uint i = 0; i < numPts; ++i) { pPts[i] += halfSize; // Update bounds minPt.x = min(minPt.x, pPts[i].x); minPt.y = min(minPt.y, pPts[i].y); maxPt.x = max(maxPt.x, pPts[i].x); maxPt.y = max(maxPt.y, pPts[i].y); } // Calculate any final offset towards working bounds minPt.x = min(minPt.x, 0.0f); minPt.y = min(minPt.y, 0.0f); maxPt.x = max(maxPt.x - size.x, 0.0f); maxPt.y = max(maxPt.y - size.y, 0.0f); Vec2 offset; offset.x = (maxPt.x > 0.0f) ? -maxPt.x : -minPt.x; offset.y = (maxPt.y > 0.0f) ? -maxPt.y : -minPt.y; // Adjust bounds if off if (offset.GetLength2() > 0.0f) { for (uint i = 0; i < numPts; ++i) { pPts[i] += offset; } for (uint i = 0; i < 3; ++i) { data.uvBasis[i].x += offset.x; data.uvBasis[i].y += offset.y; } } // Determine best anchor points const uint maxNumAnchors = SBreakableGlassInitParams::MaxNumAnchors; const uint numAnchors = min(numPts, maxNumAnchors); initParams.numAnchors = numAnchors; if (numPts <= maxNumAnchors) { // If few enough points, use them directly for (uint i = 0; i < numAnchors; ++i) { initParams.anchors[i] = pPts[i]; } } else { // Else, determine mesh points closest to corners const Vec2 anchorPos[maxNumAnchors] = { Vec2(0.0f, 0.0f), Vec2(0.0f, initParams.size.y), Vec2(initParams.size.x, 0.0f), Vec2(initParams.size.x, initParams.size.y) }; uint anchorPts[maxNumAnchors] = {0,0,0,0}; for (uint i = 0; i < maxNumAnchors; ++i) { float anchorDistSq = FLT_MAX; for (uint j = 0; j < numPts; ++j) { // Store point if new closest found const float distSq = (anchorPos[i] - pPts[j]).GetLength2(); if (distSq < anchorDistSq) { // Avoid duplicates bool duplicate = false; for (uint k = 0; k < i; ++k) { if (anchorPts[k] == j) { duplicate = true; break; } } if (!duplicate) { anchorDistSq = distSq; anchorPts[i] = j; } } } // Save closest pt to this corner initParams.anchors[i] = pPts[anchorPts[i]]; } } // Calculate approx. polygon center Vec2 center(Vec2Constants<float>::fVec2_Zero); const float fNumPts = (float)numPts; for (uint i = 0; i < numPts; ++i) { center += pPts[i]; } center *= __fres(fNumPts); // Finally, shift anchors slightly towards center const float anchorPtShift = 0.015f; for (uint i = 0; i < numAnchors; ++i) { Vec2 toCenter = (center - initParams.anchors[i]).GetNormalized(); initParams.anchors[i] += toCenter * anchorPtShift; } } }//-------------------------------------------------------------------------------------------------