📄 bsptree.c
字号:
/* bspTree.c: module to construct and traverse a BSP tree. * Copyright (c) Norman Chin */#include "bsp.h"/* local functions */static void BSPchoosePlane(FACE *faceList,PLANE *plane);static boolean doesFaceStraddlePlane(const FACE *face,const PLANE *plane);static BSPNODE *allocBspNode(NODE_TYPE kind,FACE *sameDir,FACE *oppDir);static PARTITIONNODE *allocPartitionNode(FACE *sameDir,FACE *oppDir);static void freePartitionNode(PARTITIONNODE **partitionNode);/* Returns a BSP tree of scene from a list of convex faces. * These faces' vertices are oriented in counterclockwise order where the last * vertex is a duplicate of the first, i.e., a square has five vertices. * * faceList - list of faces */BSPNODE *BSPconstructTree(FACE **faceList){ BSPNODE *newBspNode; PLANE plane; FACE *sameDirList,*oppDirList, *faceNegList,*facePosList; /* choose plane to split scene with */ BSPchoosePlane(*faceList,&plane); BSPpartitionFaceListWithPlane(&plane,faceList,&faceNegList,&facePosList, &sameDirList,&oppDirList); assert(*faceList == NULL_FACE); assert(sameDirList != NULL_FACE); /* construct the tree */ newBspNode= allocBspNode(PARTITION_NODE,sameDirList,oppDirList); /* construct tree's "-" branch */ if (faceNegList == NULL_FACE) newBspNode->node->negativeSide= allocBspNode(IN_NODE,NULL_FACE,NULL_FACE); else newBspNode->node->negativeSide= BSPconstructTree(&faceNegList); /* construct tree's "+" branch */ if (facePosList == NULL_FACE) newBspNode->node->positiveSide=allocBspNode(OUT_NODE,NULL_FACE,NULL_FACE); else newBspNode->node->positiveSide= BSPconstructTree(&facePosList); return(newBspNode);} /* BSPconstructTree() *//* Traverses BSP tree to render scene back-to-front based on viewer position. * * bspNode - a node in BSP tree * position - position of viewer */void BSPtraverseTreeAndRender(const BSPNODE *bspNode,const POINT *position){ if (bspNode == NULL_BSPNODE) return; if (bspNode->kind == PARTITION_NODE) { if (BSPisViewerInPositiveSideOfPlane(&bspNode->node->sameDir->plane,position)){ BSPtraverseTreeAndRender(bspNode->node->negativeSide,position); drawFaceList(stdout,bspNode->node->sameDir); drawFaceList(stdout,bspNode->node->oppDir); /* back-face cull */ BSPtraverseTreeAndRender(bspNode->node->positiveSide,position); } else { BSPtraverseTreeAndRender(bspNode->node->positiveSide,position); drawFaceList(stdout,bspNode->node->oppDir); drawFaceList(stdout,bspNode->node->sameDir); /* back-face cull */ BSPtraverseTreeAndRender(bspNode->node->negativeSide,position); } } else assert(bspNode->kind == IN_NODE || bspNode->kind == OUT_NODE);} /* BSPtraverseTreeAndRender() *//* Frees a BSP tree and sets pointer to it to null * * bspNode - a pointer to a node in BSP tree, set to null upon exit */void BSPfreeTree(BSPNODE **bspNode){ if (*bspNode == NULL_BSPNODE) return; if ((*bspNode)->kind == PARTITION_NODE) freePartitionNode(&(*bspNode)->node); MYFREE((char *) *bspNode); *bspNode= NULL_BSPNODE;} /* BSPfreeTree() *//* Chooses plane with which to partition. * The algorithm is to examine the first MAX_CANDIDATES on face list. For * each candidate, count how many splits it would make against the scene. * Then return the one with the minimum amount of splits as the * partitioning plane. * * faceList - list of faces * plane - plane equation returned */static void BSPchoosePlane(FACE *faceList,PLANE *plane){ FACE *rootrav; int ii; int minCount= MAXINT; FACE *chosenRoot= faceList; /* pick first face for now */ assert(faceList != NULL_FACE); /* for all candidates... */#define MAX_CANDIDATES 100 for (rootrav= faceList, ii= 0; rootrav != NULL_FACE && ii< MAX_CANDIDATES; rootrav= rootrav->fnext, ii++) { FACE *ftrav; int count= 0; /* for all faces in scene other than itself... */ for (ftrav= faceList; ftrav != NULL_FACE; ftrav= ftrav->fnext) { if (ftrav != rootrav) if (doesFaceStraddlePlane(ftrav,&rootrav->plane)) count++; } /* remember minimum count and its corresponding face */ if (count < minCount) { minCount= count; chosenRoot= rootrav; } if (count == 0) break; /* can't do better than 0 so return this plane */ } *plane= chosenRoot->plane; /* return partitioning plane */} /* BSPchoosePlane() *//* Returns a boolean to indicate whether the face straddles the plane * * face - face to check * plane - plane */static boolean doesFaceStraddlePlane(const FACE *face, const PLANE *plane){ boolean anyNegative= 0, anyPositive= 0; VERTEX *vtrav; assert(face->vhead != NULL_VERTEX); /* for all vertices... */ for (vtrav= face->vhead; vtrav->vnext !=NULL_VERTEX; vtrav= vtrav->vnext) { float value= plane->aa*vtrav->xx + plane->bb*vtrav->yy + plane->cc*vtrav->zz + plane->dd; /* check which side vertex is on relative to plane */ SIGN sign= FSIGN(value); if (sign == NEGATIVE) anyNegative= 1; else if (sign == POSITIVE) anyPositive= 1; /* if vertices on both sides of plane then face straddles else it no */ if (anyNegative && anyPositive) return(1); } return(0);} /* doesFaceStraddlePlane() *//* Returns a boolean to indicate whether or not point is in + side of plane. * * plane - plane * position - position of point */boolean BSPisViewerInPositiveSideOfPlane(const PLANE *plane,const POINT *position){ float dp= plane->aa*position->xx + plane->bb*position->yy + plane->cc*position->zz + plane->dd; return( (dp > 0.0) ? 1 : 0 );} /* BSPisViewerInPositiveSideOfPlane() *//* Allocates a BSP node. * * kind - type of BSP node * sameDir, oppDir - list of faces to be embedded in this node */static BSPNODE *allocBspNode(NODE_TYPE kind,FACE *sameDir,FACE *oppDir){ BSPNODE *newBspNode; if ((newBspNode= (BSPNODE *) MYMALLOC(sizeof(BSPNODE))) == NULL_BSPNODE) { fprintf(stderr,"?Unable to malloc bspnode.\n"); exit(1); } newBspNode->kind= kind; if (newBspNode->kind == PARTITION_NODE) newBspNode->node= allocPartitionNode(sameDir,oppDir); else { assert(kind == IN_NODE || kind == OUT_NODE); assert(sameDir == NULL_FACE && oppDir == NULL_FACE); newBspNode->node= NULL_PARTITIONNODE; } return(newBspNode);} /* allocBspNode() *//* Allocates a partition node. * * sameDir, oppDir - list of faces embedded in partition node */static PARTITIONNODE *allocPartitionNode(FACE *sameDir,FACE *oppDir){ PARTITIONNODE *newPartitionNode; if ((newPartitionNode= (PARTITIONNODE *) MYMALLOC(sizeof(PARTITIONNODE)))== NULL_PARTITIONNODE) { fprintf(stderr,"?Unable to malloc partitionnode.\n"); exit(1); } newPartitionNode->sameDir= sameDir; newPartitionNode->oppDir= oppDir; newPartitionNode->negativeSide= NULL_BSPNODE; newPartitionNode->positiveSide= NULL_BSPNODE; return(newPartitionNode);} /* allocPartitionNode() *//* Frees partition node and sets pointer to it to null. * * partitonNode - partition node to be freed, pointer is set to null */static void freePartitionNode(PARTITIONNODE **partitionNode){ freeFaceList(&(*partitionNode)->sameDir); freeFaceList(&(*partitionNode)->oppDir); BSPfreeTree(&(*partitionNode)->negativeSide); BSPfreeTree(&(*partitionNode)->positiveSide); MYFREE((char *) *partitionNode); *partitionNode= NULL_PARTITIONNODE;} /* freePartitionNode() *//* Dumps information on faces. This should be replaced with user-supplied * polygon scan converter. */void drawFaceList(FILE *fp,const FACE *faceList){ const FACE *ftrav; for (ftrav= faceList; ftrav != NULL_FACE; ftrav= ftrav->fnext) { VERTEX *vtrav; fprintf(fp,"Face: RGBi:%.2f/%.2f/%.2f a: %.3f b: %.3f c: %.3f d: %.3f ", ftrav->color.rr,ftrav->color.gg,ftrav->color.bb, ftrav->plane.aa,ftrav->plane.bb,ftrav->plane.cc,ftrav->plane.dd); fprintf(fp,"\n"); for (vtrav= ftrav->vhead; vtrav->vnext != NULL_VERTEX; vtrav= vtrav->vnext) { fprintf(fp,"\t(%.3f,%.3f,%.3f) ",vtrav->xx,vtrav->yy,vtrav->zz); fprintf(fp,"\n"); } }} /* drawFaceList() *//*** bspTree.c ***/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -