//---------------------------------------------------------------------------- bool ConvexPolyhedron::ComputeSilhouette (vector<Vector3>& rkTerminator, const Vector3& rkEye, const Plane& rkPlane, const Vector3& rkU, const Vector3& rkV, vector<Vector2>& rkSilhouette) { Real fEDist = rkPlane.DistanceTo(rkEye); // assert: fEDist > 0 // closest planar point to E is K = E-dist*N Vector3 kClosest = rkEye - fEDist*rkPlane.Normal(); // project polyhedron points onto plane for (int i = 0; i < (int)rkTerminator.size(); i++) { Vector3& rkPoint = rkTerminator[i]; Real fVDist = rkPlane.DistanceTo(rkPoint); if ( fVDist >= fEDist ) { // cannot project vertex onto plane return false; } // compute projected point Q Real fRatio = fEDist/(fEDist-fVDist); Vector3 kProjected = rkEye + fRatio*(rkPoint - rkEye); // compute (x,y) so that Q = K+x*U+y*V+z*N Vector3 kDiff = kProjected - kClosest; rkSilhouette.push_back(Vector2(rkU.Dot(kDiff),rkV.Dot(kDiff))); } return true; }
// Works for either side of a plane; this wasn't always the case bool Segment::Intersects( const Plane& p, CollisionInfo* const pInfo /*=NULL*/ ) const { Vector ab = m_Point2 - m_Point1; float d = ab.Dot( p.m_Normal ); if( Abs( d ) > EPSILON ) // Is segment not parallel to the plane? { // Test the t-value where the line crosses the plane to see if the // segment intersects or lies completely on one side (accounting // for the possibility that the segment faces the other way now). float t = -p.DistanceTo( m_Point1 ) / d; if( t >= 0.0f && t <= 1.0f ) { if( pInfo ) { pInfo->m_Collision = true; pInfo->m_Intersection = m_Point1 + t * ab; pInfo->m_Plane = p; pInfo->m_HitT = t; } return true; } } return false; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int MLRIndexedPolyMesh::FindBackFace(const Point3D& u) { Check_Object(this); int i, numPrimitives = GetNumPrimitives(); int ret = 0, len = lengths.GetLength(); unsigned char *iPtr; Plane *p; if(len <= 0) { visible = 0; return 0; } p = &facePlanes[0]; iPtr = &testList[0]; if(state.GetBackFaceMode() == MLRState::BackFaceOffMode) { ResetTestList(); ret = 1; } else { for(i=0;i<numPrimitives;i++,p++,iPtr++) { // Scalar s = p->DistanceTo(u); // *iPtr = !Get_Sign_Bit(s); *iPtr = (p->DistanceTo(u) >= 0.0f) ? (unsigned char)1: (unsigned char)0; ret += *iPtr; } visible = ret ? (unsigned char)1 : (unsigned char)0; } visible = ret ? (unsigned char)1 : (unsigned char)0; FindVisibleVertices(); return ret; }
//---------------------------------------------------------------------------- static void SplitAndDecompose (Tetrahedron kTetra, const Plane& rkPlane, vector<Tetrahedron>& rkInside) { // determine on which side of the plane the points of the tetrahedron lie Real afC[4]; int i, aiP[4], aiN[4], aiZ[4]; int iPositive = 0, iNegative = 0, iZero = 0; for (i = 0; i < 4; i++) { afC[i] = rkPlane.DistanceTo(kTetra[i]); if ( afC[i] > 0.0f ) aiP[iPositive++] = i; else if ( afC[i] < 0.0f ) aiN[iNegative++] = i; else aiZ[iZero++] = i; } // For a split to occur, one of the c_i must be positive and one must // be negative. if ( iNegative == 0 ) { // tetrahedron is completely on the positive side of plane, full clip return; } if ( iPositive == 0 ) { // tetrahedron is completely on the negative side of plane rkInside.push_back(kTetra); return; } // Tetrahedron is split by plane. Determine how it is split and how to // decompose the negative-side portion into tetrahedra (6 cases). Real fW0, fW1, fInvCDiff; Vector3 akIntp[4]; if ( iPositive == 3 ) { // +++- for (i = 0; i < iPositive; i++) { fInvCDiff = 1.0f/(afC[aiP[i]] - afC[aiN[0]]); fW0 = -afC[aiN[0]]*fInvCDiff; fW1 = +afC[aiP[i]]*fInvCDiff; kTetra[aiP[i]] = fW0*kTetra[aiP[i]] + fW1*kTetra[aiN[0]]; } rkInside.push_back(kTetra); } else if ( iPositive == 2 ) { if ( iNegative == 2 ) { // ++-- for (i = 0; i < iPositive; i++) { fInvCDiff = 1.0f/(afC[aiP[i]]-afC[aiN[0]]); fW0 = -afC[aiN[0]]*fInvCDiff; fW1 = +afC[aiP[i]]*fInvCDiff; akIntp[i] = fW0*kTetra[aiP[i]] + fW1*kTetra[aiN[0]]; } for (i = 0; i < iNegative; i++) { fInvCDiff = 1.0f/(afC[aiP[i]]-afC[aiN[1]]); fW0 = -afC[aiN[1]]*fInvCDiff; fW1 = +afC[aiP[i]]*fInvCDiff; akIntp[i+2] = fW0*kTetra[aiP[i]] + fW1*kTetra[aiN[1]]; } kTetra[aiP[0]] = akIntp[2]; kTetra[aiP[1]] = akIntp[1]; rkInside.push_back(kTetra); rkInside.push_back(Tetrahedron(kTetra[aiN[1]],akIntp[3],akIntp[2], akIntp[1])); rkInside.push_back(Tetrahedron(kTetra[aiN[0]],akIntp[0],akIntp[1], akIntp[2])); } else { // ++-0 for (i = 0; i < iPositive; i++) { fInvCDiff = 1.0f/(afC[aiP[i]]-afC[aiN[0]]); fW0 = -afC[aiN[0]]*fInvCDiff; fW1 = +afC[aiP[i]]*fInvCDiff; kTetra[aiP[i]] = fW0*kTetra[aiP[i]] + fW1*kTetra[aiN[0]]; } rkInside.push_back(kTetra); } } else if ( iPositive == 1 ) { if ( iNegative == 3 ) { // +--- for (i = 0; i < iNegative; i++) { fInvCDiff = 1.0f/(afC[aiP[0]]-afC[aiN[i]]); fW0 = -afC[aiN[i]]*fInvCDiff; fW1 = +afC[aiP[0]]*fInvCDiff; akIntp[i] = fW0*kTetra[aiP[0]] + fW1*kTetra[aiN[i]]; } kTetra[aiP[0]] = akIntp[0]; rkInside.push_back(kTetra); rkInside.push_back(Tetrahedron(akIntp[0],kTetra[aiN[1]], kTetra[aiN[2]],akIntp[1])); rkInside.push_back(Tetrahedron(kTetra[aiN[2]],akIntp[1],akIntp[2], akIntp[0])); } else if ( iNegative == 2 ) { // +--0 for (i = 0; i < iNegative; i++) { fInvCDiff = 1.0f/(afC[aiP[0]]-afC[aiN[i]]); fW0 = -afC[aiN[i]]*fInvCDiff; fW1 = +afC[aiP[0]]*fInvCDiff; akIntp[i] = fW0*kTetra[aiP[0]] + fW1*kTetra[aiN[i]]; } kTetra[aiP[0]] = akIntp[0]; rkInside.push_back(kTetra); rkInside.push_back(Tetrahedron(akIntp[1],kTetra[aiZ[0]], kTetra[aiN[1]],akIntp[0])); } else { // +-00 fInvCDiff = 1.0f/(afC[aiP[0]]-afC[aiN[0]]); fW0 = -afC[aiN[0]]*fInvCDiff; fW1 = +afC[aiP[0]]*fInvCDiff; kTetra[aiP[0]] = fW0*kTetra[aiP[0]] + fW1*kTetra[aiN[0]]; rkInside.push_back(kTetra); } } }
//---------------------------------------------------------------------------- int ConvexClipper::Clip (const Plane& rkPlane) { // compute signed distances from vertices to plane int iPositive = 0, iNegative = 0; for (int iV = 0; iV < (int)m_akVertex.size(); iV++) { Vertex& rkV = m_akVertex[iV]; if ( rkV.m_bVisible ) { rkV.m_fDistance = rkPlane.DistanceTo(rkV.m_kPoint); if ( rkV.m_fDistance >= m_fEpsilon ) { iPositive++; } else if ( rkV.m_fDistance <= -m_fEpsilon ) { iNegative++; rkV.m_bVisible = false; } else { // The point is on the plane (within floating point // tolerance). rkV.m_fDistance = 0.0f; } } } if ( iPositive == 0 ) { // mesh is in negative half-space, fully clipped return Plane::NEGATIVE_SIDE; } if ( iNegative == 0 ) { // mesh is in positive half-space, fully visible return Plane::POSITIVE_SIDE; } // clip the visible edges for (int iE = 0; iE < (int)m_akEdge.size(); iE++) { Edge& rkE = m_akEdge[iE]; if ( rkE.m_bVisible ) { int iV0 = rkE.m_aiVertex[0], iV1 = rkE.m_aiVertex[1]; int iF0 = rkE.m_aiFace[0], iF1 = rkE.m_aiFace[1]; Face& rkF0 = m_akFace[iF0]; Face& rkF1 = m_akFace[iF1]; Real fD0 = m_akVertex[iV0].m_fDistance; Real fD1 = m_akVertex[iV1].m_fDistance; if ( fD0 <= 0.0f && fD1 <= 0.0f ) { // The edge is culled. If the edge is exactly on the clip // plane, it is possible that a visible triangle shares it. // The edge will be re-added during the face loop. rkF0.m_akEdge.erase(iE); if ( rkF0.m_akEdge.empty() ) rkF0.m_bVisible = false; rkF1.m_akEdge.erase(iE); if ( rkF1.m_akEdge.empty() ) rkF1.m_bVisible = false; rkE.m_bVisible = false; continue; } if ( fD0 >= 0.0f && fD1 >= 0.0f ) { // face retains the edge continue; } // The edge is split by the plane. Compute the point of // intersection. If the old edge is <V0,V1> and I is the // intersection point, the new edge is <V0,I> when d0 > 0 or // <I,V1> when d1 > 0. int iNV = m_akVertex.size(); m_akVertex.push_back(Vertex()); Vertex& rkNV = m_akVertex[iNV]; Vector3& rkP0 = m_akVertex[iV0].m_kPoint; Vector3& rkP1 = m_akVertex[iV1].m_kPoint; rkNV.m_kPoint = rkP0+(fD0/(fD0-fD1))*(rkP1-rkP0); if ( fD0 > 0.0f ) rkE.m_aiVertex[1] = iNV; else rkE.m_aiVertex[0] = iNV; } } // The mesh straddles the plane. A new convex polygonal face will be // generated. Add it now and insert edges when they are visited. int iNF = m_akFace.size(); m_akFace.push_back(Face()); Face& rkNF = m_akFace[iNF]; rkNF.m_kPlane = rkPlane; // process the faces for (int iF = 0; iF < iNF; iF++) { Face& rkF = m_akFace[iF]; if ( rkF.m_bVisible ) { // Determine if the face is on the negative side, the positive // side, or split by the clipping plane. The m_iOccurs members // are set to zero to help find the end points of the polyline // that results from clipping a face. assert( rkF.m_akEdge.size() >= 2 ); set<int>::iterator pkIter = rkF.m_akEdge.begin(); while ( pkIter != rkF.m_akEdge.end() ) { int iE = *pkIter++; Edge& rkE = m_akEdge[iE]; assert( rkE.m_bVisible ); m_akVertex[rkE.m_aiVertex[0]].m_iOccurs = 0; m_akVertex[rkE.m_aiVertex[1]].m_iOccurs = 0; } int iVStart, iVFinal; if ( GetOpenPolyline(rkF,iVStart,iVFinal) ) { // polyline is open, close it up int iNE = m_akEdge.size(); m_akEdge.push_back(Edge()); Edge& rkNE = m_akEdge[iNE]; rkNE.m_aiVertex[0] = iVStart; rkNE.m_aiVertex[1] = iVFinal; rkNE.m_aiFace[0] = iF; rkNE.m_aiFace[1] = iNF; // add new edge to polygons rkF.m_akEdge.insert(iNE); rkNF.m_akEdge.insert(iNE); } } } // Process face rkNF to make sure it is a simple polygon (theoretically // convex, but numerically may be slightly not convex). Floating point // round-off errors can cause the new face from the last loop to be // needle-like with a collapse of two edges into a single edge. This // block guarantees the invariant "face always a simple polygon". PostProcess(iNF,rkNF); int iESize = rkNF.m_akEdge.size(); if ( iESize < 3 ) { // face is completely degenerate, remove it from mesh m_akFace.pop_back(); } return Plane::NO_SIDE; }