/* =========== CSGBrush =========== */ void CSGBrush (int brushnum) { int hull; brush_t *b1, *b2; brushhull_t *bh1, *bh2; int bn; qboolean overwrite; int i; bface_t *f, *f2, *next, *fcopy; bface_t *outside, *oldoutside; entity_t *e; vec_t area; SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_ABOVE_NORMAL); b1 = &mapbrushes[brushnum]; e = &entities[b1->entitynum]; for (hull = 0 ; hull<NUM_HULLS ; hull++) { bh1 = &b1->hulls[hull]; // set outside to a copy of the brush's faces outside = CopyFacesToOutside (bh1); overwrite = false; for (bn=0 ; bn<e->numbrushes ; bn++) { // see if b2 needs to clip a chunk out of b1 if (bn==brushnum) { overwrite = true; // later brushes now overwrite continue; } b2 = &mapbrushes[e->firstbrush + bn]; bh2 = &b2->hulls[hull]; if (!bh2->faces) continue; // brush isn't in this hull // check brush bounding box first for (i=0 ; i<3 ; i++) if (bh1->mins[i] > bh2->maxs[i] || bh1->maxs[i] < bh2->mins[i]) break; if (i<3) continue; // divide faces by the planes of the b2 to find which // fragments are inside f = outside; outside = NULL; for ( ; f ; f=next) { next = f->next; // check face bounding box first for (i=0 ; i<3 ; i++) if (bh2->mins[i] > f->maxs[i] || bh2->maxs[i] < f->mins[i]) break; if (i<3) { // this face doesn't intersect brush2's bbox f->next = outside; outside = f; continue; } oldoutside = outside; fcopy = CopyFace (f); // save to avoid fake splits // throw pieces on the front sides of the planes // into the outside list, return the remains on the inside for (f2=bh2->faces ; f2 && f ; f2=f2->next) f = ClipFace (b1, f, &outside, f2->planenum, overwrite); area = f ? WindingArea (f->w) : 0; if (f && area < 1.0) { qprintf ("Entity %i, Brush %i: tiny penetration\n" , b1->entitynum, b1->brushnum); c_tiny_clip++; FreeFace (f); f = NULL; } if (f) { // there is one convex fragment of the original // face left inside brush2 FreeFace (fcopy); if (b1->contents > b2->contents) { // inside a water brush f->contents = b2->contents; f->next = outside; outside = f; } else // inside a solid brush FreeFace (f); // throw it away } else { // the entire thing was on the outside, even // though the bounding boxes intersected, // which will never happen with axial planes // free the fragments chopped to the outside while (outside != oldoutside) { f2 = outside->next; FreeFace (outside); outside = f2; } // revert to the original face to avoid // unneeded false cuts fcopy->next = outside; outside = fcopy; } } } // all of the faces left in outside are real surface faces SaveOutside (b1, hull, outside, b1->contents); } }
int Hull::AddContactsHullHull(Separation& sep, const Point3* pVertsA, const Point3* pVertsB, const Transform& trA, const Transform& trB,const Hull& hullA,const Hull& hullB, HullContactCollector* hullContactCollector) { const int maxContacts = hullContactCollector->GetMaxNumContacts(); Vector3 normalWorld = sep.m_axis; // edge->edge contact is always a single point if (sep.m_separator == Separation::kFeatureBoth) { const Hull::Edge& edgeA = hullA.GetEdge(sep.m_featureA); const Hull::Edge& edgeB = hullB.GetEdge(sep.m_featureB); float ta, tb; Line la(pVertsA[edgeA.m_verts[0]], pVertsA[edgeA.m_verts[1]]); Line lb(pVertsB[edgeB.m_verts[0]], pVertsB[edgeB.m_verts[1]]); Intersect(la, lb, ta, tb); #ifdef VALIDATE_CONTACT_POINTS AssertPointInsideHull(contact.m_points[0].m_pos, trA, hullA); AssertPointInsideHull(contact.m_points[0].m_pos, trB, hullB); #endif Point3 posWorld = Lerp(la.m_start, la.m_end, ta); float depth = -sep.m_dist; Vector3 tangent = Normalize(pVertsA[edgeA.m_verts[1]] - pVertsA[edgeA.m_verts[0]]); sep.m_contact = hullContactCollector->BatchAddContactGroup(sep,1,normalWorld,tangent,&posWorld,&depth); } // face->face contact is polygon else { short faceA = sep.m_featureA; short faceB = sep.m_featureB; Vector3 tangent; // find face of hull A that is most opposite contact axis // TODO: avoid having to transform planes here if (sep.m_separator == Separation::kFeatureB) { const Hull::Edge& edgeB = hullB.GetEdge(hullB.GetFaceFirstEdge(faceB)); tangent = Normalize(pVertsB[edgeB.m_verts[1]] - pVertsB[edgeB.m_verts[0]]); Scalar dmin = Scalar::Consts::MaxValue; for (short face = 0; face < hullA.m_numFaces; face++) { Vector3 normal = hullA.GetPlane(face).GetNormal() * trA; Scalar d = Dot(normal, sep.m_axis); if (d < dmin) { dmin = d; faceA = face; } } } else { const Hull::Edge& edgeA = hullA.GetEdge(hullA.GetFaceFirstEdge(faceA)); tangent = Normalize(pVertsA[edgeA.m_verts[1]] - pVertsA[edgeA.m_verts[0]]); Scalar dmin = Scalar::Consts::MaxValue; for (short face = 0; face < hullB.m_numFaces; face++) { Vector3 normal = hullB.GetPlane(face).GetNormal() * trB; Scalar d = Dot(normal, -sep.m_axis); if (d < dmin) { dmin = d; faceB = face; } } } Point3 workspace[2][Hull::kMaxVerts]; // setup initial clip face (minimizing face from hull B) int numContacts = 0; for (short edge = hullB.GetFaceFirstEdge(faceB); edge != -1; edge = hullB.GetFaceNextEdge(faceB, edge)) workspace[0][numContacts++] = pVertsB[ hullB.GetEdgeVertex0(faceB, edge) ]; // clip polygon to back of planes of all faces of hull A that are adjacent to witness face Point3* pVtxIn = workspace[0]; Point3* pVtxOut = workspace[1]; #if 0 for (short edge = hullA.GetFaceFirstEdge(faceA); edge != -1; edge = hullA.GetFaceNextEdge(faceA, edge)) { Plane planeA = hullA.GetPlane( hullA.GetEdgeOtherFace(edge, faceA) ) * trA; numContacts = ClipFace(numContacts, &pVtxIn, &pVtxOut, planeA); } #else for (short f = 0; f < hullA.GetNumFaces(); f++) { Plane planeA = hullA.GetPlane(f) * trA; numContacts = ClipFace(numContacts, &pVtxIn, &pVtxOut, planeA); } #endif // only keep points that are behind the witness face Plane planeA = hullA.GetPlane(faceA) * trA; float depths[Hull::kMaxVerts]; int numPoints = 0; for (int i = 0; i < numContacts; i++) { Scalar d = Dot(planeA, pVtxIn[i]); if (IsNegative(d)) { depths[numPoints] = (float)-d; pVtxIn[numPoints] = pVtxIn[i]; #ifdef VALIDATE_CONTACT_POINTS AssertPointInsideHull(pVtxIn[numPoints], trA, hullA); AssertPointInsideHull(pVtxIn[numPoints], trB, hullB); #endif numPoints++; } } //we can also use a persistentManifold/reducer class // keep maxContacts points at most if (numPoints > 0) { if (numPoints > maxContacts) { int step = (numPoints << 8) / maxContacts; numPoints = maxContacts; for (int i = 0; i < numPoints; i++) { int nth = (step * i) >> 8; depths[i] = depths[nth]; pVtxIn[i] = pVtxIn[nth]; #ifdef VALIDATE_CONTACT_POINTS AssertPointInsideHull(contact.m_points[i].m_pos, trA, hullA); AssertPointInsideHull(contact.m_points[i].m_pos, trB, hullB); #endif } } sep.m_contact = hullContactCollector->BatchAddContactGroup(sep,numPoints,normalWorld,tangent,pVtxIn,depths); } return numPoints; }
bool VertexCoordRemapper::GetNextFaceCoordinates(Coords *result) { // DEBUG_DEBUG("mesh update get face coords"); result->tex_c = tex_coords; result->vertex_c = s_vertex_coords; // if we have some faces left over from a previous clipping operation, give // one of those first: if (GiveClipFaceResult(result)) return true; // when 180 degree bounds correction is working, we'll have two nodes. if (done_node) { do { // this will search the tree for the next leaf node. tree_node_id = tree.GetNext(); if (!tree_node_id) { // we've reached last one return false; } } // some of the verticies may have arrived from undefined transformations // if this is one, skip it and try to find another. while ((tree.nodes[tree_node_id].flags & (transform_fail_flag * 15))); // find the coordinates from the tree node result->vertex_c = tree.nodes[tree_node_id].verts; // check that the transformation is properly defined. tree.GetInputCoordinates(tree_node_id, tex_coords); // if the node has a discontinuity, we want to split it into two // faces and return each one on consecutive calls. discontinuity_flags = (tree.nodes[tree_node_id].flags / vertex_side_flag_start) % 16; if (discontinuity_flags) { done_node = false; // flip the marked nodes to the other side. copy the coordinates 1st result->vertex_c = s_vertex_coords; for (short unsigned int x = 0; x < 2; x++) { for (short unsigned int y = 0; y < 2; y++) { s_vertex_coords[x][y][0] = tree.nodes[tree_node_id].verts[x][y][0]; s_vertex_coords[x][y][1] = tree.nodes[tree_node_id].verts[x][y][1]; if (discontinuity_flags & (1 << (x*2 + y))) { DiscontinuityFlip(s_vertex_coords[x][y]); } } } } } else { // we flip the other vertices to the ones we did last time. done_node = true; for (short unsigned int x = 0; x < 2; x++) { for (short unsigned int y = 0; y < 2; y++) { s_vertex_coords[x][y][0] = tree.nodes[tree_node_id].verts[x][y][0]; s_vertex_coords[x][y][1] = tree.nodes[tree_node_id].verts[x][y][1]; if (!(discontinuity_flags & (1 << (x*2 + y)))) { DiscontinuityFlip(s_vertex_coords[x][y]); } } } } // if we are doing circular cropping, clip the face so it makes the shape if (circle_crop) { // If all points are within the radius, then don't clip // HuginBase::SrcPanoImage *src_img = visualization_state->GetSrcImage(image_number); if ( image->isInside(vigra::Point2D(int(result->tex_c[0][0][0] * width), int(result->tex_c[0][0][1] * height))) && image->isInside(vigra::Point2D(int(result->tex_c[0][1][0] * width), int(result->tex_c[0][1][1] * height))) && image->isInside(vigra::Point2D(int(result->tex_c[1][0][0] * width), int(result->tex_c[1][0][1] * height))) && image->isInside(vigra::Point2D(int(result->tex_c[1][1][0] * width), int(result->tex_c[1][1][1] * height)))) { // all inside, doesn't need clipping. return true; } // we do need to clip: ClipFace(result); // if there was anything left, return the first face and leave the rest // for later. if (GiveClipFaceResult(result)) return true; // we clipped to nothing... try and get another face: from the top... return (GetNextFaceCoordinates(result)); } return true; }