⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 bsppartition.c

📁 [Game.Programming].Academic - Graphics Gems (6 books source code)
💻 C
字号:
/* partition.c: module to partition 3D convex face with an arbitrary plane. * Copyright (c) Norman Chin */ #include "bsp.h"#define PREPEND_FACE(f,fl) (f->fnext= fl, fl= f)#define ISVERTEX_EQ(v1,v2) \                          (IS_EQ((v1)->xx,(v2)->xx) && \                           IS_EQ((v1)->yy,(v2)->yy) && \                           IS_EQ((v1)->zz,(v2)->zz))/* local functions */static VERTEX *findNextIntersection(VERTEX *vstart, const PLANE *plane,				    float *ixx, float *iyy, float *izz,				    SIGN *sign);static FACE *createOtherFace(FACE *face, VERTEX *v1, float ixx1, 			     float iyy1, float izz1, VERTEX *v2,			     float ixx2, float iyy2, float izz2			     );static SIGN whichSideIsFaceWRTplane(FACE *face, const PLANE *plane); /* Partitions a 3D convex polygon (face) with an arbitrary plane into its  * negative and positive fragments, if any, w.r.t. the partitioning plane. * Note that faceList is unusable afterwards since its vertex list has been * parceled out to the other faces. It's set to null to avoid dangling * pointer problem. Faces embedded in the plane are separated into two lists, * one facing the same direction as the partitioning plane, faceSameDir, and  * the other facing the opposite direction, faceOppDir. */void BSPpartitionFaceListWithPlane(const PLANE *plane,FACE **faceList,				   FACE **faceNeg, FACE **facePos,				   FACE **faceSameDir, FACE **faceOppDir){FACE *ftrav= *faceList;*faceSameDir= *faceOppDir = *faceNeg= *facePos= NULL_FACE;while (ftrav != NULL_FACE) {   VERTEX *v1, *v2; FACE *nextFtrav;   float ixx1,iyy1,izz1, ixx2,iyy2,izz2;   SIGN signV1, signV2;   nextFtrav= ftrav->fnext;	/* unchain current face from list */   ftrav->fnext= NULL_FACE;   /* find first intersection */   v1= findNextIntersection(ftrav->vhead,plane,&ixx1,&iyy1,&izz1,&signV1);   if (v1 != NULL_VERTEX) {      assert(signV1 != ZERO);      /* first one found, find the 2nd one, if any */      v2= findNextIntersection(v1->vnext,plane,&ixx2,&iyy2,&izz2,&signV2);      /* Due to numerical instabilities, both intersection points may       * have the same sign such as in the case when splitting very close       * to a vertex. This should not count as a split.       */      if (v2 != NULL_VERTEX && signV1 == signV2) v2= NULL_VERTEX;    }   else v2= NULL_VERTEX;	/* No first intersection found,                                 * therefore no second intersection.				 */                                   /* an intersection? */   if (v1 != NULL_VERTEX && v2 != NULL_VERTEX) { /* yes, intersection */      FACE *newOtherFace;      /* ftrav's vertex list will be modified */      newOtherFace= createOtherFace(ftrav,v1,ixx1,iyy1,izz1,				    v2,ixx2,iyy2,izz2				    );      /* return split faces on appropriate lists */      if (signV1 == NEGATIVE) { 	 PREPEND_FACE(ftrav,*faceNeg);	 PREPEND_FACE(newOtherFace,*facePos);      }      else {	 assert(signV1 == POSITIVE);	 PREPEND_FACE(newOtherFace,*faceNeg);	 PREPEND_FACE(ftrav,*facePos);      }				       }   else {			/* no intersection  */      SIGN side;      /* Face is embedded or wholly to one side of partitioning plane. */      side= whichSideIsFaceWRTplane(ftrav,plane);      if (side == NEGATIVE) 	 PREPEND_FACE(ftrav,*faceNeg);      else if (side == POSITIVE) 	 PREPEND_FACE(ftrav,*facePos);      else {	 assert(side == ZERO);	 if (IS_EQ(ftrav->plane.aa,plane->aa) && 	     IS_EQ(ftrav->plane.bb,plane->bb) &&	     IS_EQ(ftrav->plane.cc,plane->cc))             PREPEND_FACE(ftrav,*faceSameDir);	 else PREPEND_FACE(ftrav,*faceOppDir);      }   }   ftrav= nextFtrav;		/* get next */} /* while ftrav != NULL_FACE */   /* faceList's vertex list has been parceled out to other lists so    * set this to null.    */   *faceList= NULL_FACE;		#if 0 /* only true for BSPconstructTree() */   /* there's at least one face embedded in the partitioning plane */   assert(*faceSameDir != NULL_FACE); #endif} /* BSPpartitionFaceListWithPlane() */			    /* Finds next intersection on or after vstart.  *  * If an intersection is found,  *    a pointer to first vertex of the edge is returned,  *    the intersection point (ixx,iyy,izz) and its sign is updated.  * Otherwise a null pointer is returned. */static VERTEX *findNextIntersection(VERTEX *vstart,const PLANE *plane,				    float *ixx,float *iyy,float *izz,				    SIGN *sign){   VERTEX *vtrav;   /* for all edges starting from vstart ... */   for (vtrav= vstart; vtrav->vnext != NULL_VERTEX; vtrav= vtrav->vnext) {      if ((*sign= anyEdgeIntersectWithPlane(vtrav->xx,vtrav->yy,vtrav->zz,					    vtrav->vnext->xx,vtrav->vnext->yy,					    vtrav->vnext->zz,plane,					    ixx,iyy,izz))) {	 return(vtrav);      }   }   return(NULL_VERTEX);} /* findNextIntersection() *//* Memory allocated for split face's vertices and pointers tediously updated. * * face - face to be split * v1   - 1st vertex of edge of where 1st intersection was found  * (ixx1,iyy1,izz1) - 1st intersection * v2   - 1st vertex of edge of where 2nd intersection was found  * (ixx2,iyy2,izz2) - 2nd intersection */static FACE *createOtherFace(FACE *face, 			     VERTEX *v1, float ixx1, float iyy1, float izz1,			     VERTEX *v2, float ixx2, float iyy2, float izz2			     ){   VERTEX *i1p1, *i2p1;		/* new vertices for original face  */   VERTEX *i1p2, *i2p2;		/* new vertices for new face */   VERTEX *p2end;		/* new vertex for end of new face */   VERTEX *vtemp;		/* place holders */   register VERTEX *beforeV2;	/* place holders */   FACE *newFace;		/* newly allocated face */   /* new intersection vertices */   i1p1= allocVertex(ixx1,iyy1,izz1);    i2p1= allocVertex(ixx2,iyy2,izz2);   i1p2= allocVertex(ixx1,iyy1,izz1);   i2p2= allocVertex(ixx2,iyy2,izz2);    /* duplicate 1st vertex of 2nd list to close it up */   p2end= allocVertex(v2->xx,v2->yy,v2->zz);   vtemp= v1->vnext;   /* merge both intersection vertices i1p1 & i2p1 into 1st list */   if (ISVERTEX_EQ(i2p1,v2->vnext)) { /* intersection vertex coincident? */      assert(i2p1->vnext == NULL_VERTEX);      freeVertexList(&i2p1);	/* yes, so free it */      i1p1->vnext= v2->vnext;   }   else {      i2p1->vnext= v2->vnext;	/* attach intersection list onto 1st list */      i1p1->vnext= i2p1;	/* attach both intersection vertices */   }   v1->vnext= i1p1; /* attach front of 1st list to intersection vertices */   /* merge intersection vertices i1p2, i2p2 & p2end into second list */   i2p2->vnext= i1p2;		/* attach both intersection vertices */   v2->vnext= i2p2;		/* attach 2nd list to interection vertices */   if (vtemp == v2) {      i1p2->vnext= p2end;	/* close up 2nd list */   }   else {      if (ISVERTEX_EQ(i1p2,vtemp)) { /* intersection vertex coincident? */	 assert(i1p2->vnext == NULL_VERTEX);	 freeVertexList(&i1p2);	/* yes, so free it */	 i2p2->vnext= vtemp;	/* attach intersection vertex to 2nd list */      }      else {	 i1p2->vnext= vtemp;	/* attach intersection list to 2nd list */      }      /* find previous vertex to v2 */      for (beforeV2= vtemp; beforeV2->vnext != v2; beforeV2= beforeV2->vnext)	 ;			/* lone semi-colon */      beforeV2->vnext= p2end;	/* and attach it to complete the 2nd list */   }   /* copy original face info but with new vertex list */   newFace= allocFace(&face->color,v2,&face->plane);   return(newFace);} /* createOtherFace() *//* Determines which side a face is with respect to a plane. * * However, due to numerical problems, when a face is very close to the plane, * some vertices may be misclassified.  * There are several solutions, two of which are mentioned here: *    1- classify the one vertex furthest away from the plane, (note that *       one need not compute the actual distance) and use that side. *    2- count how many vertices lie on either side and pick the side *       with the maximum. (this is the one implemented). */static SIGN whichSideIsFaceWRTplane(FACE *face, const PLANE *plane){   register VERTEX *vtrav;   float value;   boolean isNeg, isPos;   isNeg= isPos= FALSE;      for (vtrav= face->vhead; vtrav->vnext != NULL_VERTEX; vtrav= vtrav->vnext){      value= (plane->aa*vtrav->xx) + (plane->bb*vtrav->yy) + 	     (plane->cc*vtrav->zz) + plane->dd;      if (value < -TOLER) 	 isNeg= TRUE;      else if (value > TOLER)	 isPos= TRUE;      else assert(IS_EQ(value,0.0));   } /* for vtrav */    /* in the very rare case that some vertices slipped thru to other side of    * plane due to round-off errors, execute the above again but count the     * vertices on each side instead and pick the maximum.    */   if (isNeg && isPos) {	/* yes so handle this numerical problem */      int countNeg, countPos;            /* count how many vertices are on either side */      countNeg= countPos= 0;      for (vtrav= face->vhead; vtrav->vnext != NULL_VERTEX; 	   vtrav= vtrav->vnext) {	 value= (plane->aa*vtrav->xx) + (plane->bb*vtrav->yy) + 	        (plane->cc*vtrav->zz) + plane->dd;	 if (value < -TOLER)	    countNeg++;	 else if (value > TOLER)	    countPos++;	 else assert(IS_EQ(value,0.0));      } /* for */      /* return the side corresponding to the maximum */      if (countNeg > countPos) return(NEGATIVE);      else if (countPos > countNeg) return(POSITIVE);      else return(ZERO);   }   else {			/* this is the usual case */      if (isNeg) return(NEGATIVE);      else if (isPos) return(POSITIVE);      else return(ZERO);   }} /* whichSideIsFaceWRTplane() *//* Determines if an edge bounded by (x1,y1,z1)->(x2,y2,z2) intersects * the plane. *  * If there's an intersection,  *    the sign of (x1,y1,z1), NEGATIVE or POSITIVE, w.r.t. the plane is *    returned with the intersection (ixx,iyy,izz) updated. * Otherwise ZERO is returned.     */SIGN anyEdgeIntersectWithPlane(float x1,float y1,float z1,			       float x2,float y2,float z2,			       const PLANE *plane,			       float *ixx, float *iyy, float *izz){   float temp1, temp2;   int sign1, sign2;		/* must be int since gonna do a bitwise ^ */   float aa= plane->aa; float bb= plane->bb; float cc= plane->cc;   float dd= plane->dd;   /* get signs */   temp1= (aa*x1) + (bb*y1) + (cc*z1) + dd;   if (temp1 < -TOLER)      sign1= -1;   else if (temp1 > TOLER)      sign1= 1;   else {      /* edges beginning with a 0 sign are not considered valid intersections       * case 1 & 6 & 7, see Gems III.       */      assert(IS_EQ(temp1,0.0));      return(ZERO);   }   temp2= (aa*x2) + (bb*y2) + (cc*z2) + dd;   if (temp2 < -TOLER)      sign2= -1;   else if (temp2 > TOLER)      sign2= 1;   else {			/* case 8 & 9, see Gems III */      assert(IS_EQ(temp2,0.0));      *ixx= x2;      *iyy= y2;      *izz= z2;      return( (sign1 == -1) ? NEGATIVE : POSITIVE);   }   /* signs different?     * recall: -1^1 == 1^-1 ==> 1    case 4 & 5, see Gems III    *         -1^-1 == 1^1 ==> 0    case 2 & 3, see Gems III    */   if (sign1 ^ sign2) {      float dx,dy,dz;      float denom, tt;      /* compute intersection point */      dx= x2-x1;      dy= y2-y1;      dz= z2-z1;      denom= (aa*dx) + (bb*dy) + (cc*dz);      tt= - ((aa*x1) + (bb*y1) + (cc*z1) + dd) / denom;      *ixx= x1 + (tt * dx);      *iyy= y1 + (tt * dy);      *izz= z1 + (tt * dz);      assert(sign1 == 1 || sign1 == -1);      return( (sign1 == -1) ? NEGATIVE : POSITIVE );   }   else return(ZERO);} /* anyEdgeIntersectWithPlane() *//*** bspPartition.c ***/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -