| 1 | /* |
|---|
| 2 | ================================================================================= |
|---|
| 3 | This file is part of Cafu, the open-source game engine and graphics engine |
|---|
| 4 | for multiplayer, cross-platform, real-time 3D action. |
|---|
| 5 | Copyright (C) 2002-2012 Carsten Fuchs Software. |
|---|
| 6 | |
|---|
| 7 | Cafu is free software: you can redistribute it and/or modify it under the terms |
|---|
| 8 | of the GNU General Public License as published by the Free Software Foundation, |
|---|
| 9 | either version 3 of the License, or (at your option) any later version. |
|---|
| 10 | |
|---|
| 11 | Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|---|
| 12 | without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
|---|
| 13 | PURPOSE. See the GNU General Public License for more details. |
|---|
| 14 | |
|---|
| 15 | You should have received a copy of the GNU General Public License |
|---|
| 16 | along with Cafu. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 17 | |
|---|
| 18 | For support and more information about Cafu, visit us at <http://www.cafu.de>. |
|---|
| 19 | ================================================================================= |
|---|
| 20 | */ |
|---|
| 21 | |
|---|
| 22 | /***************************************/ |
|---|
| 23 | /*** ***/ |
|---|
| 24 | /*** Cafu Potentially Visibility Set ***/ |
|---|
| 25 | /*** ***/ |
|---|
| 26 | /***************************************/ |
|---|
| 27 | |
|---|
| 28 | #include <time.h> |
|---|
| 29 | #include <stdio.h> |
|---|
| 30 | |
|---|
| 31 | #include "CaPVSWorld.hpp" |
|---|
| 32 | #include "Win32/Win32Console.hpp" |
|---|
| 33 | |
|---|
| 34 | |
|---|
| 35 | const time_t ProgramStartTime=time(NULL); |
|---|
| 36 | Win32ConsoleT Win32Console; |
|---|
| 37 | ArrayT<SuperLeafT> SuperLeaves; |
|---|
| 38 | ArrayT<unsigned long> SuperLeavesPVS; |
|---|
| 39 | unsigned long MasterSuperLeafNr; |
|---|
| 40 | |
|---|
| 41 | |
|---|
| 42 | // This function determines the adjacency cell graph of the 'SuperLeaves'. |
|---|
| 43 | // It is filled-in and stored in the 'Neighbours' members of the 'SuperLeaves'. |
|---|
| 44 | void DetermineAdjacencyGraph() |
|---|
| 45 | { |
|---|
| 46 | Win32Console.FunctionID("SuperLeaves Adjacency Graph"); |
|---|
| 47 | |
|---|
| 48 | for (unsigned long SL1Nr=0; SL1Nr<SuperLeaves.Size(); SL1Nr++) |
|---|
| 49 | for (unsigned long SL2Nr=0; SL2Nr<SuperLeaves.Size(); SL2Nr++) |
|---|
| 50 | { |
|---|
| 51 | if (SL1Nr==SL2Nr || !BoundingBoxTest(SuperLeaves[SL1Nr].BB, SuperLeaves[SL2Nr].BB)) continue; |
|---|
| 52 | |
|---|
| 53 | // Jedes Portal des ersten Leafs gegen jedes Portal des zweiten Leafs |
|---|
| 54 | // prüfen und testen, ob sie sich schneiden (dann sind sie durchlässig). |
|---|
| 55 | // BEACHTE: SuperLeaves haben *alle* Portale ihrer Leaves! |
|---|
| 56 | // D.h. es können sich nicht nur Portale auf ihrer konvexen Hülle befinden, sondern auch "innen drin". |
|---|
| 57 | // Wegen der Konvexität der SuperLeaves dürfte das aber keine Rolle spielen und folgendes müßte trotzdem korrekt funktionieren. |
|---|
| 58 | for (unsigned long Portal1Nr=0; Portal1Nr<SuperLeaves[SL1Nr].Portals.Size(); Portal1Nr++) |
|---|
| 59 | for (unsigned long Portal2Nr=0; Portal2Nr<SuperLeaves[SL2Nr].Portals.Size(); Portal2Nr++) |
|---|
| 60 | { |
|---|
| 61 | if (!PolygonOverlap(SuperLeaves[SL1Nr].Portals[Portal1Nr], SuperLeaves[SL2Nr].Portals[Portal2Nr])) continue; |
|---|
| 62 | |
|---|
| 63 | // Folgender Check ist zwar redundant, aber zumindest sinnvoll. |
|---|
| 64 | // Da diese Funktion sowieso schnell genug ist, lasse ihn nicht weg! |
|---|
| 65 | if (PolygonWhatSide(SuperLeaves[SL1Nr].Portals[Portal1Nr], SuperLeaves[SL2Nr].Portals[Portal2Nr].Plane)!=-3) continue; |
|---|
| 66 | |
|---|
| 67 | ArrayT<PolygonT> NewPortals; |
|---|
| 68 | PolygonChopUp(SuperLeaves[SL2Nr].Portals[Portal2Nr], SuperLeaves[SL1Nr].Portals[Portal1Nr], NewPortals); |
|---|
| 69 | |
|---|
| 70 | SuperLeaves[SL1Nr].Neighbours.PushBackEmpty(); |
|---|
| 71 | SuperLeaves[SL1Nr].Neighbours[SuperLeaves[SL1Nr].Neighbours.Size()-1].SuperLeafNr=SL2Nr; |
|---|
| 72 | SuperLeaves[SL1Nr].Neighbours[SuperLeaves[SL1Nr].Neighbours.Size()-1].SubPortal =NewPortals[NewPortals.Size()-1]; |
|---|
| 73 | } |
|---|
| 74 | } |
|---|
| 75 | |
|---|
| 76 | printf("done.\n"); |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | |
|---|
| 80 | // Markiere das SuperLeaf 'SLNr' als von 'MasterSuperLeafNr' aus sichtbar. |
|---|
| 81 | inline void FlagVisible(unsigned long SLNr) |
|---|
| 82 | { |
|---|
| 83 | const unsigned long PVSTotalBitNr=MasterSuperLeafNr*SuperLeaves.Size()+SLNr; |
|---|
| 84 | const unsigned long PVS_W32_Nr =PVSTotalBitNr >> 5; |
|---|
| 85 | const unsigned long PVSBitMask =1 << (PVSTotalBitNr & 31); |
|---|
| 86 | |
|---|
| 87 | SuperLeavesPVS[PVS_W32_Nr]|=PVSBitMask; |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | |
|---|
| 91 | // Bestimme die Sichtpyramide (Frustum), die eine Lichtquelle (LightSource) durch ein Loch (Hole) wirft. |
|---|
| 92 | // Dabei leuchtet die Lichtquelle in die entgegengesetzte(!!) Richtung ihres Normalenvektors, und das Loch |
|---|
| 93 | // läßt auch nur Licht in der Gegenrichtung seines Normalenvektors durch. Würde man das Loch zur Lichtquelle |
|---|
| 94 | // und umgekehrt machen (PolygonMirror), wäre das Frustum das gleiche, die Ebenen wären aber gespiegelt! |
|---|
| 95 | // Voraussetzung: Die Ebene der Lichtquelle schneidet nicht das Loch und umgekehrt. Dieser Fall wird nicht |
|---|
| 96 | // abgedeckt bzw. ist gar nicht durchdacht. Da die Leaves konvex sind usw. wird dies aber immer eingehalten! |
|---|
| 97 | inline void FindFrustum(const PolygonT& LightSource, const PolygonT& Hole, ArrayT<PlaneT>& Frustum) |
|---|
| 98 | { |
|---|
| 99 | Frustum.Clear(); // Frustum.ClearForOverwrite(); |
|---|
| 100 | |
|---|
| 101 | unsigned long V2=Hole.Vertices.Size()-1; |
|---|
| 102 | for(unsigned long V3=0; V3<Hole.Vertices.Size(); V3++) |
|---|
| 103 | { |
|---|
| 104 | for (unsigned long V1=0; V1<LightSource.Vertices.Size(); V1++) |
|---|
| 105 | { |
|---|
| 106 | // Eigentlich würde ich hier gerne folgenden Wunsch-Code schreiben: |
|---|
| 107 | // try |
|---|
| 108 | // { |
|---|
| 109 | // PlaneT FrustumPlane(Hole.Vertices[V2], LightSource.Vertices[V1], Hole.Vertices[V3]); |
|---|
| 110 | // |
|---|
| 111 | // // ... |
|---|
| 112 | // } |
|---|
| 113 | // catch (DivisionByZero) { } // Nicht mögliche FrustumPlanes einfach ignorieren. |
|---|
| 114 | // Aus irgendeinem Grund ist die Verwendung oder das Fangen der DivisionByZero-Exception aber sehr langsam. |
|---|
| 115 | // Deshalb rolle ich lieber den PlaneT-Konstruktor aus, um ohne dieses Exception-Handling auszukommen. |
|---|
| 116 | // Das Programm wird *deutlich* schneller, ca. Faktor 1,5. Ob das eine Schwäche des Watcom-Compilers ist?? |
|---|
| 117 | VectorT Normal(VectorCross(Hole.Vertices[V3]-Hole.Vertices[V2], LightSource.Vertices[V1]-Hole.Vertices[V2])); |
|---|
| 118 | double NLength=VectorLength(Normal); |
|---|
| 119 | |
|---|
| 120 | if (NLength<0.1) continue; |
|---|
| 121 | Normal=VectorScale(Normal, 1.0/NLength); |
|---|
| 122 | |
|---|
| 123 | PlaneT FrustumPlane(Normal, VectorDot(Hole.Vertices[V2], Normal)); |
|---|
| 124 | |
|---|
| 125 | // Diese neue FrustumPlane nur dann akzeptieren, wenn das Hole auf ihrer Vorderseite liegt |
|---|
| 126 | // (konstruktionsbedingt sowieso der Fall!) und die LightSource auf ihrer Rückseite liegt. |
|---|
| 127 | // Wenn eine Edge des Hole in der Ebene der LightSource liegt, darf die LightSource |
|---|
| 128 | // auch in der FrustumPlane liegen. |
|---|
| 129 | if (PolygonWhatSide(LightSource, FrustumPlane)<0) |
|---|
| 130 | { |
|---|
| 131 | Frustum.PushBack(FrustumPlane); |
|---|
| 132 | break; |
|---|
| 133 | } |
|---|
| 134 | } |
|---|
| 135 | |
|---|
| 136 | V2=V3; |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | // Rollen vertauschen: Das Loch sei nun die Lichtquelle, und die Lichtquelle das Loch! Siehe Skizze! |
|---|
| 140 | V2=LightSource.Vertices.Size()-1; |
|---|
| 141 | for(V3=0; V3<LightSource.Vertices.Size(); V3++) |
|---|
| 142 | { |
|---|
| 143 | for (unsigned long V1=0; V1<Hole.Vertices.Size(); V1++) // Optimize: Check if edges are in already existing frustum planes! |
|---|
| 144 | { |
|---|
| 145 | // Eigentlich würde ich hier gerne folgenden Wunsch-Code schreiben: |
|---|
| 146 | // try |
|---|
| 147 | // { |
|---|
| 148 | // PlaneT FrustumPlane(LightSource.Vertices[V2], Hole.Vertices[V1], LightSource.Vertices[V3]); |
|---|
| 149 | // |
|---|
| 150 | // // ... |
|---|
| 151 | // } |
|---|
| 152 | // catch (DivisionByZero) { } // Nicht mögliche Ebenen einfach ignorieren. |
|---|
| 153 | // Aus irgendeinem Grund ist die Verwendung oder das Fangen der DivisionByZero-Exception aber sehr langsam. |
|---|
| 154 | // Deshalb rolle ich lieber den PlaneT-Konstruktor aus, um ohne dieses Exception-Handling auszukommen. |
|---|
| 155 | // Das Programm wird *deutlich* schneller, ca. Faktor 1,5. Ob das eine Schwäche des Watcom-Compilers ist?? |
|---|
| 156 | VectorT Normal(VectorCross(LightSource.Vertices[V3]-LightSource.Vertices[V2], Hole.Vertices[V1]-LightSource.Vertices[V2])); |
|---|
| 157 | double NLength=VectorLength(Normal); |
|---|
| 158 | |
|---|
| 159 | if (NLength<0.1) continue; |
|---|
| 160 | Normal=VectorScale(Normal, 1.0/NLength); |
|---|
| 161 | |
|---|
| 162 | PlaneT FrustumPlane(Normal, VectorDot(LightSource.Vertices[V2], Normal)); |
|---|
| 163 | |
|---|
| 164 | // Diese neue FrustumPlane nur dann akzeptieren, wenn die LightSource auf ihrer Rückseite |
|---|
| 165 | // liegt (konstruktionsbedingt sowieso der Fall!) und das Hole auf ihrer Vorderseite liegt. |
|---|
| 166 | // Wenn eine Edge der LightSource in der Ebene des Holes liegt, darf das Hole |
|---|
| 167 | // auch in der FrustumPlane liegen. |
|---|
| 168 | const signed char Side=PolygonWhatSide(Hole, FrustumPlane); // Wegen dem Rollentausch ist die Orientierung für diesen Test falsch, ... |
|---|
| 169 | |
|---|
| 170 | if (Side==1 || Side==-3) |
|---|
| 171 | { |
|---|
| 172 | Frustum.PushBack(FrustumPlane); // ...im Gesamten aber wieder richtig! |
|---|
| 173 | break; |
|---|
| 174 | } |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | V2=V3; |
|---|
| 178 | } |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | |
|---|
| 182 | // Betrete das SuperLeaf 'SLNr' durch das 'EnteringPortal' des vorhergehenden SuperLeafs. |
|---|
| 183 | void FindVisibleLeaves(unsigned long SLNr, const PolygonT& MasterPortal, const PolygonT& EnteringPortal) |
|---|
| 184 | { |
|---|
| 185 | FlagVisible(SLNr); |
|---|
| 186 | |
|---|
| 187 | // Das Frustum MasterPortal --> EnteringPortal bestimmen. |
|---|
| 188 | ArrayT<PlaneT> Frustum; |
|---|
| 189 | FindFrustum(MasterPortal, EnteringPortal, Frustum); |
|---|
| 190 | |
|---|
| 191 | for (unsigned long NeighbourNr=0; NeighbourNr<SuperLeaves[SLNr].Neighbours.Size(); NeighbourNr++) |
|---|
| 192 | { |
|---|
| 193 | PolygonT NextPortal=SuperLeaves[SLNr].Neighbours[NeighbourNr].SubPortal; |
|---|
| 194 | |
|---|
| 195 | // Abkürzung: Gleiche Ebene? Dann weiter mit dem nächsten Portal! |
|---|
| 196 | if (abs(PolygonWhatSide(NextPortal, EnteringPortal.Plane))==3) continue; |
|---|
| 197 | |
|---|
| 198 | // Clippe 'NextPortal' gegen das Frustum MasterPortal --> EnteringPortal. |
|---|
| 199 | static PolygonT ClipPortal; |
|---|
| 200 | |
|---|
| 201 | for (unsigned long FrustumNr=0; FrustumNr<Frustum.Size(); FrustumNr++) |
|---|
| 202 | { |
|---|
| 203 | const signed char Side=PolygonWhatSide(NextPortal, Frustum[FrustumNr]); |
|---|
| 204 | |
|---|
| 205 | if (Side==2) PolygonSplit(NextPortal, Frustum[FrustumNr], NextPortal, ClipPortal); |
|---|
| 206 | else if (Side!=1) break; |
|---|
| 207 | } |
|---|
| 208 | if (FrustumNr<Frustum.Size()) continue; |
|---|
| 209 | |
|---|
| 210 | // Clippe 'MasterPortal' gegen das Frustum NextPortal --> EnteringPortal. |
|---|
| 211 | // Um die Portale nicht spiegeln zu müssen, das gespiegelte Frustum benutzen! |
|---|
| 212 | ArrayT<PlaneT> Frustum2; |
|---|
| 213 | FindFrustum(EnteringPortal, NextPortal, Frustum2); |
|---|
| 214 | |
|---|
| 215 | PolygonT MP=MasterPortal; |
|---|
| 216 | |
|---|
| 217 | for (FrustumNr=0; FrustumNr<Frustum2.Size(); FrustumNr++) |
|---|
| 218 | { |
|---|
| 219 | const signed char Side=PolygonWhatSide(MP, Frustum2[FrustumNr]); |
|---|
| 220 | |
|---|
| 221 | if (Side== 2) PolygonSplit(MP, Frustum2[FrustumNr], ClipPortal, MP); |
|---|
| 222 | else if (Side!=-1) break; |
|---|
| 223 | } |
|---|
| 224 | if (FrustumNr<Frustum2.Size()) continue; |
|---|
| 225 | |
|---|
| 226 | FindVisibleLeaves(SuperLeaves[SLNr].Neighbours[NeighbourNr].SuperLeafNr, MP, NextPortal); |
|---|
| 227 | } |
|---|
| 228 | } |
|---|
| 229 | |
|---|
| 230 | |
|---|
| 231 | void BuildPVS() |
|---|
| 232 | { |
|---|
| 233 | Win32Console.FunctionID("Potentially Visibility Set"); |
|---|
| 234 | |
|---|
| 235 | // Für jedes SuperLeaf das PVS bestimmen. |
|---|
| 236 | for (MasterSuperLeafNr=0; MasterSuperLeafNr<SuperLeaves.Size(); MasterSuperLeafNr++) |
|---|
| 237 | { |
|---|
| 238 | Win32Console.RefreshStatusLine((double)MasterSuperLeafNr/SuperLeaves.Size()); |
|---|
| 239 | |
|---|
| 240 | FlagVisible(MasterSuperLeafNr); |
|---|
| 241 | |
|---|
| 242 | // Für den alten Algorithmus war hier vermerkt, daß "outer leaves" keine Neighbours/Portals haben, |
|---|
| 243 | // wegen der Eigenschaften von Portalize() und FillInside() des CaBSP Programms. |
|---|
| 244 | // Der neue Algorithmus verwendet SuperLeaves, bei denen überhaupt nicht zwischen "inner" und "outer" unterschieden wird. |
|---|
| 245 | // Alles was zählt, sind die Portale. Deshalb setzen sich SuperLeaves korrekt aus beliebigen Leaves eines Sub-Trees zusammen. |
|---|
| 246 | for (unsigned long NeighbourNr=0; NeighbourNr<SuperLeaves[MasterSuperLeafNr].Neighbours.Size(); NeighbourNr++) |
|---|
| 247 | { |
|---|
| 248 | const unsigned long NeighbourSLNr=SuperLeaves[MasterSuperLeafNr].Neighbours[NeighbourNr].SuperLeafNr; |
|---|
| 249 | const PolygonT& MasterPortal =SuperLeaves[MasterSuperLeafNr].Neighbours[NeighbourNr].SubPortal; |
|---|
| 250 | |
|---|
| 251 | // Der unmittelbare Nachbar ist auf jeden Fall sichtbar. |
|---|
| 252 | FlagVisible(NeighbourSLNr); |
|---|
| 253 | |
|---|
| 254 | for (unsigned long NNr=0; NNr<SuperLeaves[NeighbourSLNr].Neighbours.Size(); NNr++) |
|---|
| 255 | { |
|---|
| 256 | const unsigned long NeighboursNeighbourSLNr=SuperLeaves[NeighbourSLNr].Neighbours[NNr].SuperLeafNr; |
|---|
| 257 | const PolygonT& EnteringPortal =SuperLeaves[NeighbourSLNr].Neighbours[NNr].SubPortal; |
|---|
| 258 | |
|---|
| 259 | if (abs(PolygonWhatSide(MasterPortal, EnteringPortal.Plane))==3) continue; |
|---|
| 260 | |
|---|
| 261 | FindVisibleLeaves(NeighboursNeighbourSLNr, MasterPortal, EnteringPortal); |
|---|
| 262 | } |
|---|
| 263 | } |
|---|
| 264 | } |
|---|
| 265 | |
|---|
| 266 | printf("SuperLeavesPVS : done\n"); |
|---|
| 267 | } |
|---|
| 268 | |
|---|
| 269 | |
|---|
| 270 | void WriteLogFileEntry(const char* WorldPathName, unsigned long CheckSum) |
|---|
| 271 | { |
|---|
| 272 | FILE* LogFile=fopen("CaPVS.log", "a"); |
|---|
| 273 | |
|---|
| 274 | if (LogFile) |
|---|
| 275 | { |
|---|
| 276 | SYSTEMTIME SystemTime; |
|---|
| 277 | char WorldName[_MAX_FNAME]; |
|---|
| 278 | char ComputerName[MAX_COMPUTERNAME_LENGTH+1]="unknown"; |
|---|
| 279 | unsigned long LengthCN =MAX_COMPUTERNAME_LENGTH+1; |
|---|
| 280 | unsigned long RunningSec=(unsigned long)difftime(time(NULL), ProgramStartTime); |
|---|
| 281 | |
|---|
| 282 | GetLocalTime(&SystemTime); |
|---|
| 283 | GetComputerName(ComputerName, &LengthCN); |
|---|
| 284 | _splitpath(WorldPathName, NULL, NULL, WorldName, NULL); |
|---|
| 285 | |
|---|
| 286 | // Date, Time, WorldName, TimeForCompletion on [ComputerName] |
|---|
| 287 | fprintf(LogFile, "%02u.%02u.%02u %2u:%02u %-16s%3u:%02u:%02u [%-16s] %10u\n", |
|---|
| 288 | SystemTime.wDay, SystemTime.wMonth, SystemTime.wYear % 100, SystemTime.wHour, SystemTime.wMinute, |
|---|
| 289 | WorldName, RunningSec/3600, (RunningSec/60) % 60, RunningSec % 60, ComputerName, CheckSum); |
|---|
| 290 | fclose(LogFile); |
|---|
| 291 | } |
|---|
| 292 | } |
|---|
| 293 | |
|---|
| 294 | |
|---|
| 295 | void Usage() |
|---|
| 296 | { |
|---|
| 297 | printf("\n"); |
|---|
| 298 | printf("USAGE: CaPVS Path/WorldName.cw [OPTIONS]\n"); |
|---|
| 299 | printf("\n"); |
|---|
| 300 | printf("OPTIONS:\n"); |
|---|
| 301 | printf("-maxRecDepthSL n : Leaves that are stored in the BSP tree and have a greater\n"); |
|---|
| 302 | printf(" depth than this value get combined in a common SuperLeaf\n"); |
|---|
| 303 | printf(" that is created at this depth value.\n"); |
|---|
| 304 | printf("-minAreaSL a : BSP sub-trees whose non-detail faces have a common area of\n"); |
|---|
| 305 | printf(" less than this value get combined in a SuperLeaf.\n"); |
|---|
| 306 | printf("-onlySLs : Do not compute the PVS, just print out how many SuperLeaves\n"); |
|---|
| 307 | printf(" would be created. Used for estimating the above parameter\n"); |
|---|
| 308 | printf(" values (speed vs. quality).\n"); |
|---|
| 309 | printf("\n"); |
|---|
| 310 | |
|---|
| 311 | exit(1); |
|---|
| 312 | } |
|---|
| 313 | |
|---|
| 314 | |
|---|
| 315 | int main(int ArgC, const char* ArgV[]) |
|---|
| 316 | { |
|---|
| 317 | unsigned long MaxRecDepthSL =0xFFFFFFFF; |
|---|
| 318 | double MinAreaSL =0.0; |
|---|
| 319 | bool OnlySuperLeaves=false; |
|---|
| 320 | |
|---|
| 321 | Win32Console.InitScreen("Cafu Potentially Visibility Set Utility, Version 04", __DATE__); |
|---|
| 322 | |
|---|
| 323 | if (ArgC<2) Usage(); |
|---|
| 324 | |
|---|
| 325 | for (int ArgNr=2; ArgNr<ArgC; ArgNr++) |
|---|
| 326 | { |
|---|
| 327 | if (!stricmp(ArgV[ArgNr], "-maxRecDepthSL")) |
|---|
| 328 | { |
|---|
| 329 | ArgNr++; |
|---|
| 330 | if (ArgNr>=ArgC) Usage(); |
|---|
| 331 | |
|---|
| 332 | MaxRecDepthSL=atoi(ArgV[ArgNr]); |
|---|
| 333 | printf("maxRecDepthSL == %u\n", MaxRecDepthSL); |
|---|
| 334 | } |
|---|
| 335 | else if (!stricmp(ArgV[ArgNr], "-minAreaSL")) |
|---|
| 336 | { |
|---|
| 337 | ArgNr++; |
|---|
| 338 | if (ArgNr>=ArgC) Usage(); |
|---|
| 339 | |
|---|
| 340 | MinAreaSL=atof(ArgV[ArgNr]); |
|---|
| 341 | if (MinAreaSL<0.0) MinAreaSL=0.0; |
|---|
| 342 | printf("minAreaSL == %.3f\n", MinAreaSL); |
|---|
| 343 | } |
|---|
| 344 | else if (!stricmp(ArgV[ArgNr], "-onlySLs")) |
|---|
| 345 | { |
|---|
| 346 | OnlySuperLeaves=true; |
|---|
| 347 | } |
|---|
| 348 | else |
|---|
| 349 | { |
|---|
| 350 | printf("Unknown option '%s'.\n", ArgV[ArgNr]); |
|---|
| 351 | Usage(); |
|---|
| 352 | } |
|---|
| 353 | } |
|---|
| 354 | |
|---|
| 355 | try |
|---|
| 356 | { |
|---|
| 357 | printf("Load World %s\n", ArgV[1]); |
|---|
| 358 | |
|---|
| 359 | CaPVSWorldT* CaPVSWorld=new CaPVSWorldT(ArgV[1], MaxRecDepthSL, MinAreaSL); |
|---|
| 360 | |
|---|
| 361 | // Robustness against the 'sharp wedge' problem is granted: |
|---|
| 362 | // DetermineAdjacencyGraph() is the only place where PolygonOverlap() is called, but only once on *input* portals. |
|---|
| 363 | // Everywhere else (especially in BuildPVS() and its sub-functions), only PolygonSplit() and PolygonWhatSide() are called. |
|---|
| 364 | CaPVSWorld->CreateSuperLeaves(SuperLeaves); |
|---|
| 365 | if (OnlySuperLeaves) return 0; |
|---|
| 366 | DetermineAdjacencyGraph(); |
|---|
| 367 | |
|---|
| 368 | // SuperLeavesPVS erzeugen und zurücksetzen (völlige Blindheit). |
|---|
| 369 | SuperLeavesPVS.PushBackEmpty((SuperLeaves.Size()*SuperLeaves.Size()+31)/32); |
|---|
| 370 | for (unsigned long Vis=0; Vis<SuperLeavesPVS.Size(); Vis++) SuperLeavesPVS[Vis]=0; |
|---|
| 371 | |
|---|
| 372 | // Berechne das PVS der SuperLeaves. |
|---|
| 373 | BuildPVS(); |
|---|
| 374 | |
|---|
| 375 | // 'SuperLeavesPVS' ins PVS der 'CaPVSWorld' übertragen. |
|---|
| 376 | CaPVSWorld->StorePVS(SuperLeaves, SuperLeavesPVS); |
|---|
| 377 | |
|---|
| 378 | // Drucke Statistiken aus und erhalte eine Prüfsumme zurück. |
|---|
| 379 | unsigned long CheckSum=CaPVSWorld->GetChecksumAndPrintStats(); |
|---|
| 380 | |
|---|
| 381 | // Speichere die World zurück auf Disk. |
|---|
| 382 | Win32Console.FunctionID("Save World %s", ArgV[1]); |
|---|
| 383 | CaPVSWorld->SaveToDisk(ArgV[1]); |
|---|
| 384 | |
|---|
| 385 | delete CaPVSWorld; |
|---|
| 386 | WriteLogFileEntry(ArgV[1], CheckSum); |
|---|
| 387 | printf("COMPLETED.\n"); |
|---|
| 388 | } |
|---|
| 389 | catch (const WorldT::LoadErrorT& E) { Win32Console.Error(E.Msg); } |
|---|
| 390 | catch (const WorldT::SaveErrorT& E) { Win32Console.Error(E.Msg); } |
|---|
| 391 | |
|---|
| 392 | return 0; |
|---|
| 393 | } |
|---|