![]() | Binary Space Partitioning Tutorial | ![]() |
![]() | written by Gary Simmons |


| Camera Position | Correct Drawing Order |
| Ca | D or E in any order, A , C & B is Unclear |
| Cb | D or E in any order, A , B & C is Unclear |
| Cc | C & B is Unclear , A , D or E in any order |
| In case you dont know what a plane is , every polygon lies on an infinite plane.Get a piece of A4 paper and draw a small triangle in the middle of it.The triangle is the Polygon but the Paper is the Plane.The plane goes on forever (it has no edges like the paper) though but can you see that if you hold the paper up infront of you and start turning it to different angles the paper is always at the same angle as the triangle.In other words a plane is almost like a polygon but with NO edges around the outside.You can also see that if you are sat in your living room and the paper went on forever it would carve a line in the walls of your house.If you imagine that walls of your house are polygons you can see that after the wall has been split by the paper one half would lie to one side of the paper and the other half would lie the other side (front and back splitting). |
![]() | Front List of split (A) | Back List of Split (A) | |
| B , C | D , E | ||
| Front List of split (B) | Back List of Split (B) | ||
| Ca | Cb | ||
| Front List of split (D) | Back List of Split (D) | ||
| E | Nothing |
| Note: The reason I keep reminding you that the polygons stored at each node are also the splitters is that it is possible to use any arbitrary planes as the splitters.For example, you may wish to devise a tree where all the splitters are planes constructed to be aligned to the principle world axis and in some instances this can be beneficial , but this usually causes more polygons to be split which raises the polygon count.Also its much easier to use the polygons as the splitting planes because we already have them. |
![]() | First we choose polygon R as our splitter.This means that the polygons get divided into two lists (Front & Back).At this point the Front list contains polygons G,H,I,J,K,L,M and the Back list contains polygons A,B,C,D,E,F. So then first we take a look at splitter R's Front list.We then choose another polygon (G in our example) and make Polygon R point to polygon G with its front child pointer .We further split this list into front and back lists.There are no children to the back of polygon G so all the remaining polygons (H,I,J,K,L,M) are put into G's front list.We then choose another splitter (H) and point to this with Polygon G's front child pointer.When the front list is done for each splitter we then do the same with the back list.Study the BSP Tree diagram hard an make sure you can see exactly how the splitters are linked to each other.When you have this in your head carry on reading... |
struct BSPNode{
POLYGON *splitter;
BSPNode *FrontChild;
BSPNode *BackChild;
};
void RenderBSP (NODE * CurrentNode)
{
int Result;
Result=ClassifyPoint(CameraPosition,CurrentNode->Polygon);
if (Result==Front)
{
if (CurrentNode->BackChild!=NULL) RenderBSP (CurrentNode->BackChild);
DrawPolygon(CurrentNode->Polygon);
if (CurrentNode->FrontChild!=NULL) RenderBSP (CurrentNode->FrontChild);
}
else
{
if (CurrentNode->FrontChild!=NULL) RenderBSP (CurrentNode->FrontChild);
DrawPolygon(CurrentNode->Polygon);
if (CurrentNode->BackChild!=NULL) RenderBSP (CurrentNode->BackChild);
}
}
![]() | We first call our function passing in our ROOT Node A. Are we in front of Node A? No we are behind it so examine Node A's front child.This points to node B.Is the camera in front of Node B ? No so check Node B's front Child.This is Node Ca(remember our compiler had to split this line because it straddled B's split line) of which there are NO front or back child Nodes so we render Polygon Ca.We then return to the function that was processing Node B.We draw Node B's polygon and then check Node B's Back child pointer which points to node Cb.This node has no front or back Child Nodes so we render polygon Cb which returns to Node B's function which itself is now finished with its work.So node B's function returns and we are now back in the original function call we made to A.We have rendered all the walls in front of A so now we Render Polygon A.We now step back and examine Node A's Back Child pointer which points to Node D.The camera is in front of Node D so we draw the Back Nodes first but there are none so we simply Draw Node D and then Check for any Nodes in front of D.There is one, Node E.Is the camera in front of E? Yes so draw back wall first but there aren't any so simply draw Polygon E and then check for Nodes that are in front of wall D.There aren't any so we are done and our level has been drawn in the correct order.WOW. |

void WalkBspTree(NODE *Node,D3DVECTOR *pos)
{
POLYGON *shared;
int result=ClassifyPoint(pos,Node-> Splitter);
if (result==CP_FRONT)
{
shared=Node-> Splitter->SameFacingShared;
if (Node-> Back!=NULL) WalkBspTree(Node-> Back,pos);
lpDevice-> DrawIndexedPrimitive(D3DPT_TRIANGLELIST,D3DFVF_LVERTEX,&Node-> Splitter-> VertexList[0],Node-> Splitter-> NumberOfVertices,&Node-> Splitter->Indices[0],Node-> Splitter-> NumberOfIndices,NULL);
while (shared!=NULL)
{
lpDevice-> DrawIndexedPrimitive(D3DPT_TRIANGLELIST,D3DFVF_LVERTEX,&shared-> VertexList[0],shared-> NumberOfVertices,&shared-> Indices[0],shared-> NumberOfIndices,NULL);
shared=shared-> SameFacingShared;
}
if (Node->Front!=NULL) WalkBspTree(Node->Front,pos);
return ;
}
// this means we are at back of node
shared=Node->Splitter->OppositeFacingShared;
if (Node->Front!=NULL) WalkBspTree(Node->Front,pos);
while (shared!=NULL)
{
lpDevice-> DrawIndexedPrimitive(D3DPT_TRIANGLELIST,D3DFVF_LVERTEX,&shared-> VertexList[0],shared-> NumberOfVertices,&shared-> Indices[0],shared-> NumberOfIndices,NULL);
shared=shared-> OppositeFacingShared;
}
if (Node-> Back!=NULL) WalkBspTree(Node->Back,pos);
return;
}
I have highlighted the lines of interest.If we are in front of the current Node then we Render the Back Tree first and then the Nodes polygon and also render all polygons that are facing the same way.If we are Behind this node then we render the front Tree but we DO NOT bother rendering the actual Nodes polygon because we know we are behind so will be back face culled anyway.Then we render ALL polygons that are facing the opposite way to the Node(in other words they are front facing because the node itself is back facing.

![]() | Ok then, our compiler function is passed all the polygons A through M to compile.It choose splitter A as the first split and stores it in the Root Node.We then test all the polygons against polygon A so that they are put into the respective Front and Back Lists.Node A's front List will contain Polygons E through M and A's back list will contain polygons B through D.Lets just concentrate on polygon A's back list for now.The Compiler function creates a new node and calls itself with this new node and poygon A's back list B,C,D.The compiler then chooses another wall as a splitter from this list .Wall B in our example.This wall is then stored in the new Node we passed in to the function and linked to Node A's Back Pointer. |
![]() | Now with Node B stored we then test the remaining walls in the list (C & D) against wall B and as you can see they both go in wall B's back list.There are no walls in front of Node B so instead of just setting it to NULL we create New node and add it to Node B's Front Pointer.This Node has no polygon though because it is a leaf.Instead we just set the variable 'IsLeaf' to true and because this leaf is in front of its parent node (B) we set 'IsSolid' to false.This is an empty space that can be walked in.If we traverse the tree and end up in this leaf then this means we are somewhere in the Blue box shown opposite.There are walls however in Node B's front list so the compiler function calls itself again once again passing in the Front list (walls C&D) and a newly created Node. |
![]() | This time through the compiler function has a choice of either wall C or wall D as a splitter.It chooses wall C and stores it in the newly passed node (which was attached to B's Back pointer).There are no walls in front of C so once again a new leaf node is created and added to Node C's Front Pointer .Once again 'IsSolid' is set to false because it is in front of C and 'IsLeaf' is set to true so we know that this is leaf and no polygon is stored here.Node C does have a back wall though (D) so once again the function called itself passing in wall D as the polygon list and also creates a new node that is attached to C's Back Pointer.This is passed to the function also and will end up containing D as the splitter because it is the only one left in the list. |
![]() | Now we choose splitter D because it is the only one left in the list.There are no front walls so once again a new leaf Node is created and attached to Node D's front pointer once again setting 'IsSolid' to false.If we end up in this leaf then we are somewhere within the green box shown opposite.However, there are no walls behind Node D either so once again we create a new leaf node and attach it to Node D's back list but because this leaf is BEHIND node D we set 'IsSolid' to true.If we end up in this leaf then we are in the soild area bounded by walls A,B,C,D and this is not allowed. Its important to realize how a leaf is bounded by its parent Nodes to create an atomic space.(convex Hull in other words).The function returns to Node C's function which in turn returns to Node B's function which ends up back at A (Root node and the first instance of the function that we called) where we have only processed the back list.Now we process Node A's front list which if you remember consited of walls E through M. |
![]() | The compiler function now recursivly calls itself again with Node A's front list.We choose wall E as the splitter,store it in a new node and again attach it to Node A's front pointer.The remaining polygons are tested against wall E and we end up with a back list of polygon F & G and a front list for E containing polygons H through M.Lets have a look at the back list first.The compiler function calls itself passing in the back list of E and a new node and this time wall F is selected and stored in the new node and the new node is conected to Node E's Back pointer.There are no walls in front of wall F so once again a new leaf is created and added to Node F's Front pointer once again setting 'IsLeaf' to true and because this leaf node is in front of wall F once again 'IsSolid' is set to false because this is an empty space.If we end up in this leaf we are in the dark red box to the right of wall F. G is the last wall sent which has no front or back walls.Once again a front leaf is created as being empty and attached to G's front Pointer and a Solid leaf is created and attached to G's back pointer.This leaf represents the solid are bounded by walls E,F & G.You can now see the top solid area is now completely represented in the tree. |
![]() | Here is this section finished being compiled.You step through the other splits in your head and make sure that you fully understand the THEORY of whats going on even if you do not know how to code it yet.The most important point to remember is that a splitter is not in ANY of its own lists.For example wall A may look like it is behind wall B but remember that wall B is a child of A and can only divide wall A's back subspace.Wall A is not in B's lists at all.Just like wall C is a child of B so wall C can not SEE B because it can only divide wall B's back sub space.This is the very core process to spacial sub division other wise the compiler could not section off bits of space. |
| Note: The above technique is a simplified version of how collision detection and line of sight works.Ofcourse it does not allow for the fact that the point you are about to move to is empty but there may be something in between your current position and the position you are about to move to.We will cover this in detail later when we write our LineOfSight function and its still very easy to solve but I do not want to confuse you any more than you probably already are at the moment. |
![]() | You can see that this polygon is made up of 5 vertices but has to be broken into three triangles in order to be rendered.Although this shape has 5 vertices it will need 9 indices to describe it to the renderer because the renderer needs to render triangles and each triangle needs 3 points.The nine indices would be as follows v1,v2,v3,v1,v3,v4,v1,v4,v5.You can calculate the number of indices needed with the following equation:- NumberOfIndices=(NumberOfVertices-2)*3; This of course has nothing whatsoever to do with BSP Trees but I am explaining how our BSP Compiler expects to see the polygons represented. This polygon can be rendered using the following line:- lpDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,D3DFVF_LVERTEX,pVertexList, 5 ,pIndices, 9 ,NULL); As you can see the above shape may render three triangles and that would normally mean 9 vertices have to be transformed and lit.However because we are using indices to describe the triangle only five vertices actually get transformed and lit and are re used by faces that share them.We will look at the code responsable for breaking up the polygons into multiple triangles later when we write our AddPolygon function which will add polygons to the scene. |
|
BYTE BSPMAP []= {0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,2,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0, 0,2,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0, 1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1, 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1, 0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,3,1, 0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1, 1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1, 0,1,0,0,0,0,1,2,0,0,0,1,0,0,0,1,0,0,0,1, 0,1,0,0,0,1,2,0,0,0,0,1,1,0,0,0,0,0,0,1, 0,1,0,0,0,1,0,0,0,0,0,3,1,0,0,0,0,0,0,1, 0,1,0,1,1,2,0,0,0,0,0,0,1,0,0,0,0,0,0,1, 1,2,0,0,0,0,0,0,1,0,0,0,1,1,1,1,0,0,0,1, 1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1, 1,0,0,1,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1, 1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; | With this array we are going to build a function that will loop through each element of the array.One entry in the array represents 1 unit squared of 3D World Space so when a 1 is encountered with zeros all around it 4 polygons are created for the four walls of a cube all facing outwards. If a number other than zero is to any side of the 1 then a wall is not built for that side because it is covered up by an adjacent wall.So in other words each 1 digit represent a 1x1 cube in world space and we will texture the four walls (n,s,e & w) with some brick wall texture.The 2 and 3's in the map were just put in there by me at the last minute to give the map a few angles.A 2 digit represents a NE facing wall and a 3 represents a NW facing wall.I didn't bother to create SW or SE but you can do that yourself if you want.Just build them the same as the NW and NE walls but reverse the winding order of the vertices.The polygons are created in WORLD space with the center of space 0,0,0 being in the middle of the map and each digit as I said represents 1x1 unit in space.The full grid is 20x40 in dimensions (not all shown here). Note:- For those of you reading this and thinking what a dodgy way to make a level you are of course right.But all I needed was an easy way to create lots of polygons to throw at the BSP Compiler.Also the last thing we need right now is a tutorial on how to import levels from various editors which actually is just as well because I have not got a clue. |
void InitPolygons(void)
{
D3DLVERTEX VERTLIST[4][4];
PolygonList=NULL;// this is a GLOBAL pointer
POLYGON *child=NULL;
int direction[4];
for (int y=0;y< 40;y++)
{
for (int x=0;x< 20;x++)
{
ZeroMemory(direction,sizeof(int)*4);
int offset=(y*20)+x;
// check what the digit is in the current map location
if (BSPMAP[offset]!=0)
{
if (BSPMAP[offset]==2)// North East Wall
{
VERTLIST[0][0]=D3DLVERTEX(D3DVECTOR(x-10.5f,3.0f,(20.0f-y)-0.5f),RGB_MAKE( 255, 255, 255),0,0,0);
VERTLIST[0][1]=D3DLVERTEX(D3DVECTOR(x-9.5f,3.0f,(20.0f-y)+0.5f),RGB_MAKE( 255, 255, 255),0,1,0);
VERTLIST[0][2]=D3DLVERTEX(D3DVECTOR(x-9.5f,0.0f,(20.0f-y)+0.5f),RGB_MAKE( 255, 255, 255),0,1,1);
VERTLIST[0][3]=D3DLVERTEX(D3DVECTOR(x-10.5f,0.0f,(20.0f-y)-0.5f),RGB_MAKE( 255, 255, 255),0,0,1);
direction[0]=1;
}
if (BSPMAP[offset]==3)// North West Wall
{
VERTLIST[0][0]=D3DLVERTEX(D3DVECTOR(x-10.5f,3.0f,(20.0f-y)+0.5f),RGB_MAKE( 255, 255, 255),0,0,0);
VERTLIST[0][1]=D3DLVERTEX(D3DVECTOR(x-9.5f,3.0f,(20.0f-y)-0.5f),RGB_MAKE( 255, 255, 255),0,1,0);
VERTLIST[0][2]=D3DLVERTEX(D3DVECTOR(x-9.5f,0.0f,(20.0f-y)-0.5f),RGB_MAKE( 255, 255, 255),0,1,1);
VERTLIST[0][3]=D3DLVERTEX(D3DVECTOR(x-10.5f,0.0f,(20.0f-y)+0.5f),RGB_MAKE( 255, 255, 255),0,0,1);
direction[0]=1;
}
if (BSPMAP[offset]==1)// Its a Standared wall
{
if (x > 0)
{
if (BSPMAP[offset-1]==0)// if theres nothing to the left add a left facing wall
{
VERTLIST[0][0]=D3DLVERTEX(D3DVECTOR(x-10.5f,3.0f,(20.0f-y)+0.5f),RGB_MAKE(255,255,255),0,0,0);
VERTLIST[0][1]=D3DLVERTEX(D3DVECTOR(x-10.5f,3.0f,(20.0f-y)-0.5f),RGB_MAKE(255,255,255),0,1,0);
VERTLIST[0][2]=D3DLVERTEX(D3DVECTOR(x-10.5f,0.0f,(20.0f-y)-0.5f),RGB_MAKE(255,255,255),0,1,1);
VERTLIST[0][3]=D3DLVERTEX(D3DVECTOR(x-10.5f,0.0f,(20.0f-y)+0.5f),RGB_MAKE(255,255,255),0,0,1);
direction[0]=1;
}
}
if (x < 19)
{
if (BSPMAP[offset+1]==0)// if there is nothing to the right add a right facing wall
{
VERTLIST[1][0]=D3DLVERTEX(D3DVECTOR(x-9.5f,3.0f,(20.0f-y)-0.5f),RGB_MAKE(255,255,255),0,0,0);
VERTLIST[1][1]=D3DLVERTEX(D3DVECTOR(x-9.5f,3.0f,(20.0f-y)+0.5f),RGB_MAKE(255,255,255),0,1,0);
VERTLIST[1][2]=D3DLVERTEX(D3DVECTOR(x-9.5f,0.0f,(20.0f-y)+0.5f),RGB_MAKE(255,255,255),0,1,1);
VERTLIST[1][3]=D3DLVERTEX(D3DVECTOR(x-9.5f,0.0f,(20.0f-y)-0.5f),RGB_MAKE(255,255,255),0,0,1);
direction[1]=1;
}
}
if (y > 0)
{
if (BSPMAP[offset-20]==0)// if there is nothing south add a south facing wall
{
VERTLIST[2][0]=D3DLVERTEX(D3DVECTOR(x-9.5f,3.0f,(20.0f-y)+0.5f),RGB_MAKE(255,255,255),0,0,0);
VERTLIST[2][1]=D3DLVERTEX(D3DVECTOR(x-10.5f,3.0f,(20.0f-y)+0.5f),RGB_MAKE(255,255,255),0,1,0);
VERTLIST[2][2]=D3DLVERTEX(D3DVECTOR(x-10.5f,0.0f,(20.0f-y)+0.5f),RGB_MAKE(255,255,255),0,1,1);
VERTLIST[2][3]=D3DLVERTEX(D3DVECTOR(x-9.5f,0.0f,(20.0f-y)+0.5f),RGB_MAKE( 255, 255, 255),0,0,1);
direction[2]=1;;
}
}
if(y < 39)
{
if (BSPMAP[offset+20]==0)// if there is nothing to the north add a north facing wall
{
VERTLIST[3][0]=D3DLVERTEX(D3DVECTOR(x-10.5f,3.0f,(20.0f-y)-0.5f),RGB_MAKE(255,255,255),0,0,0);
VERTLIST[3][1]=D3DLVERTEX(D3DVECTOR(x-9.5f,3.0f,(20.0f-y)-0.5f),RGB_MAKE(255, 255, 255),0,1,0);
VERTLIST[3][2]=D3DLVERTEX(D3DVECTOR(x-9.5f,0.0f,(20.0f-y)-0.5f),RGB_MAKE( 255, 255, 255),0,1,1);
VERTLIST[3][3]=D3DLVERTEX(D3DVECTOR(x-10.5f,0.0f,(20.0f-y)-0.5f),RGB_MAKE( 255, 255, 255),0,0,1);
direction[3]=1;;
}
}
}// end for if offset==1
// build the polygons
for (int a=0;a<4;a++)
{
if (direction[a]!=0)
{
if (PolygonList==NULL)
{
PolygonList=AddPolygon(NULL,&VERTLIST[a][0],4);
child=PolygonList;
}
else
{
child=AddPolygon(child,&VERTLIST[a][0],4);
}
}//
}////
}// end for if offset!=0
}
}
BSPTreeRootNode=new NODE;
BuildBspTree(BSPTreeRootNode,PolygonList);
}
POLYGON * AddPolygon(POLYGON* Parent,D3DLVERTEX *Vertices,WORD NOV)
{
int loop;
POLYGON * Child=new POLYGON;
Child-> NumberOfVertices=NOV;
Child-> NumberOfIndices=(NOV-2)*3;
Child-> Next=NULL;
for (loop=0;loop< NOV;loop++)
{
Child-> VertexList[loop]=Vertices[loop];
}
//calculate indices
WORD v0,v1,v2;
for (loop=0;loop< Child-> NumberOfIndices/3;loop++)
{
if (loop==0)
{
v0=0;
v1=1;
v2=2;
}
else
{
v1=v2;
v2++;
}
Child-> Indices[loop*3]=v0;
Child-> Indices[(loop*3)+1]=v1;
Child-> Indices[(loop*3)+2]=v2;
}
Not very long this bit is it when you think how clever it is.Can you see whats happening each time through the loop? Get a pen and paper and try it drawing a square with four vertices (or any convex shape) and have a look at the indices list you get.Actually forget the pen and paper I will fire up my paint package and show you exactly what is happening to our wall (square polygon) in this loop.
![]() | You can see that the two triangles are created out of the square yet only four Vertices are needed to describe them.The Indices list for our wall would hold 6 entries like so:- v1,v2,v3,v1,v3,v4 Three Indicies a triangle so two triangles need six Indices. |
// generate polygon normal
D3DVECTOR * vec0=(D3DVECTOR *) &Child->VertexList[0];
D3DVECTOR * vec1=(D3DVECTOR *) &Child->VertexList[1];
D3DVECTOR * vec2=(D3DVECTOR *) &Child->VertexList[Child->NumberOfVertices-1];// the last vert
D3DVECTOR edge1=(*vec1)-(*vec0);
D3DVECTOR edge2=(*vec2)-(*vec0);
Child->Normal=CrossProduct(edge1,edge2);
Child->Normal=Normalize(Child->Normal);
if (Parent!=NULL)
{
Parent->Next=Child;
}
return Child;
}
POLYGON * AddPolygon(POLYGON* Parent,D3DLVERTEX *Vertices,WORD NOV)
{
int loop;
POLYGON * Child=new POLYGON;
Child-> NumberOfVertices=NOV;
Child-> NumberOfIndices=(NOV-2)*3;
Child-> Next=NULL;
for (loop=0;loop< NOV;loop++)
{
Child-> VertexList[loop]=Vertices[loop];
}
//calculate indices
WORD v0,v1,v2;
for (loop=0;loop< Child-> NumberOfIndices/3;loop++)
{
if (loop==0)
{
v0=0;
v1=1;
v2=2;
}
else
{
v1=v2;
v2++;
}
Child-> Indices[loop*3]=v0;
Child-> Indices[(loop*3)+1]=v1;
Child-> Indices[(loop*3)+2]=v2;
}
// generate polygon normal
D3DVECTOR * vec0=(D3DVECTOR *) &Child->VertexList[0];
D3DVECTOR * vec1=(D3DVECTOR *) &Child->VertexList[1];
D3DVECTOR * vec2=(D3DVECTOR *) &Child->VertexList[Child->NumberOfVertices-1];// the last vert
D3DVECTOR edge1=(*vec1)-(*vec0);
D3DVECTOR edge2=(*vec2)-(*vec0);
Child->Normal=CrossProduct(edge1,edge2);
Child->Normal=Normalize(Child->Normal);
if (Parent!=NULL)
{
Parent->Next=Child;
}
return Child;
}
BSPTreeRootNode=new NODE;
BuildBspTree(BSPTreeRootNode,PolygonList);
int ClassifyPoint(D3DVECTOR *pos,POLYGON * Plane)
{
float result;
D3DVECTOR *vec1=(D3DVECTOR *)&Plane-> VertexList[0];
D3DVECTOR Direction=(*vec1)-(*pos);
result=DotProduct(Direction,Plane->Normal);
if (result< -0.001) return CP_FRONT;
if (result> 0.001) return CP_BACK;
return CP_ONPLANE;int ClassifyPoly(POLYGON *Plane,POLYGON * Poly)
{
int Infront=0;
int Behind=0;
int OnPlane=0;
float result;
D3DVECTOR *vec1=(D3DVECTOR *)&Plane->VertexList[0];
for (int a=0;aNumberOfVertices;a++)
{
D3DVECTOR *vec2=(D3DVECTOR *)&Poly->VertexList[a];
D3DVECTOR Direction=(*vec1)-(*vec2);
result=DotProduct(Direction,Plane->Normal);
if (result> 0.001)
{
Behind++;
}
else
if (result< -0.001)
{
Infront++;
}
else
{
OnPlane++;
Infront++;
Behind++;
}
}
if (OnPlane==Poly-> NumberOfVertices) return CP_FRONT;// this would nomrally be CP_ONPLANE
if (Behind==Poly-> NumberOfVertices) return CP_BACK;
if (Infront==Poly-> NumberOfVertices) return CP_FRONT;
return CP_SPANNING;
}
Not a lot to explain here.It just classifies each point in the Polygon with the Plane (which is passed in as a polygon itself) and for each point behind,in front or on the plane a counter is kept.At the end of the function if all the vertices are not on the plane and all the vertices are not in front of the plane and all the vertices are not behind the plane then it means that vertices must be on opposing sides of the plane meaning a split will have to be performed.In this case the function returns CP_SPANNING.This will tell our compiler function that the polygon needs to be split.void SplitPolygon(POLYGON *Poly,POLYGON *Plane,POLYGON *FrontSplit,POLYGON *BackSplit);![]() | As you can see the left tree has exactly one node behind the root and one node in front of the root.The right most tree has no nodes in front of it but has two nodes behind it.One very important thing to notice here is that the left most tree has a depth of 1 but the right most tree actually has a depth of 2.By the depth I mean in the diagram the number of nodes vertically descending from the root node.The deeper a tree (the more nodes you have to traverse before you reach a node) the more time it takes to traverse the tree.Sometimes frame rate consistency is more important than out right speed.For example it is better to have a game engine run at 30 fps consistently throughout the level than have a level run at 90fps in some places and drop to 7fps in others.This is where the Balance of the tree comes into play.Although the balance of the tree does not effect OUR rendering routines because we will be using the painters algorithm to draw back to front which means every node will be visited anyway (until we implemement Fustrum rejection bounding boxes.more on this later), the balance will effect the collision detection routines we write later.Look at the right most example in the above picture.If we are in front of R we no longer have to check any walls because there are no front walls in Rs list.However if we are behind wall R we will have to descend into the tree and visit the nodes down the back list.Now imagine this simple example being converted to thousands of polygons and you can see that the frame rate would drop if we were behind wall R and speed up in front of wall R.The balanced tree above would be consistent because it would be equally deep at each leaf node. |
POLYGON * SelectBestSplitter(POLYGON *PolyList)
{
POLYGON* Splitter=PolyList;
POLYGON* CurrentPoly=NULL;
unsigned long BestScore=100000;// just set to a very high value to begin
POLYGON * SelectedPoly=NULL;
while (Splitter!=NULL)
{
CurrentPoly=PolyList;
unsigned long score,splits,backfaces,frontfaces;
score=splits=backfaces=frontfaces=0;
while (CurrentPoly!=NULL)
{
if (CurrentPoly!=Splitter)
{
int result=ClassifyPoly(Splitter,CurrentPoly);
switch ( result)
{
case CP_ONPLANE:
break;
case CP_FRONT:
frontfaces++;
break;
case CP_BACK:
backfaces++;
break;
case CP_SPANNING:
splits++;
break;
default:
break;
}
}
CurrentPoly=CurrentPoly-> Next;
}// end while current poly
score=abs(frontfaces-backfaces)+(splits*8);
if (score< BestScore)
{
BestScore=score;
SelectedPoly=Splitter;
}
Splitter=Splitter-> Next;
}// end while splitter == null
return SelectedPoly;
}
void BuildBspTree(NODE * CurrentNode,POLYGON * PolyList)
{
POLYGON *polyTest=NULL;
POLYGON *FrontList=NULL;
POLYGON *BackList=NULL;
POLYGON *NextPolygon=NULL;
POLYGON *FrontSplit=NULL;
POLYGON *BackSplit=NULL;
D3DVECTOR vec1,vec2;
CurrentNode-> Splitter=SelectBestSplitter(PolyList);
polyTest=PolyList;
while (polyTest!=NULL)
{
NextPolygon=polyTest-> Next;// remember because polytest-> Next will be altered
if (polyTest!=CurrentNode-> Splitter)
{
switch (ClassifyPoly(CurrentNode-> Splitter,polyTest))
{
case CP_FRONT:
polyTest-> Next=FrontList;
FrontList=polyTest;
break;
case CP_BACK:
polyTest-> Next=BackList;
BackList=polyTest;
break;
Nothing special with the first two cases.If the polygon is behind or front of the splitter it is added to the appropriate lists.Just in case you are confused by the switching around I will explain it.In the case of CP_FRONT above, the Current Polygon being tested has its next pointer set to point at the 'FrontList' pointer.This will be NULL the first time a polygon is assigned to the front list.Then the FrontList pointer is altered to point at the newly assigned polygon.In other words the new polygon is added to the top of the list and the polygons 'Next' pointer is set to point at whatever FrontList pointed at before so we still have a linked list.Notice that we have changed the 'Next' Pointer of the polygon which we would normally use to look at the next polygon in the global polygon list.We cannot do this now because it only points to the back list.This is why we made a copy of this pointer above so that we can still step through and access the rest of the polygons.case CP_SPANNING:
FrontSplit=new POLYGON;
BackSplit=new POLYGON;
ZeroMemory(FrontSplit,sizeof(POLYGON));
ZeroMemory(BackSplit,sizeof(POLYGON));
SplitPolygon(polyTest,CurrentNode-> Splitter,FrontSplit,BackSplit);
delete polyTest;
FrontSplit-> Next=FrontList;
FrontList=FrontSplit;
BackSplit-> Next=BackList;
BackList=BackSplit;
break;
default:
break;
}
}// end if polypoint!=CurrentNodesplitter
polyTest=NextPolygon;
}// end while loop
If the current polygon being tested does straddle the splitter then we create two new polygons and zero the memory.We then send these pointers along with the polygons that needs to be split and the Splitter it needs to be split against to the 'SplitPolygon' function which will when returns will have split the polygon into two halves and they will be pointed to by the FrontSplit and BackSplit pointers.Interestingly enough we no longer need the orginal polygon so we free up the memory.This may sound strange at first but remember a polygon isn't in the tree until it is used as a splitter and I may have been split many times by different splitters at this point.If a polygon is split and then those to splits are split the first two splits not only are not used by the tree anymore but we also loose pointers to them in memory so we must free them up here while a valid temporary pointer to the unsplit polygon still exists.The FrontSplit and BackSplit pointers which now point to the two newly created splits are added to the front list and back list respectively.After this polyTest is then reloaded with the orginal 'Next' pointers value which of course is the next polygon in the input list.We do this loop for every polygon in the input list (ignoring the splitter itself of course so this does not get added to any list).if (FrontList==NULL)
{
NODE *leafnode=new NODE;
ZeroMemory(leafnode,sizeof(leafnode));
leafnode-> IsLeaf=true;
leafnode-> IsSolid=false;
CurrentNode-> Front=leafnode;
}
else
{
NODE * newnode=new NODE;
ZeroMemory(newnode,sizeof(newnode));
newnode-> IsLeaf=false;
CurrentNode-> Front=newnode;
BuildBspTree(newnode,FrontList);
}
if (BackList==NULL)
{
NODE *leafnode=new NODE;
ZeroMemory(leafnode,sizeof(leafnode));
leafnode-> IsLeaf=true;
leafnode-> IsSolid=true;
CurrentNode-> Back=leafnode;;
}
else
{
NODE * newnode=new NODE;
ZeroMemory(newnode,sizeof(newnode));
newnode-> IsLeaf=false;
CurrentNode-> Back=newnode;
BuildBspTree(newnode,BackList);
}
}// end functionvoid BuildBspTree(NODE * CurrentNode,POLYGON * PolyList)
{
POLYGON *polyTest=NULL;
POLYGON *FrontList=NULL;
POLYGON *BackList=NULL;
POLYGON *NextPolygon=NULL;
POLYGON *FrontSplit=NULL;
POLYGON *BackSplit=NULL;
D3DVECTOR vec1,vec2;
CurrentNode-> Splitter=SelectBestSplitter(PolyList);
polyTest=PolyList;
while (polyTest!=NULL)
{
NextPolygon=polyTest-> Next;// remember because polytest-> Next will be altered
if (polyTest!=CurrentNode-> Splitter)
{
switch (ClassifyPoly(CurrentNode-> Splitter,polyTest))
{
case CP_FRONT:
polyTest-> Next=FrontList;
FrontList=polyTest;
break;
case CP_BACK:
polyTest-> Next=BackList;
BackList=polyTest;
break;
case CP_SPANNING:
FrontSplit=new POLYGON;
BackSplit=new POLYGON;
ZeroMemory(FrontSplit,sizeof(POLYGON));
ZeroMemory(BackSplit,sizeof(POLYGON));
SplitPolygon(polyTest,CurrentNode-> Splitter,FrontSplit,BackSplit);
delete polyTest;
FrontSplit-> Next=FrontList;
FrontList=FrontSplit;
BackSplit-> Next=BackList;
BackList=BackSplit;
break;
default:
break;
}
}// end if polypoint!=CurrentNodesplitter
polyTest=NextPolygon;
}// end while loop
if (FrontList==NULL)
{
NODE *leafnode=new NODE;
ZeroMemory(leafnode,sizeof(leafnode));
leafnode-> IsLeaf=true;
leafnode-> IsSolid=false;
CurrentNode-> Front=leafnode;
}
else
{
NODE * newnode=new NODE;
ZeroMemory(newnode,sizeof(newnode));
newnode-> IsLeaf=false;
CurrentNode-> Front=newnode;
BuildBspTree(newnode,FrontList);
}
if (BackList==NULL)
{
NODE *leafnode=new NODE;
ZeroMemory(leafnode,sizeof(leafnode));
leafnode-> IsLeaf=true;
leafnode-> IsSolid=true;
CurrentNode-> Back=leafnode;;
}
else
{
NODE * newnode=new NODE;
ZeroMemory(newnode,sizeof(newnode));
newnode-> IsLeaf=false;
CurrentNode-> Back=newnode;
BuildBspTree(newnode,BackList);
}
}// end functionbool Get_Intersect (D3DVECTOR *linestart,D3DVECTOR *lineend,D3DVECTOR *vertex,D3DVECTOR *normal,D3DVECTOR * intersection, float *percentage)
{
D3DVECTOR direction,L1;
float linelength,dist_from_plane;
direction.x=lineend->x-linestart->x;
direction.y=lineend->y-linestart->y;
direction.z=lineend->z-linestart->z;
linelength=DotProduct(direction,*normal);
if (fabsf(linelength)<0.0001)
{
return false;
}
L1.x=vertex->x-linestart->x;
L1.y=vertex->y-linestart->y;
L1.z=vertex->z-linestart->z;
dist_from_plane=DotProduct(L1,*normal);
*percentage=dist_from_plane/linelength;
if (*percentage<0.0f)
{
return false;
}
else
if (*percentage>1.0f)
{
return false;
}
intersection->x=linestart->x+direction.x*(*percentage);
intersection->y=linestart->y+direction.y*(*percentage);
intersection->z=linestart->z+direction.z*(*percentage);
return true;
}void SplitPolygon(POLYGON *Poly,POLYGON *Plane,POLYGON *FrontSplit,POLYGON *BackSplit)
{
D3DLVERTEX FrontList[20],BackList[20],FirstVertex;
D3DVECTOR PlaneNormal,IntersectPoint,PointOnPlane,PointA,PointB;
WORD FrontCounter=0,BackCounter=0,loop=0,CurrentVertex=0;
float percent;
// Find any vertex on the plane to use later in plane intersect routine
PointOnPlane=*(D3DVECTOR *)&Plane->VertexList[0];
The first thing we do above is store the first vertex of the splitter polygon in the PointOnPlane vector.The line may look a bit weird with all its casting etc but remember that the first part of a D3DLVERTEX structure is just a vector so can be cast as such.We will use this PointOnPlane variable later when we call the GetIntersect routine.
// first we find out if the first vertex belongs in front or back list
FirstVertex=Poly->VertexList[0];
switch (ClassifyPoint( (D3DVECTOR *)&FirstVertex,Plane))
{
case CP_FRONT:
FrontList[FrontCounter++]=FirstVertex;
break;
case CP_BACK:
BackList[BackCounter++]=FirstVertex;
break;
case CP_ONPLANE:
BackList[BackCounter++]=FirstVertex;
FrontList[FrontCounter++]=FirstVertex;
break;
default:
break;
}
As you have probably gathered the whole purpose of this function is just to classify edges of the polygon to the splitter and assigned them to the relative lists.Once we have done this we we have two lists which we can then construct the polygons out of.![]() | We have already assigned V0 to the front list in our example with the preceeding code.Now we loop through vertices 1 though 0(again) and each edge being tested is the Current Vertex with the previous.So for example we test point 1 & 0 then point 2 & 1 etc. This is how it works using the example opposite.We test v1 and v0 and discover they are both on the front side of the splitter so v1 gets added to the Front list (which already contains v0) and so all is done.Next we compare v2 with v1 and discover they are on opposing sides of the splitter so we call GetIntersect which will return the position of the new vertex on the split line (v1a opposite).This intersection is added to both lists and v2 is added to the back list. Front List so far contains : v0,v1,v1a Back List so far contains : v1a,v2. Now next time through the loop we test v3 with v2 and they are both behind the plane so v3 gets added to the back list.We then loop round to compare v4 with v3 and find another intersection.The intersection (v3a) is added to the back list and the front list and v4 is added to the front list also. Front List so far contains : v0,v1,v1a,v3a,v4 Back List so far contains : v1a,v2,v3,v3a We then loop through again but roll back over to test zero and the previous vertex (v4) there is no split they are both on the same side of the plane but because this is vertex zero which is already in the list it does not get added to any list and we are done.However had there been a split between v4 & v0 (lets call it v4a) then v4a would have been added to both front and back lists. |
for (loop=1;loop<Poly->NumberOfVertices+1;loop++)
{
if (loop==Poly->NumberOfVertices)
{
CurrentVertex=0;
}
else
{
CurrentVertex=loop;
}
PointA=*(D3DVECTOR *)&Poly->VertexList[loop-1];
PointB=*(D3DVECTOR *)&Poly->VertexList[CurrentVertex];
As you can see we loop through the vertices of the polygon that needs to be split.The current vertex to be worked on is equal to the current value of the loop unless it we have rolled over the last vertex which means we set the current vertex to zero. At this point we now know the two vertices that make up the edge we are going to test.PointA a takes the previous vertex in the polygon (which would have already been assigned to a list) and casts it to a D3DVECTOR so that we can use it with all our functions and PointB casts the current vertex (not in any list yet) to a D3DVECTOR.In other words, the first time through the loop PointA will hold vertex zero and PointB will hold vertex one.This ofcourse makes up the first edge.PlaneNormal=Plane->Normal;
int Result=ClassifyPoint(&PointB,Plane);
if (Result==CP_ONPLANE)
{
BackList[BackCounter++]=Poly->VertexList[CurrentVertex];
FrontList[FrontCounter++]=Poly->VertexList[CurrentVertex];
}
else
{
If PointB is not on the plane then we test for an intersection with the plane.We call GetIntersect passing in PointA,PointB and the Ray start and end and also pass in the Point on the plane we got earlier (first vertex in the splitter) and the Plane Normal(splitters Normal).If this function returns 'true' IntersecPoint will hold the X,Y,Z of the new vertex created by the split that is on the plane (v1a or v3a in the above example).We can create a new vertex out of this vector.Also if the function returns 'true' the float 'percentage' will hold how far down the ray from the start (as a percentage between 0 and 1) the intersection occurred.We will use this for generating a NEW texture coordinate for the NEW vertex.if (Get_Intersect(&PointA,&PointB,&PointOnPlane,&PlaneNormal,&IntersectPoint, &percent)==true)
{
If we pass the test and a collision has occurred we already have the new vertex position in 'IntersectPoint'.When I First wrote my compiler the level was not texture mapped.I simply created a new vertex out of the IntersectPoint returned and fely very happy with my self until I tried to to texture the level and all the textures screwed up.I forgot that the new vertex is going to need a new Texture Coordinate.This was quite easy to overcome (after a bit of thought) which is why the 'GetIntersect' function was modified to also return a percentage.Lets see how it works.(I assume you are familiar with the Normalized Texture Coordinates that D3D uses).![]() | The picture opposite shows how a triangle polygon may be mapped to a texture.You can see the texture coordinates for v1 and v2.The red line shows where an intersection with the plane has occurred with this polygon and infact shows the point that a new texture coordinate needs to be created for.First of all we subtract the first vertex texture coordintes from the second and we end up with the Vector Length of the line between v1 and v2 on the texture.You can see opposite that <0.8,0.7>-<0.4,0.2>=<0.4,0.5> which is the direction and the length between texture coordinate 1 & 2.Now the great thing is that our GetIntersect function returned a percentage from the start of the line that the intersect occurred.We can reuse this value for texture coordinates as well.For example imagine that the percentage returned by GetIntersect is 0.5 meaning that the plane intersect the polygon exactly half way between vertex 1 & vertex 2.If we multiply the Vector opposite (vector length) by the percentage (0.5 in this example) we get a vector of Now just add this vector so it is relative to the start point (the start of the ray or in our case the first vertices texture coordinates) New Texture Coordinates=<0.4 , 0.2> + <0.2 , 0.25>=<0.6 , 0.45> This same interpolation could also be used for calculating a Vertex light value as well. |
float deltax,deltay,texx,texy;
deltax=Poly->VertexList[CurrentVertex].tu-Poly->VertexList[loop-1].tu;
deltay=Poly->VertexList[CurrentVertex].tv-Poly->VertexList[loop-1].tv;
texx=Poly->VertexList[loop-1].tu+(deltax*percent);
texy=Poly->VertexList[loop-1].tv+(deltay*percent);
D3DLVERTEX copy=D3DLVERTEX(IntersectPoint,RGB_MAKE(255,255,255),0,texx,texy);
Now we have the new Vertex and we know we have to add it to both front and back polygon list (because it belongs in both the front split and the back split polygons) but we also have to put the Current Vertex into the correct list also.If PointB is in front of the splitter then we add the New Vertex to Both lists and then add PointB to the Front List AS LONG AS the current vertex is NOT vertex zero because this one would already be in the list.The same is true if PointB is behind the splitter but the other way around obviously.
if (Result==CP_FRONT )
{
BackList[BackCounter++]=copy;
FrontList[FrontCounter++]=copy;
if (CurrentVertex!=0)
{
FrontList[FrontCounter++]=Poly->VertexList[CurrentVertex];
}
}
if (Result==CP_BACK)
{
FrontList[FrontCounter++]=copy;
BackList[BackCounter++]=copy;
if (CurrentVertex!=0)
{
BackList[BackCounter++]=Poly->VertexList[CurrentVertex];
}
}
}// end if intersection (get intersect==true)
else
{
if (Result==CP_FRONT)
{
if (CurrentVertex!=0)
{
FrontList[FrontCounter++]=Poly->VertexList[CurrentVertex];
}
}
if (Result==CP_BACK)
{
if (CurrentVertex!=0)
{
BackList[BackCounter++]=Poly->VertexList[CurrentVertex];
}
}
}
}
}//end loop through each edge
At this point in the function we all the loops have ended and we have two lists of vertices.One for the front split polygon and one for the back split polygon.The remainder of the function simply builds the polygons using exactly the same technique as we used earlier in our AddPolygon routine.It copies the Vertex Lists in to the Front and Back split polygon structures, Calculates the indices (because remember even if a triangle is split with a plane one of the splits will now have four vertices) and cuilds the indices for each polygon.Then as a last step it generates the polygon Normals for the two polygons.Heres the rest of the function:-//OK THEN LETS BUILD THESE TWO POLYGONAL BAD BOYS
FrontSplit->NumberOfVertices=0;
BackSplit->NumberOfVertices=0;
for (loop=0;loop<FrontCounter;loop++)
{
FrontSplit->NumberOfVertices++;
FrontSplit->VertexList[loop]=FrontList[loop];
}
for (loop=0;loop<BackCounter;loop++)
{
BackSplit->NumberOfVertices++;
BackSplit->VertexList[loop]=BackList[loop];
}
BackSplit->NumberOfIndices=(BackSplit->NumberOfVertices-2)*3;
FrontSplit->NumberOfIndices=(FrontSplit->NumberOfVertices-2)*3;
// Fill in the Indices Array
WORD v0,v1,v2;
for (loop=0;loop<FrontSplit->NumberOfIndices/3;loop++)
{
if (loop==0)
{
v0=0;
v1=1;
v2=2;
}
else
{
v1=v2;
v2++;
}
FrontSplit->Indices[loop*3]=v0;
FrontSplit->Indices[(loop*3)+1]=v1;
FrontSplit->Indices[(loop*3)+2]=v2;
}
for (loop=0;loop<BackSplit->NumberOfIndices/3;loop++)
{
if (loop==0)
{
v0=0;
v1=1;
v2=2;
}
else
{
v1=v2;
v2++;
}
BackSplit->Indices[loop*3]=v0;
BackSplit->Indices[(loop*3)+1]=v1;
BackSplit->Indices[(loop*3)+2]=v2;
}
// calculate polygon Normals
D3DVECTOR edge1,edge2;
edge1=*(D3DVECTOR *)&FrontSplit->VertexList[FrontSplit->Indices[1]]-*(D3DVECTOR *)&FrontSplit->VertexList[FrontSplit->Indices[0]];
edge2=*(D3DVECTOR *)&FrontSplit->VertexList[FrontSplit->Indices[FrontSplit->NumberOfIndices-1]]-*(D3DVECTOR *)&FrontSplit->VertexList[FrontSplit->Indices[0]];
FrontSplit->Normal=CrossProduct(edge1,edge2);
FrontSplit->Normal=Normalize(FrontSplit->Normal);
edge1=*(D3DVECTOR *)&BackSplit->VertexList[BackSplit->Indices[1]]-*(D3DVECTOR *)&BackSplit->VertexList[BackSplit->Indices[0]];
edge2=*(D3DVECTOR *)&BackSplit->VertexList[BackSplit->Indices[BackSplit->NumberOfIndices-1]]-*(D3DVECTOR *)&BackSplit->VertexList[BackSplit->Indices[0]];
BackSplit->Normal=CrossProduct(edge1,edge2);
BackSplit->Normal=Normalize(BackSplit->Normal);
}
Phew, that function was larger than the actual BSP Compiler function.But it is the last function needed to make our BSP Compiler function work.Once again then , here is the Split Polygon Function in its entirety.void SplitPolygon(POLYGON *Poly,POLYGON *Plane,POLYGON *FrontSplit,POLYGON *BackSplit)
{
D3DLVERTEX FrontList[20],BackList[20],FirstVertex;
D3DVECTOR PlaneNormal,IntersectPoint,PointOnPlane,PointA,PointB;
WORD FrontCounter=0,BackCounter=0,loop=0,CurrentVertex=0;
float percent;
// Find any vertex on the plane to use later in plane intersect routine
PointOnPlane=*(D3DVECTOR *)&Plane->VertexList[0];
// first we find out if the first vertex belongs in front or back list
FirstVertex=Poly->VertexList[0];
switch (ClassifyPoint( (D3DVECTOR *)&FirstVertex,Plane))
{
case CP_FRONT:
FrontList[FrontCounter++]=FirstVertex;
break;
case CP_BACK:
BackList[BackCounter++]=FirstVertex;
break;
case CP_ONPLANE:
BackList[BackCounter++]=FirstVertex;
FrontList[FrontCounter++]=FirstVertex;
break;
default:
break;
}
for (loop=1;loop<Poly->NumberOfVertices+1;loop++)
{
if (loop==Poly->NumberOfVertices)
{
CurrentVertex=0;
}
else
{
CurrentVertex=loop;
}
PointA=*(D3DVECTOR *)&Poly->VertexList[loop-1];
PointB=*(D3DVECTOR *)&Poly->VertexList[CurrentVertex];
PlaneNormal=Plane->Normal;
int Result=ClassifyPoint(&PointB,Plane);
if (Result==CP_ONPLANE)
{
BackList[BackCounter++]=Poly->VertexList[CurrentVertex];
FrontList[FrontCounter++]=Poly->VertexList[CurrentVertex];
}
else
{
if (Get_Intersect(&PointA,&PointB,&PointOnPlane,&PlaneNormal,&IntersectPoint, &percent)==true)
{
float deltax,deltay,texx,texy;
deltax=Poly->VertexList[CurrentVertex].tu-Poly->VertexList[loop-1].tu;
deltay=Poly->VertexList[CurrentVertex].tv-Poly->VertexList[loop-1].tv;
texx=Poly->VertexList[loop-1].tu+(deltax*percent);
texy=Poly->VertexList[loop-1].tv+(deltay*percent);
D3DLVERTEX copy=D3DLVERTEX(IntersectPoint,RGB_MAKE(255,255,255),0,texx,texy);
if (Result==CP_FRONT )
{
BackList[BackCounter++]=copy;
FrontList[FrontCounter++]=copy;
if (CurrentVertex!=0)
{
FrontList[FrontCounter++]=Poly->VertexList[CurrentVertex];
}
}
if (Result==CP_BACK)
{
FrontList[FrontCounter++]=copy;
BackList[BackCounter++]=copy;
if (CurrentVertex!=0)
{
BackList[BackCounter++]=Poly->VertexList[CurrentVertex];
}
}
}// end if intersection (get intersect==true)
else
{
if (Result==CP_FRONT)
{
if (CurrentVertex!=0)
{
FrontList[FrontCounter++]=Poly->VertexList[CurrentVertex];
}
}
if (Result==CP_BACK)
{
if (CurrentVertex!=0)
{
BackList[BackCounter++]=Poly->VertexList[CurrentVertex];
}
}
}
}
}//end loop through each edge
//OK THEN LETS BUILD THESE TWO POLYGONAL BAD BOYS
FrontSplit->NumberOfVertices=0;
BackSplit->NumberOfVertices=0;
for (loop=0;loop<FrontCounter;loop++)
{
FrontSplit->NumberOfVertices++;
FrontSplit->VertexList[loop]=FrontList[loop];
}
for (loop=0;loop<BackCounter;loop++)
{
BackSplit->NumberOfVertices++;
BackSplit->VertexList[loop]=BackList[loop];
}
BackSplit->NumberOfIndices=(BackSplit->NumberOfVertices-2)*3;
FrontSplit->NumberOfIndices=(FrontSplit->NumberOfVertices-2)*3;
// Fill in the Indices Array
WORD v0,v1,v2;
for (loop=0;loop<FrontSplit->NumberOfIndices/3;loop++)
{
if (loop==0)
{
v0=0;
v1=1;
v2=2;
}
else
{
v1=v2;
v2++;
}
FrontSplit->Indices[loop*3]=v0;
FrontSplit->Indices[(loop*3)+1]=v1;
FrontSplit->Indices[(loop*3)+2]=v2;
}
for (loop=0;loop<BackSplit->NumberOfIndices/3;loop++)
{
if (loop==0)
{
v0=0;
v1=1;
v2=2;
}
else
{
v1=v2;
v2++;
}
BackSplit->Indices[loop*3]=v0;
BackSplit->Indices[(loop*3)+1]=v1;
BackSplit->Indices[(loop*3)+2]=v2;
}
// calculate polygon Normals
D3DVECTOR edge1,edge2;
edge1=*(D3DVECTOR *)&FrontSplit->VertexList[FrontSplit->Indices[1]]-*(D3DVECTOR *)&FrontSplit->VertexList[FrontSplit->Indices[0]];
edge2=*(D3DVECTOR *)&FrontSplit->VertexList[FrontSplit->Indices[FrontSplit->NumberOfIndices-1]]-*(D3DVECTOR *)&FrontSplit->VertexList[FrontSplit->Indices[0]];
FrontSplit->Normal=CrossProduct(edge1,edge2);
FrontSplit->Normal=Normalize(FrontSplit->Normal);
edge1=*(D3DVECTOR *)&BackSplit->VertexList[BackSplit->Indices[1]]-*(D3DVECTOR *)&BackSplit->VertexList[BackSplit->Indices[0]];
edge2=*(D3DVECTOR *)&BackSplit->VertexList[BackSplit->Indices[BackSplit->NumberOfIndices-1]]-*(D3DVECTOR *)&BackSplit->VertexList[BackSplit->Indices[0]];
BackSplit->Normal=CrossProduct(edge1,edge2);
BackSplit->Normal=Normalize(BackSplit->Normal);
}
void WalkBspTree(NODE *Node,D3DVECTOR *pos)
{
if (Node->IsLeaf==true) return;
int result=ClassifyPoint(pos,Node->Splitter);
if (result==CP_FRONT)
{
if (Node->Back!=NULL) WalkBspTree(Node->Back,pos);
lpDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,D3DFVF_LVERTEX,&Node->Splitter->VertexList[0],Node->Splitter->NumberOfVertices,&Node->Splitter->Indices[0],Node->Splitter->NumberOfIndices,NULL);
if (Node->Front!=NULL) WalkBspTree(Node->Front,pos);
return ;
}
// this happens if we are at back or on plane
if (Node->Front!=NULL) WalkBspTree(Node->Front,pos);
if (Node->Back!=NULL) WalkBspTree(Node->Back,pos);
return;
}


bool LineOfSight (D3DVECTOR *Start,D3DVECTOR *End, NODE *Node)
{
float temp;
D3DVECTOR intersection;
if (Node->IsLeaf==true)
{
return !Node->IsSolid;
}
int PointA=ClassifyPoint(Start,Node->Splitter);
int PointB=ClassifyPoint(End,Node->Splitter);
if (PointA==CP_ONPLANE && PointB==CP_ONPLANE)
{
return LineOfSight(Start,End,Node->Front);
}
if (PointA==CP_FRONT && PointB==CP_BACK)
{
Get_Intersect (Start,End,(D3DVECTOR *) &Node->Splitter->VertexList[0],&Node->Splitter->Normal,&intersection,&temp);
return LineOfSight(Start,&intersection,Node->Front) && LineOfSight(End,&intersection,Node->Back) ;
}
if (PointA==CP_BACK && PointB==CP_FRONT)
{
Get_Intersect (Start,End,(D3DVECTOR *) &Node->Splitter->VertexList[0],&Node->Splitter->Normal,&intersection,&temp);
return LineOfSight(End,&intersection,Node->Front) && LineOfSight(Start,&intersection,Node->Back) ;
}
// if we get here one of the points is on the plane
if (PointA==CP_FRONT || PointB==CP_FRONT)
{
return LineOfSight(Start,End,Node->Front);
}
else
{
return LineOfSight(Start,End,Node->Back);
}
return true;
}
With a little help from the GetIntersect function the above code sorts out the whole collision detection thing in just a couple of lines of code by just recursively calling itself until every section of the reay ends up in a leaf (either solid or empty).It's quick too.