Real LodCollapseCostQuadric::computeEdgeCollapseCost( LodData* data, LodData::Vertex* src, LodData::Edge* dstEdge ) { LodData::Vertex* dst = dstEdge->dst; if (isBorderVertex(src)) { return LodData::NEVER_COLLAPSE_COST; } if (src->seam && !dst->seam) { return LodData::NEVER_COLLAPSE_COST; } Matrix4 Qnew = mVertexQuadricList[LodData::getVectorIDFromPointer(data->mVertexList, src)] + mVertexQuadricList[LodData::getVectorIDFromPointer(data->mVertexList, dst)]; Vector4 Vnew(dst->position); // error = Vnew^T * Qnew * Vnew Real cost = (Vnew[0] * Qnew[0][0] + Vnew[1] * Qnew[0][1] + Vnew[2] * Qnew[0][2] + Vnew[3] * Qnew[0][3]) * Vnew[0] + (Vnew[0] * Qnew[1][0] + Vnew[1] * Qnew[1][1] + Vnew[2] * Qnew[1][2] + Vnew[3] * Qnew[1][3]) * Vnew[1] + (Vnew[0] * Qnew[2][0] + Vnew[1] * Qnew[2][1] + Vnew[2] * Qnew[2][2] + Vnew[3] * Qnew[2][3]) * Vnew[2] + (Vnew[0] * Qnew[3][0] + Vnew[1] * Qnew[3][1] + Vnew[2] * Qnew[3][2] + Vnew[3] * Qnew[3][3]) * Vnew[3]; if (dst->seam) { cost *= 8; } return cost; }
Real LodCollapseCostCurvature::computeEdgeCollapseCost( LodData* data, LodData::Vertex* src, LodData::Edge* dstEdge ) { LodData::Vertex* dst = dstEdge->dst; // Degenerate case check // Are we going to invert a face normal of one of the neighboring faces? // Can occur when we have a very small remaining edge and collapse crosses it // Look for a face normal changing by > 90 degrees if(MESHLOD_QUALITY >= 2) { // 30% speedup if disabled. LodData::VTriangles::iterator it = src->triangles.begin(); LodData::VTriangles::iterator itEnd = src->triangles.end(); for (; it != itEnd; ++it) { LodData::Triangle* triangle = *it; // Ignore the deleted faces (those including src & dest) if (!triangle->hasVertex(dst)) { // Test the new face normal LodData::Vertex* pv0, * pv1, * pv2; // Replace src with dest wherever it is pv0 = (triangle->vertex[0] == src) ? dst : triangle->vertex[0]; pv1 = (triangle->vertex[1] == src) ? dst : triangle->vertex[1]; pv2 = (triangle->vertex[2] == src) ? dst : triangle->vertex[2]; // Cross-product 2 edges Vector3 e1 = pv1->position - pv0->position; Vector3 e2 = pv2->position - pv1->position; Vector3 newNormal = e1.crossProduct(e2); // Dot old and new face normal // If < 0 then more than 90 degree difference if (newNormal.dotProduct(triangle->normal) < 0.0f) { // Don't do it! return LodData::NEVER_COLLAPSE_COST; } } } } Real cost; // Special cases // If we're looking at a border vertex if (isBorderVertex(src)) { if (dstEdge->refCount > 1) { // src is on a border, but the src-dest edge has more than one tri on it // So it must be collapsing inwards // Mark as very high-value cost // curvature = 1.0f; cost = 1.0f; } else { // Collapsing ALONG a border // We can't use curvature to measure the effect on the model // Instead, see what effect it has on 'pulling' the other border edges // The more colinear, the less effect it will have // So measure the 'kinkiness' (for want of a better term) // Find the only triangle using this edge. // PMTriangle* triangle = findSideTriangle(src, dst); cost = -1.0f; Vector3 collapseEdge = src->position - dst->position; collapseEdge.normalise(); LodData::VEdges::iterator it = src->edges.begin(); LodData::VEdges::iterator itEnd = src->edges.end(); for (; it != itEnd; it++) { LodData::Vertex* neighbor = it->dst; if (neighbor != dst && it->refCount == 1) { Vector3 otherBorderEdge = src->position - neighbor->position; otherBorderEdge.normalise(); // This time, the nearer the dot is to -1, the better, because that means // the edges are opposite each other, therefore less kinkiness // Scale into [0..1] Real kinkiness = otherBorderEdge.dotProduct(collapseEdge); cost = std::max<Real>(cost, kinkiness); } } cost = (1.002f + cost) * 0.5f; } } else { // not a border // Standard inner vertex // Calculate curvature // use the triangle facing most away from the sides // to determine our curvature term // Iterate over src's faces again cost = 1.0f; LodData::VTriangles::iterator it = src->triangles.begin(); LodData::VTriangles::iterator itEnd = src->triangles.end(); for (; it != itEnd; ++it) { Real mincurv = -1.0f; // curve for face i and closer side to it LodData::Triangle* triangle = *it; LodData::VTriangles::iterator it2 = src->triangles.begin(); for (; it2 != itEnd; ++it2) { LodData::Triangle* triangle2 = *it2; if (triangle2->hasVertex(dst)) { // Dot product of face normal gives a good delta angle Real dotprod = triangle->normal.dotProduct(triangle2->normal); // NB we do (1-..) to invert curvature where 1 is high curvature [0..1] // Whilst dot product is high when angle difference is low mincurv = std::max<Real>(mincurv, dotprod); } } cost = std::min<Real>(cost, mincurv); } cost = (1.002f - cost) * 0.5f; } // check for texture seam ripping and multiple submeshes if (src->seam) { if (!dst->seam) { cost = std::max<Real>(cost, (Real)0.05f); cost *= 64; } else { if(MESHLOD_QUALITY >= 3) { int seamNeighbors = 0; LodData::Vertex* otherSeam; LodData::VEdges::iterator it = src->edges.begin(); LodData::VEdges::iterator itEnd = src->edges.end(); for (; it != itEnd; it++) { LodData::Vertex* neighbor = it->dst; if(neighbor->seam) { seamNeighbors++; if(neighbor != dst){ otherSeam = neighbor; } } } if(seamNeighbors != 2 || (seamNeighbors == 2 && dst->edges.has(LodData::Edge(otherSeam)))) { cost = std::max<Real>(cost, 0.05f); cost *= 64; } else { cost = std::max<Real>(cost, 0.005f); cost *= 8; } } else { cost = std::max<Real>(cost, 0.005f); cost *= 8; } } } Real diff = src->normal.dotProduct(dst->normal) / 8.0f; Real dist = src->position.distance(dst->position); cost = cost * dist; if(data->mUseVertexNormals){ //Take into account vertex normals. Real normalCost = 0; LodData::VEdges::iterator it = src->edges.begin(); LodData::VEdges::iterator itEnd = src->edges.end(); for (; it != itEnd; ++it) { LodData::Vertex* neighbor = it->dst; Real beforeDist = neighbor->position.distance(src->position); Real afterDist = neighbor->position.distance(dst->position); Real beforeDot = neighbor->normal.dotProduct(src->normal); Real afterDot = neighbor->normal.dotProduct(dst->normal); normalCost = std::max<Real>(normalCost, std::max<Real>(diff, std::abs(beforeDot - afterDot)) * std::max<Real>(afterDist/8.0f, std::max<Real>(dist, std::abs(beforeDist - afterDist)))); } cost = std::max<Real>(normalCost * 0.25f, cost); } OgreAssert(cost >= 0 && cost != LodData::UNINITIALIZED_COLLAPSE_COST, "Invalid collapse cost"); return cost; }