vec Plane::ClosestPoint(const LineSegment &lineSegment) const { /* ///@todo Output parametric d as well. float d; if (lineSegment.Intersects(*this, &d)) return lineSegment.GetPoint(d); else if (Distance(lineSegment.a) < Distance(lineSegment.b)) return Project(lineSegment.a); else return Project(lineSegment.b); */ assume(lineSegment.IsFinite()); assume(!IsDegenerate()); float aDist = Dot(normal, lineSegment.a); float bDist = Dot(normal, lineSegment.b); float denom = bDist - aDist; if (EqualAbs(denom, 0.f)) return Project(Abs(aDist) < Abs(bDist) ? lineSegment.a : lineSegment.b); // Project()ing the result here is not strictly necessary, // but done for numerical stability, so that Plane::Contains() // will return true for the returned point. else { ///@todo Output parametric t along the ray as well. float t = (d - Dot(normal, lineSegment.a)) / (bDist - aDist); t = Clamp01(t); // Project()ing the result here is necessary only if we clamped, but done for numerical stability, so that Plane::Contains() will // return true for the returned point. return Project(lineSegment.GetPoint(t)); } }
float3 Triangle::RandomPointOnEdge(LCG &rng) const { assume(!IsDegenerate()); float ab = a.Distance(b); float bc = b.Distance(c); float ca = c.Distance(a); float r = rng.Float(0, ab + bc + ca); if (r < ab) return a + (b-a) * r / ab; r -= ab; if (r < bc) return b + (c-b) * r / bc; r -= bc; return c + (a-c) * r / ca; }
vec Plane::ClosestPoint(const Ray &ray) const { assume(ray.IsFinite()); assume(!IsDegenerate()); // The plane and a ray have three configurations: // 1) the ray and the plane don't intersect: the closest point is the ray origin point. // 2) the ray and the plane do intersect: the closest point is the intersection point. // 3) the ray is parallel to the plane: any point on the ray projected to the plane is a closest point. float denom = Dot(normal, ray.dir); if (denom == 0.f) return Project(ray.pos); // case 3) float t = (d - Dot(normal, ray.pos)) / denom; if (t >= 0.f && t < 1e6f) // Numerical stability check: Instead of checking denom against epsilon, check the resulting t for very large values. return ray.GetPoint(t); // case 2) else return Project(ray.pos); // case 1) }
//////////////////////////////////////////////////////////////////////////////////////// // CreateStrips() // // Generates actual strips from the list-in-strip-order. // void NvStripifier::CreateStrips(const NvStripInfoVec& allStrips, IntVec& stripIndices, const bool bStitchStrips, unsigned int& numSeparateStrips, const bool bRestart, const unsigned int restartVal) { assert(numSeparateStrips == 0); NvFaceInfo tLastFace(0, 0, 0); NvFaceInfo tPrevStripLastFace(0, 0, 0); size_t nStripCount = allStrips.size(); //we infer the cw/ccw ordering depending on the number of indices //this is screwed up by the fact that we insert -1s to denote changing strips //this is to account for that int accountForNegatives = 0; for (size_t i = 0; i < nStripCount; i++) { NvStripInfo *strip = allStrips[i]; int nStripFaceCount = strip->m_faces.size(); assert(nStripFaceCount > 0); // Handle the first face in the strip { NvFaceInfo tFirstFace(strip->m_faces[0]->m_v0, strip->m_faces[0]->m_v1, strip->m_faces[0]->m_v2); // If there is a second face, reorder vertices such that the // unique vertex is first if (nStripFaceCount > 1) { int nUnique = NvStripifier::GetUniqueVertexInB(strip->m_faces[1], &tFirstFace); if (nUnique == tFirstFace.m_v1) { SWAP(tFirstFace.m_v0, tFirstFace.m_v1); } else if (nUnique == tFirstFace.m_v2) { SWAP(tFirstFace.m_v0, tFirstFace.m_v2); } // If there is a third face, reorder vertices such that the // shared vertex is last if (nStripFaceCount > 2) { if(IsDegenerate(strip->m_faces[1])) { int pivot = strip->m_faces[1]->m_v1; if(tFirstFace.m_v1 == pivot) { SWAP(tFirstFace.m_v1, tFirstFace.m_v2); } } else { int nShared0, nShared1; GetSharedVertices(strip->m_faces[2], &tFirstFace, &nShared0, &nShared1); if ( (nShared0 == tFirstFace.m_v1) && (nShared1 == -1) ) { SWAP(tFirstFace.m_v1, tFirstFace.m_v2); } } } } if( (i == 0) || !bStitchStrips || bRestart) { if(!IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1)) stripIndices.push_back(tFirstFace.m_v0); } else { // Double tap the first in the new strip stripIndices.push_back(tFirstFace.m_v0); // Check CW/CCW ordering if (NextIsCW(stripIndices.size() - accountForNegatives) != IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1)) { stripIndices.push_back(tFirstFace.m_v0); } } stripIndices.push_back(tFirstFace.m_v0); stripIndices.push_back(tFirstFace.m_v1); stripIndices.push_back(tFirstFace.m_v2); // Update last face info tLastFace = tFirstFace; } for (int j = 1; j < nStripFaceCount; j++) { int nUnique = GetUniqueVertexInB(&tLastFace, strip->m_faces[j]); if (nUnique != -1) { stripIndices.push_back(nUnique); // Update last face info tLastFace.m_v0 = tLastFace.m_v1; tLastFace.m_v1 = tLastFace.m_v2; tLastFace.m_v2 = nUnique; } else { //we've hit a degenerate stripIndices.push_back(strip->m_faces[j]->m_v2); tLastFace.m_v0 = strip->m_faces[j]->m_v0;//tLastFace.m_v1; tLastFace.m_v1 = strip->m_faces[j]->m_v1;//tLastFace.m_v2; tLastFace.m_v2 = strip->m_faces[j]->m_v2;//tLastFace.m_v1; } } // Double tap between strips. if (bStitchStrips && !bRestart) { if (i != nStripCount - 1) stripIndices.push_back(tLastFace.m_v2); } else if (bRestart) { stripIndices.push_back(restartVal); } else { //-1 index indicates next strip stripIndices.push_back(-1); accountForNegatives++; numSeparateStrips++; } // Update last face info tLastFace.m_v0 = tLastFace.m_v1; tLastFace.m_v1 = tLastFace.m_v2; tLastFace.m_v2 = tLastFace.m_v2; } if(bStitchStrips || bRestart) numSeparateStrips = 1; }
/////////////////////////////////////////////////////////////////////////////////////////// // BuildStripifyInfo() // // Builds the list of all face and edge infos // void NvStripifier::BuildStripifyInfo(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, const unsigned short maxIndex) { // reserve space for the face infos, but do not resize them. int numIndices = indices.size(); faceInfos.reserve(numIndices / 3); // we actually resize the edge infos, so we must initialize to NULL edgeInfos.resize(maxIndex + 1); for (unsigned short i = 0; i < maxIndex + 1; i++) edgeInfos[i] = NULL; // iterate through the triangles of the triangle list int numTriangles = numIndices / 3; int index = 0; bool bFaceUpdated[3]; for (int i = 0; i < numTriangles; i++) { bool bMightAlreadyExist = true; bFaceUpdated[0] = false; bFaceUpdated[1] = false; bFaceUpdated[2] = false; // grab the indices int v0 = indices[index++]; int v1 = indices[index++]; int v2 = indices[index++]; //we disregard degenerates if(IsDegenerate(v0, v1, v2)) continue; // create the face info and add it to the list of faces, but only if this exact face doesn't already // exist in the list NvFaceInfo *faceInfo = new NvFaceInfo(v0, v1, v2); // grab the edge infos, creating them if they do not already exist NvEdgeInfo *edgeInfo01 = FindEdgeInfo(edgeInfos, v0, v1); if (edgeInfo01 == NULL) { //since one of it's edges isn't in the edge data structure, it can't already exist in the face structure bMightAlreadyExist = false; // create the info edgeInfo01 = new NvEdgeInfo(v0, v1); // update the linked list on both edgeInfo01->m_nextV0 = edgeInfos[v0]; edgeInfo01->m_nextV1 = edgeInfos[v1]; edgeInfos[v0] = edgeInfo01; edgeInfos[v1] = edgeInfo01; // set face 0 edgeInfo01->m_face0 = faceInfo; } else { if (edgeInfo01->m_face1 != NULL) { printf("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"); } else { edgeInfo01->m_face1 = faceInfo; bFaceUpdated[0] = true; } } // grab the edge infos, creating them if they do not already exist NvEdgeInfo *edgeInfo12 = FindEdgeInfo(edgeInfos, v1, v2); if (edgeInfo12 == NULL) { bMightAlreadyExist = false; // create the info edgeInfo12 = new NvEdgeInfo(v1, v2); // update the linked list on both edgeInfo12->m_nextV0 = edgeInfos[v1]; edgeInfo12->m_nextV1 = edgeInfos[v2]; edgeInfos[v1] = edgeInfo12; edgeInfos[v2] = edgeInfo12; // set face 0 edgeInfo12->m_face0 = faceInfo; } else { if (edgeInfo12->m_face1 != NULL) { printf("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"); } else { edgeInfo12->m_face1 = faceInfo; bFaceUpdated[1] = true; } } // grab the edge infos, creating them if they do not already exist NvEdgeInfo *edgeInfo20 = FindEdgeInfo(edgeInfos, v2, v0); if (edgeInfo20 == NULL) { bMightAlreadyExist = false; // create the info edgeInfo20 = new NvEdgeInfo(v2, v0); // update the linked list on both edgeInfo20->m_nextV0 = edgeInfos[v2]; edgeInfo20->m_nextV1 = edgeInfos[v0]; edgeInfos[v2] = edgeInfo20; edgeInfos[v0] = edgeInfo20; // set face 0 edgeInfo20->m_face0 = faceInfo; } else { if (edgeInfo20->m_face1 != NULL) { printf("BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"); } else { edgeInfo20->m_face1 = faceInfo; bFaceUpdated[2] = true; } } if(bMightAlreadyExist) { if(!AlreadyExists(faceInfo, faceInfos)) faceInfos.push_back(faceInfo); else { delete faceInfo; //cleanup pointers that point to this deleted face if(bFaceUpdated[0]) edgeInfo01->m_face1 = NULL; if(bFaceUpdated[1]) edgeInfo12->m_face1 = NULL; if(bFaceUpdated[2]) edgeInfo20->m_face1 = NULL; } } else { faceInfos.push_back(faceInfo); } } }
/////////////////////////////////////////////////////////////////////////////////////////// // SplitUpStripsAndOptimize() // // Splits the input vector of strips (allBigStrips) into smaller, cache friendly pieces, then // reorders these pieces to maximize cache hits // The final strips are output through outStrips // void NvStripifier::SplitUpStripsAndOptimize(NvStripInfoVec &allStrips, NvStripInfoVec &outStrips, NvEdgeInfoVec& edgeInfos, NvFaceInfoVec& outFaceList) { int threshold = cacheSize; NvStripInfoVec tempStrips; //split up strips into threshold-sized pieces for(size_t i = 0; i < allStrips.size(); i++) { NvStripInfo* currentStrip; NvStripStartInfo startInfo(NULL, NULL, false); int actualStripSize = 0; for(size_t j = 0; j < allStrips[i]->m_faces.size(); ++j) { if( !IsDegenerate(allStrips[i]->m_faces[j]) ) actualStripSize++; } if(actualStripSize /*allStrips[i]->m_faces.size()*/ > threshold) { int numTimes = actualStripSize /*allStrips[i]->m_faces.size()*/ / threshold; int numLeftover = actualStripSize /*allStrips[i]->m_faces.size()*/ % threshold; int degenerateCount = 0; int j = 0; for(; j < numTimes; j++) { currentStrip = new NvStripInfo(startInfo, 0, -1); int faceCtr = j*threshold + degenerateCount; bool bFirstTime = true; while(faceCtr < threshold+(j*threshold)+degenerateCount) { if(IsDegenerate(allStrips[i]->m_faces[faceCtr])) { degenerateCount++; //last time or first time through, no need for a degenerate if( (((faceCtr + 1) != threshold+(j*threshold)+degenerateCount) || ((j == numTimes - 1) && (numLeftover < 4) && (numLeftover > 0))) && !bFirstTime) { currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr++]); } else { //but, we do need to delete the degenerate, if it's marked fake, to avoid leaking if(allStrips[i]->m_faces[faceCtr]->m_bIsFake) { delete allStrips[i]->m_faces[faceCtr], allStrips[i]->m_faces[faceCtr] = NULL; } ++faceCtr; } } else { currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr++]); bFirstTime = false; } } /* for(int faceCtr = j*threshold; faceCtr < threshold+(j*threshold); faceCtr++) { currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr]); } */ if(j == numTimes - 1) //last time through { if( (numLeftover < 4) && (numLeftover > 0) ) //way too small { //just add to last strip int ctr = 0; while(ctr < numLeftover) { IsDegenerate( allStrips[i]->m_faces[faceCtr] ) ? ++degenerateCount : ++ctr; currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr++]); } numLeftover = 0; } } tempStrips.push_back(currentStrip); } int leftOff = j * threshold + degenerateCount; if(numLeftover != 0) { currentStrip = new NvStripInfo(startInfo, 0, -1); int ctr = 0; bool bFirstTime = true; while(ctr < numLeftover) { if( !IsDegenerate(allStrips[i]->m_faces[leftOff]) ) { ctr++; bFirstTime = false; currentStrip->m_faces.push_back(allStrips[i]->m_faces[leftOff++]); } else if(!bFirstTime) currentStrip->m_faces.push_back(allStrips[i]->m_faces[leftOff++]); else { //don't leak if(allStrips[i]->m_faces[leftOff]->m_bIsFake) { delete allStrips[i]->m_faces[leftOff], allStrips[i]->m_faces[leftOff] = NULL; } leftOff++; } } /* for(size_t k = 0; k < numLeftover; k++) { currentStrip->m_faces.push_back(allStrips[i]->m_faces[leftOff++]); } */ tempStrips.push_back(currentStrip); } } else { //we're not just doing a tempStrips.push_back(allBigStrips[i]) because // this way we can delete allBigStrips later to free the memory currentStrip = new NvStripInfo(startInfo, 0, -1); for(size_t j = 0; j < allStrips[i]->m_faces.size(); j++) currentStrip->m_faces.push_back(allStrips[i]->m_faces[j]); tempStrips.push_back(currentStrip); } } //add small strips to face list NvStripInfoVec tempStrips2; RemoveSmallStrips(tempStrips, tempStrips2, outFaceList); outStrips.clear(); //screw optimization for now // for(size_t i = 0; i < tempStrips.size(); ++i) // outStrips.push_back(tempStrips[i]); if(tempStrips2.size() != 0) { //Optimize for the vertex cache VertexCache* vcache = new VertexCache(cacheSize); float bestNumHits = -1.0f; float numHits; int bestIndex; bool done = false; int firstIndex = 0; float minCost = 10000.0f; for(size_t i = 0; i < tempStrips2.size(); i++) { int numNeighbors = 0; //find strip with least number of neighbors per face for(size_t j = 0; j < tempStrips2[i]->m_faces.size(); j++) { numNeighbors += NumNeighbors(tempStrips2[i]->m_faces[j], edgeInfos); } float currCost = (float)numNeighbors / (float)tempStrips2[i]->m_faces.size(); if(currCost < minCost) { minCost = currCost; firstIndex = i; } } UpdateCacheStrip(vcache, tempStrips2[firstIndex]); outStrips.push_back(tempStrips2[firstIndex]); tempStrips2[firstIndex]->visited = true; bool bWantsCW = (tempStrips2[firstIndex]->m_faces.size() % 2) == 0; //this n^2 algo is what slows down stripification so much.... // needs to be improved while(1) { bestNumHits = -1.0f; //find best strip to add next, given the current cache for(size_t i = 0; i < tempStrips2.size(); i++) { if(tempStrips2[i]->visited) continue; numHits = CalcNumHitsStrip(vcache, tempStrips2[i]); if(numHits > bestNumHits) { bestNumHits = numHits; bestIndex = i; } else if(numHits >= bestNumHits) { //check previous strip to see if this one requires it to switch polarity NvStripInfo *strip = tempStrips2[i]; int nStripFaceCount = strip->m_faces.size(); NvFaceInfo tFirstFace(strip->m_faces[0]->m_v0, strip->m_faces[0]->m_v1, strip->m_faces[0]->m_v2); // If there is a second face, reorder vertices such that the // unique vertex is first if (nStripFaceCount > 1) { int nUnique = NvStripifier::GetUniqueVertexInB(strip->m_faces[1], &tFirstFace); if (nUnique == tFirstFace.m_v1) { SWAP(tFirstFace.m_v0, tFirstFace.m_v1); } else if (nUnique == tFirstFace.m_v2) { SWAP(tFirstFace.m_v0, tFirstFace.m_v2); } // If there is a third face, reorder vertices such that the // shared vertex is last if (nStripFaceCount > 2) { int nShared0, nShared1; GetSharedVertices(strip->m_faces[2], &tFirstFace, &nShared0, &nShared1); if ( (nShared0 == tFirstFace.m_v1) && (nShared1 == -1) ) { SWAP(tFirstFace.m_v1, tFirstFace.m_v2); } } } // Check CW/CCW ordering if (bWantsCW == IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1)) { //I like this one! bestIndex = i; } } } if(bestNumHits == -1.0f) break; tempStrips2[bestIndex]->visited = true; UpdateCacheStrip(vcache, tempStrips2[bestIndex]); outStrips.push_back(tempStrips2[bestIndex]); bWantsCW = (tempStrips2[bestIndex]->m_faces.size() % 2 == 0) ? bWantsCW : !bWantsCW; } delete vcache; } }
bool Triangle::IsDegenerate(float /*epsilon*/) const { return IsDegenerate(a, b, c); }
bool Triangle::IsDegenerate(float epsilon) const { return IsDegenerate(a, b, c, epsilon); }