1 /****************************************************************************************/
\r
4 /* Author: John Pollard */
\r
5 /* Description: Creates and manages portals (passages from leaf-to-leaf) */
\r
7 /* The contents of this file are subject to the Genesis3D Public License */
\r
8 /* Version 1.01 (the "License"); you may not use this file except in */
\r
9 /* compliance with the License. You may obtain a copy of the License at */
\r
10 /* http://www.genesis3d.com */
\r
12 /* Software distributed under the License is distributed on an "AS IS" */
\r
13 /* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See */
\r
14 /* the License for the specific language governing rights and limitations */
\r
15 /* under the License. */
\r
17 /* The Original Code is Genesis3D, released March 25, 1999. */
\r
18 /*Genesis3D Version 1.1 released November 15, 1999 */
\r
19 /* Copyright (C) 1999 WildTangent, Inc. All Rights Reserved */
\r
21 /****************************************************************************************/
\r
22 #include <Windows.h>
\r
26 #include "Portals.h"
\r
29 #include "GBSPFile.h"
\r
36 GBSP_Node *OutsideNode;
\r
37 geVec3d NodeMins, NodeMaxs;
\r
38 geBoolean VisPortals;
\r
40 geBoolean CreateAllOutsidePortals(GBSP_Node *Node);
\r
41 void GetNodeMinsMaxs(GBSP_Node *Node);
\r
42 geBoolean AddPortalToNodes(GBSP_Portal *Portal, GBSP_Node *Front, GBSP_Node *Back);
\r
43 geBoolean RemovePortalFromNode(GBSP_Portal *Portal, GBSP_Node *Node);
\r
44 GBSP_Portal *AllocPortal(void);
\r
46 //=====================================================================================
\r
48 //=====================================================================================
\r
49 geBoolean CreatePolyOnNode (GBSP_Node *Node, GBSP_Poly **Out)
\r
55 Poly = CreatePolyFromPlane(&Planes[Node->PlaneNum]);
\r
59 GHook.Error("CreatePolyOnNode: Could not create poly.\n");
\r
63 // Clip this portal by all the parents of this node
\r
64 for (Parent = Node->Parent ; Parent && Poly ; )
\r
68 Plane = &Planes[Parent->PlaneNum];
\r
70 Side = (Parent->Children[0] == Node) ? GE_FALSE : GE_TRUE;
\r
72 if (!ClipPolyEpsilon(Poly, 0.001f, Plane, Side, &Poly))
\r
76 Parent = Parent->Parent;
\r
84 //=====================================================================================
\r
86 //=====================================================================================
\r
87 geBoolean CreatePortals(GBSP_Node *RootNode, GBSP_Model *Model, geBoolean Vis)
\r
90 GHook.Printf(" --- Create Portals --- \n");
\r
94 OutsideNode = &Model->OutsideNode;
\r
96 NodeMins = Model->Mins;
\r
97 NodeMaxs = Model->Maxs;
\r
99 if (!CreateAllOutsidePortals(RootNode))
\r
101 GHook.Error("CreatePortals: Could not create bbox portals.\n");
\r
105 if (!PartitionPortals_r(RootNode))
\r
107 GHook.Error("CreatePortals: Could not partition portals.\n");
\r
114 //=====================================================================================
\r
115 // CreateOutsidePortal
\r
116 //=====================================================================================
\r
117 GBSP_Portal *CreateOutsidePortal(GBSP_Plane *Plane, GBSP_Node *Node)
\r
119 GBSP_Portal *NewPortal;
\r
122 NewPortal = AllocPortal();
\r
126 NewPortal->Poly = CreatePolyFromPlane(Plane);
\r
127 if (!NewPortal->Poly)
\r
131 NewPortal->PlaneNum = FindPlane(Plane, &Side);
\r
133 if (NewPortal->PlaneNum == -1)
\r
135 GHook.Error("CreateOutsidePortal: Could not create plane.\n");
\r
141 if (!AddPortalToNodes(NewPortal, Node, OutsideNode))
\r
146 if (!AddPortalToNodes(NewPortal, OutsideNode, Node))
\r
153 //=====================================================================================
\r
154 // CreateAllOutsidePortals
\r
155 //=====================================================================================
\r
156 geBoolean CreateAllOutsidePortals(GBSP_Node *Node)
\r
159 GBSP_Plane PPlanes[6];
\r
160 GBSP_Portal *Portals[6];
\r
162 memset(OutsideNode, 0, sizeof(GBSP_Node));
\r
164 memset(PPlanes, 0, 6*sizeof(GBSP_Plane));
\r
166 OutsideNode->PlaneNum = PLANENUM_LEAF;
\r
167 OutsideNode->Contents = BSP_CONTENTS_SOLID2;
\r
168 OutsideNode->Portals = NULL;
\r
169 OutsideNode->BrushList = NULL;
\r
171 // So there won't be NULL volume leafs when we create the outside portals
\r
172 for (k=0; k< 3; k++)
\r
174 if (VectorToSUB(NodeMins, k)-128 <= -MIN_MAX_BOUNDS || VectorToSUB(NodeMaxs, k)+128 >= MIN_MAX_BOUNDS)
\r
176 GHook.Error("CreateAllOutsidePortals: World BOX out of range...\n");
\r
180 VectorToSUB(NodeMins, k) -= (geFloat)128;
\r
181 VectorToSUB(NodeMaxs, k) += (geFloat)128;
\r
184 // Create 6 portals, and point to the outside and the RootNode
\r
185 for (i=0; i<3; i++)
\r
187 for (k=0; k<2; k++)
\r
190 geVec3d_Clear(&PPlanes[Index].Normal);
\r
194 VectorToSUB(PPlanes[Index].Normal, i) = (geFloat)1;
\r
195 PPlanes[Index].Dist = VectorToSUB(NodeMins, i);
\r
199 VectorToSUB(PPlanes[Index].Normal, i) = (geFloat)-1;
\r
200 PPlanes[Index].Dist = -VectorToSUB(NodeMaxs, i);
\r
203 Portals[Index] = CreateOutsidePortal(&PPlanes[Index], Node);
\r
204 if (!Portals[Index])
\r
209 for (i=0; i< 6; i++)
\r
211 for (k=0; k< 6; k++)
\r
216 if (!ClipPoly(Portals[i]->Poly, &PPlanes[k], GE_FALSE, &Portals[i]->Poly))
\r
218 GHook.Error("CreateAllOutsidePortals: There was an error clipping the portal.\n");
\r
222 if (!Portals[i]->Poly)
\r
224 GHook.Error("CreateAllOutsidePortals: Portal was clipped away.\n");
\r
233 //=====================================================================================
\r
235 //=====================================================================================
\r
236 geBoolean CheckPortal(GBSP_Portal *Portal)
\r
243 Poly = Portal->Poly;
\r
244 Verts = Poly->Verts;
\r
246 if (Poly->NumVerts < 3)
\r
248 GHook.Error("CheckPortal: NumVerts < 3.\n");
\r
252 for (i=0; i< Poly->NumVerts; i++)
\r
254 for (k=0; k<3; k++)
\r
256 Val = VectorToSUB(Verts[i], k);
\r
258 if (Val == MIN_MAX_BOUNDS)
\r
260 GHook.Error("CheckPortal: Portal was not clipped on all sides!!!\n");
\r
264 if (Val == -MIN_MAX_BOUNDS)
\r
266 GHook.Error("CheckPortal: Portal was not clipped on all sides!!!\n");
\r
275 //=======================================================================================
\r
276 // CalcNodeBoundsFromPortals
\r
277 // Calcs bounds for nodes, and leafs
\r
278 //=======================================================================================
\r
279 void CalcNodeBoundsFromPortals(GBSP_Node *Node)
\r
284 ClearBounds(&Node->Mins, &Node->Maxs);
\r
286 for (p=Node->Portals; p; p = p->Next[s])
\r
288 s = (p->Nodes[1] == Node);
\r
290 for (i=0; i<p->Poly->NumVerts; i++)
\r
291 AddPointToBounds(&p->Poly->Verts[i], &Node->Mins, &Node->Maxs);
\r
295 //=====================================================================================
\r
296 // PartitionPortals_r
\r
297 //=====================================================================================
\r
298 geBoolean PartitionPortals_r(GBSP_Node *Node)
\r
300 GBSP_Poly *NewPoly, *FPoly, *BPoly;
\r
301 GBSP_Plane *pPlane, *pPlane2;
\r
302 GBSP_Portal *Portal, *NewPortal, *Next;
\r
303 GBSP_Node *Front, *Back, *OtherNode;
\r
306 CalcNodeBoundsFromPortals(Node);
\r
308 if (Node->PlaneNum == PLANENUM_LEAF)
\r
311 if (VisPortals && Node->Detail) // We can stop at detail seperators for the vis tree
\r
314 //if (!InitializeNodePortal(Node))
\r
315 // return GE_FALSE;
\r
316 //if (!DistributeNodePortalsToChildren(Node))
\r
317 // return GE_FALSE;
\r
319 Front = Node->Children[0];
\r
320 Back = Node->Children[1];
\r
322 pPlane = &Planes[Node->PlaneNum];
\r
324 // Create a new portal
\r
325 if (!CreatePolyOnNode (Node, &NewPoly))
\r
327 GHook.Error("PartitionPortals_r: CreatePolyOnNode failed.\n");
\r
331 // Clip it against all other portals attached to this node
\r
332 for (Portal = Node->Portals; Portal && NewPoly; Portal = Portal->Next[Side])
\r
334 if (Portal->Nodes[0] == Node)
\r
336 else if (Portal->Nodes[1] == Node)
\r
340 GHook.Error("PartitionPortals_r: Portal does not look at either node.\n");
\r
344 pPlane2 = &Planes[Portal->PlaneNum];
\r
346 if (!ClipPolyEpsilon(NewPoly, 0.001f, pPlane2, Side, &NewPoly))
\r
348 GHook.Error("PartitionPortals_r: There was an error clipping the poly.\n");
\r
354 GHook.Printf("PartitionPortals_r: Portal was cut away.\n");
\r
359 if (NewPoly && PolyIsTiny (NewPoly))
\r
367 NewPortal = AllocPortal();
\r
370 GHook.Error("PartitionPortals_r: Out of memory for portal.\n");
\r
373 NewPortal->Poly = NewPoly;
\r
374 NewPortal->PlaneNum = Node->PlaneNum;
\r
375 NewPortal->OnNode = Node;
\r
377 if (!CheckPortal(NewPortal))
\r
379 GHook.Error("PartiionPortals_r: Check Portal failed.\n");
\r
383 AddPortalToNodes(NewPortal, Front, Back);
\r
387 // Partition all portals by this node
\r
388 for (Portal = Node->Portals; Portal; Portal = Next)
\r
390 if (Portal->Nodes[0] == Node)
\r
392 else if (Portal->Nodes[1] == Node)
\r
396 GHook.Error("PartitionPortals_r: Portal does not look at either node.\n");
\r
400 Next = Portal->Next[Side];
\r
402 // Remember the node on the back side
\r
403 OtherNode = Portal->Nodes[!Side];
\r
404 RemovePortalFromNode(Portal, Portal->Nodes[0]);
\r
405 RemovePortalFromNode(Portal, Portal->Nodes[1]);
\r
407 if (!SplitPolyEpsilon(Portal->Poly, 0.001f, pPlane, &FPoly, &BPoly, GE_FALSE))
\r
409 GHook.Error("PartitionPortals_r: Could not split portal.\n");
\r
413 if (FPoly && PolyIsTiny(FPoly))
\r
419 if (BPoly && PolyIsTiny(BPoly))
\r
425 if (!FPoly && !BPoly)
\r
430 Portal->Poly = BPoly;
\r
432 AddPortalToNodes(Portal, OtherNode, Back);
\r
434 AddPortalToNodes(Portal, Back, OtherNode);
\r
440 Portal->Poly = FPoly;
\r
442 AddPortalToNodes(Portal, OtherNode, Front);
\r
444 AddPortalToNodes(Portal, Front, OtherNode);
\r
448 // Portal was split
\r
449 NewPortal = AllocPortal();
\r
452 GHook.Error("PartitionPortals_r: Out of memory for portal.\n");
\r
455 Portal->Poly = FPoly;
\r
456 *NewPortal = *Portal;
\r
457 NewPortal->Poly = BPoly;
\r
461 AddPortalToNodes(Portal, OtherNode, Front);
\r
462 AddPortalToNodes(NewPortal, OtherNode, Back);
\r
466 AddPortalToNodes(Portal, Front, OtherNode);
\r
467 AddPortalToNodes(NewPortal, Back, OtherNode);
\r
471 if (Node->Portals != NULL)
\r
473 GHook.Printf("*WARNING* PartitionPortals_r: Portals still on node after distribution...\n");
\r
476 if (!PartitionPortals_r(Front))
\r
479 if (!PartitionPortals_r(Back))
\r
485 //=====================================================================================
\r
486 // AddPortalToNodes
\r
487 //=====================================================================================
\r
488 geBoolean AddPortalToNodes(GBSP_Portal *Portal, GBSP_Node *Front, GBSP_Node *Back)
\r
490 if (Portal->Nodes[0] || Portal->Nodes[1])
\r
492 GHook.Error("LinkPortal: Portal allready looks at one of the nodes.\n");
\r
496 Portal->Nodes[0] = Front;
\r
497 Portal->Next[0] = Front->Portals;
\r
498 Front->Portals = Portal;
\r
500 Portal->Nodes[1] = Back;
\r
501 Portal->Next[1] = Back->Portals;
\r
502 Back->Portals = Portal;
\r
507 //=====================================================================================
\r
508 // RemovePortalFromNode
\r
509 //=====================================================================================
\r
510 geBoolean RemovePortalFromNode(GBSP_Portal *Portal, GBSP_Node *Node)
\r
512 GBSP_Portal *p, **p2;
\r
515 assert(Node->Portals); // Better have some portals on this node
\r
516 assert(!(Portal->Nodes[0] == Node && Portal->Nodes[1] == Node));
\r
517 assert(Portal->Nodes[0] == Node || Portal->Nodes[1] == Node);
\r
519 // Find the portal on this node
\r
520 for (p2 = &Node->Portals, p = *p2; p; p2 = &p->Next[Side], p = *p2)
\r
522 assert(!(p->Nodes[0] == Node && p->Nodes[1] == Node));
\r
523 assert(p->Nodes[0] == Node || p->Nodes[1] == Node);
\r
525 Side = (p->Nodes[1] == Node); // Get the side of the portal that this node is on
\r
531 assert(p && p2 && *p2);
\r
533 Side = (Portal->Nodes[1] == Node); // Get the side of the portal that the node was on
\r
535 *p2 = Portal->Next[Side];
\r
536 Portal->Nodes[Side] = NULL;
\r
541 //=====================================================================================
\r
543 //=====================================================================================
\r
544 GBSP_Portal *AllocPortal(void)
\r
546 GBSP_Portal *NewPortal;
\r
548 NewPortal = GE_RAM_ALLOCATE_STRUCT(GBSP_Portal);
\r
552 GHook.Error("AllocPortal: Out of memory!\n");
\r
556 memset(NewPortal, 0, sizeof(GBSP_Portal));
\r
561 //=====================================================================================
\r
563 //=====================================================================================
\r
564 geBoolean FreePortal(GBSP_Portal *Portal)
\r
568 GHook.Error("FreePortal: Portal with NULL Poly.\n");
\r
572 FreePoly(Portal->Poly);
\r
574 geRam_Free(Portal);
\r
579 //=====================================================================================
\r
581 //=====================================================================================
\r
582 geBoolean FreePortals_r(GBSP_Node *Node)
\r
584 GBSP_Portal *Portal, *Next;
\r
590 for (Portal = Node->Portals; Portal; Portal = Next)
\r
592 if (Portal->Nodes[0] == Node)
\r
594 else if (Portal->Nodes[1] == Node)
\r
598 GHook.Error("FreePortals_r: Portal does not look at either node.\n");
\r
602 Next = Portal->Next[Side];
\r
604 if (!RemovePortalFromNode(Portal, Portal->Nodes[0]))
\r
607 if (!RemovePortalFromNode(Portal, Portal->Nodes[1]))
\r
610 if (!FreePortal(Portal))
\r
614 Node->Portals = NULL;
\r
616 if (Node->PlaneNum == PLANENUM_LEAF)
\r
619 if (!FreePortals_r(Node->Children[0]))
\r
622 if (!FreePortals_r(Node->Children[1]))
\r
628 //=====================================================================================
\r
630 //=====================================================================================
\r
631 geBoolean FreePortals(GBSP_Node *RootNode)
\r
633 return(FreePortals_r(RootNode));
\r
636 void AddPointToBounds(geVec3d *v, geVec3d *Mins, geVec3d *Maxs);
\r
638 //=====================================================================================
\r
640 //=====================================================================================
\r
641 void GetNodeMinsMaxs(GBSP_Node *Node)
\r
643 ClearBounds(&NodeMins, &NodeMaxs);
\r
645 NodeMins = Node->Mins;
\r
646 NodeMaxs = Node->Maxs;
\r
649 //====================================================================================
\r
650 // GetLeafBBoxFromPortals
\r
651 //====================================================================================
\r
652 geBoolean GetLeafBBoxFromPortals(GBSP_Node *Node, geVec3d *Mins, geVec3d *Maxs)
\r
656 GBSP_Portal *Portal;
\r
659 if (Node->PlaneNum != PLANENUM_LEAF)
\r
661 GHook.Error("GetLeafBBoxFromPortals: Not a leaf.\n");
\r
665 ClearBounds(Mins, Maxs);
\r
667 for (Portal = Node->Portals; Portal; Portal = Portal->Next[Side])
\r
669 Side = (Portal->Nodes[1] == Node);
\r
671 Poly = Portal->Poly;
\r
673 Verts = Poly->Verts;
\r
674 for (i=0; i< Poly->NumVerts; i++)
\r
676 for (k=0; k<3; k++)
\r
678 if (VectorToSUB(Verts[i], k) < VectorToSUB(*Mins, k))
\r
679 VectorToSUB(*Mins, k) = VectorToSUB(Verts[i], k);
\r
680 if (VectorToSUB(Verts[i], k) > VectorToSUB(*Maxs, k))
\r
681 VectorToSUB(*Maxs, k) = VectorToSUB(Verts[i], k);
\r