| 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 Binary Space Partitioning Utility ***/ |
|---|
| 25 | /*** ***/ |
|---|
| 26 | /*** I think that I shall never see ***/ |
|---|
| 27 | /*** a poem lovely as a tree ***/ |
|---|
| 28 | /*** ***/ |
|---|
| 29 | /**********************************************/ |
|---|
| 30 | |
|---|
| 31 | #ifdef _WIN32 |
|---|
| 32 | #define WIN32_LEAN_AND_MEAN |
|---|
| 33 | #include <windows.h> |
|---|
| 34 | #else |
|---|
| 35 | #include <stdarg.h> |
|---|
| 36 | #include <unistd.h> |
|---|
| 37 | #include <string.h> |
|---|
| 38 | #define _stricmp strcasecmp |
|---|
| 39 | #endif |
|---|
| 40 | |
|---|
| 41 | #include <time.h> |
|---|
| 42 | #include <ctype.h> |
|---|
| 43 | #include <stdio.h> |
|---|
| 44 | |
|---|
| 45 | #include "ConsoleCommands/Console.hpp" |
|---|
| 46 | #include "ConsoleCommands/ConsoleInterpreter.hpp" |
|---|
| 47 | #include "ConsoleCommands/ConsoleStdout.hpp" |
|---|
| 48 | #include "FileSys/FileManImpl.hpp" |
|---|
| 49 | #include "Templates/Array.hpp" |
|---|
| 50 | #include "MaterialSystem/Material.hpp" |
|---|
| 51 | #include "MaterialSystem/MaterialManager.hpp" |
|---|
| 52 | #include "MaterialSystem/MaterialManagerImpl.hpp" |
|---|
| 53 | #include "ClipSys/CollisionModelMan_impl.hpp" |
|---|
| 54 | |
|---|
| 55 | #include "BspTreeBuilder/BspTreeBuilder.hpp" |
|---|
| 56 | |
|---|
| 57 | #if defined(_WIN32) |
|---|
| 58 | #if defined(_MSC_VER) |
|---|
| 59 | #define vsnprintf _vsnprintf |
|---|
| 60 | #if (_MSC_VER<1300) |
|---|
| 61 | #define for if (false) ; else for |
|---|
| 62 | #endif |
|---|
| 63 | #endif |
|---|
| 64 | #endif |
|---|
| 65 | |
|---|
| 66 | |
|---|
| 67 | static cf::ConsoleStdoutT ConsoleStdout(true); // Enable auto-flushing the stdout stream for CaBSP. |
|---|
| 68 | cf::ConsoleI* Console=&ConsoleStdout; |
|---|
| 69 | |
|---|
| 70 | static cf::FileSys::FileManImplT FileManImpl; |
|---|
| 71 | cf::FileSys::FileManI* cf::FileSys::FileMan=&FileManImpl; |
|---|
| 72 | |
|---|
| 73 | static cf::ClipSys::CollModelManImplT CCM; |
|---|
| 74 | cf::ClipSys::CollModelManI* cf::ClipSys::CollModelMan=&CCM; |
|---|
| 75 | |
|---|
| 76 | ConsoleInterpreterI* ConsoleInterpreter=NULL; |
|---|
| 77 | MaterialManagerI* MaterialManager =NULL; |
|---|
| 78 | |
|---|
| 79 | |
|---|
| 80 | const time_t ProgramStartTime=time(NULL); |
|---|
| 81 | |
|---|
| 82 | // Returns a string with the elapsed time since program start. |
|---|
| 83 | // The string is in the format "hh:mm:ss". |
|---|
| 84 | const char* GetTimeSinceProgramStart() |
|---|
| 85 | { |
|---|
| 86 | const unsigned long TotalSec=(unsigned long)difftime(time(NULL), ProgramStartTime); |
|---|
| 87 | const unsigned long Sec =TotalSec % 60; |
|---|
| 88 | const unsigned long Min =(TotalSec/60) % 60; |
|---|
| 89 | const unsigned long Hour =TotalSec/3600; |
|---|
| 90 | |
|---|
| 91 | static char TimeString[16]; |
|---|
| 92 | sprintf(TimeString, "%2lu:%2lu:%2lu", Hour, Min, Sec); |
|---|
| 93 | |
|---|
| 94 | return TimeString; |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | |
|---|
| 98 | static void Error(const char* ErrorText, ...) |
|---|
| 99 | { |
|---|
| 100 | va_list ArgList; |
|---|
| 101 | char ErrorString[256]; |
|---|
| 102 | |
|---|
| 103 | if (ErrorText!=NULL) |
|---|
| 104 | { |
|---|
| 105 | va_start(ArgList, ErrorText); |
|---|
| 106 | vsnprintf(ErrorString, 256, ErrorText, ArgList); |
|---|
| 107 | va_end(ArgList); |
|---|
| 108 | |
|---|
| 109 | Console->Print(cf::va("\nFATAL ERROR: %s\n", ErrorString)); |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | Console->Print("Program aborted.\n\n"); |
|---|
| 113 | exit(1); |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | |
|---|
| 117 | static void WriteLogFileEntry(const char* WorldPathName) |
|---|
| 118 | { |
|---|
| 119 | char DateTime [256]="unknown"; |
|---|
| 120 | char HostName [256]="unknown"; |
|---|
| 121 | char WorldName[256]="unknown"; |
|---|
| 122 | time_t Time =time(NULL); |
|---|
| 123 | unsigned long RunningSec =(unsigned long)difftime(Time, ProgramStartTime); |
|---|
| 124 | FILE* LogFile =fopen("CaBSP.log", "a"); |
|---|
| 125 | |
|---|
| 126 | if (!LogFile) return; |
|---|
| 127 | |
|---|
| 128 | strftime(DateTime, 256, "%d.%m.%Y %H:%M", localtime(&Time)); |
|---|
| 129 | DateTime[255]=0; |
|---|
| 130 | |
|---|
| 131 | #ifdef _WIN32 |
|---|
| 132 | unsigned long Dummy=256; |
|---|
| 133 | if (!GetComputerName(HostName, &Dummy)) sprintf(HostName, "unknown (look-up failed)."); |
|---|
| 134 | #else |
|---|
| 135 | // This function also works on Windows, but sadly requires calls to 'WSAStartup()' and 'WSACleanup()'. |
|---|
| 136 | if (gethostname(HostName, 256)) sprintf(HostName, "unknown (look-up failed)."); |
|---|
| 137 | #endif |
|---|
| 138 | HostName[255]=0; |
|---|
| 139 | |
|---|
| 140 | if (WorldPathName) |
|---|
| 141 | { |
|---|
| 142 | // Dateinamen abtrennen (mit Extension). |
|---|
| 143 | size_t i=strlen(WorldPathName); |
|---|
| 144 | |
|---|
| 145 | while (i>0 && WorldPathName[i-1]!='/' && WorldPathName[i-1]!='\\') i--; |
|---|
| 146 | strncpy(WorldName, WorldPathName+i, 256); |
|---|
| 147 | WorldName[255]=0; |
|---|
| 148 | |
|---|
| 149 | // Extension abtrennen. |
|---|
| 150 | i=strlen(WorldName); |
|---|
| 151 | |
|---|
| 152 | while (i>0 && WorldName[i-1]!='.') i--; |
|---|
| 153 | if (i>0) WorldName[i-1]=0; |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | // Date, Time, WorldName, TimeForCompletion on [HostName] |
|---|
| 157 | fprintf(LogFile, "%-16s %-16s%3lu:%02lu:%02lu [%-16s]\n", DateTime, WorldName, RunningSec/3600, (RunningSec/60) % 60, RunningSec % 60, HostName); |
|---|
| 158 | fclose(LogFile); |
|---|
| 159 | } |
|---|
| 160 | |
|---|
| 161 | |
|---|
| 162 | void Usage() |
|---|
| 163 | { |
|---|
| 164 | Console->Print("USAGE: CaBSP InFile.cmap OutFile.cw [OPTIONS]\n"); |
|---|
| 165 | Console->Print("\n"); |
|---|
| 166 | // Console->Print("OPTIONS:\n"); |
|---|
| 167 | // Console->Print("-putWadInCW WadFile : Place textures used from WAD specified into CW.\n"); |
|---|
| 168 | // Console->Print("-p WadFile : Short form of '-putWadInCW'. Recommended when the\n"); |
|---|
| 169 | // Console->Print(" limited length of the command line is a problem.\n"); |
|---|
| 170 | // Console->Print("\n"); |
|---|
| 171 | Console->Print("Please note that all file names must include their paths and suffixes!\n"); |
|---|
| 172 | // Console->Print("WAD file names are partially name-matched, case insensitive.\n"); |
|---|
| 173 | |
|---|
| 174 | // The most simple tree means that there is no leak detection and no attempt to fill the world. |
|---|
| 175 | |
|---|
| 176 | exit(1); |
|---|
| 177 | } |
|---|
| 178 | |
|---|
| 179 | |
|---|
| 180 | #include "../Common/World.hpp" |
|---|
| 181 | #include "LoadWorld.cpp" |
|---|
| 182 | |
|---|
| 183 | |
|---|
| 184 | int main(int ArgC, const char* ArgV[]) |
|---|
| 185 | { |
|---|
| 186 | bool Option_MostSimpleTree =false; |
|---|
| 187 | bool Option_MinimizeFaceSplits=false; |
|---|
| 188 | |
|---|
| 189 | Console->Print(cf::va("\n*** Cafu Binary Space Partitioning Utility, Version 10a (%s) ***\n\n", __DATE__)); |
|---|
| 190 | |
|---|
| 191 | |
|---|
| 192 | // Initialize the FileMan by mounting the default file system. |
|---|
| 193 | // Note that specifying "./" (instead of "") as the file system description effectively prevents the use of |
|---|
| 194 | // absolute paths like "D:\abc\someDir\someFile.xy" or "/usr/bin/xy". This however should be fine for this application. |
|---|
| 195 | cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_LOCAL_PATH, "./", ""); |
|---|
| 196 | cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/TechDemo.zip", "Games/DeathMatch/Textures/TechDemo/", "Ca3DE"); |
|---|
| 197 | cf::FileSys::FileMan->MountFileSystem(cf::FileSys::FS_TYPE_ZIP_ARCHIVE, "Games/DeathMatch/Textures/SkyDomes.zip", "Games/DeathMatch/Textures/SkyDomes/", "Ca3DE"); |
|---|
| 198 | |
|---|
| 199 | // Parse the command-line. |
|---|
| 200 | // Currently, no extension-checking or other comfort for file names is provided. |
|---|
| 201 | if (ArgC<3) Usage(); |
|---|
| 202 | |
|---|
| 203 | for (int ArgNr=3; ArgNr<ArgC; ArgNr++) |
|---|
| 204 | { |
|---|
| 205 | if (!_stricmp(ArgV[ArgNr], "-putWadInCW") || !_stricmp(ArgV[ArgNr], "-p")) |
|---|
| 206 | { |
|---|
| 207 | ArgNr++; |
|---|
| 208 | if (ArgNr>=ArgC) Usage(); |
|---|
| 209 | |
|---|
| 210 | Console->Print(cf::va("NOTE: %s is obsolete now. Ignored \"%s %s\".\n", ArgV[ArgNr-1], ArgV[ArgNr-1], ArgV[ArgNr])); |
|---|
| 211 | } |
|---|
| 212 | else if (!_stricmp(ArgV[ArgNr], "-mostSimpleTree" ) || !_stricmp(ArgV[ArgNr], "-mst")) { Option_MostSimpleTree =true; } |
|---|
| 213 | else if (!_stricmp(ArgV[ArgNr], "-minimizeFaceSplits") || !_stricmp(ArgV[ArgNr], "-mfs")) |
|---|
| 214 | { |
|---|
| 215 | Console->Print("\n*** WARNING: This option will cause the Cafu engine to render many faces\n"); |
|---|
| 216 | Console->Print("*** *TWICE*! Therefore, it's use it currently highly discouraged.\n\n"); |
|---|
| 217 | Option_MinimizeFaceSplits=true; |
|---|
| 218 | } |
|---|
| 219 | else if (ArgV[ArgNr][0]==0) |
|---|
| 220 | { |
|---|
| 221 | // The argument is "", the empty string. |
|---|
| 222 | // This can happen under Linux, when CaBSP is called via wxExecute() with white-space trailing the command string. |
|---|
| 223 | } |
|---|
| 224 | else |
|---|
| 225 | { |
|---|
| 226 | Console->Print(cf::va("Unknown option '%s'.\n", ArgV[ArgNr])); |
|---|
| 227 | Usage(); |
|---|
| 228 | } |
|---|
| 229 | } |
|---|
| 230 | |
|---|
| 231 | |
|---|
| 232 | std::string GameDirectory=ArgV[2]; |
|---|
| 233 | |
|---|
| 234 | // Determine the game directory, cleverly assuming that the destination file is in "Worlds". |
|---|
| 235 | { |
|---|
| 236 | // Strip the file name and extention off. |
|---|
| 237 | size_t i=GameDirectory.find_last_of("/\\"); |
|---|
| 238 | |
|---|
| 239 | GameDirectory=GameDirectory.substr(0, i==std::string::npos ? 0 : i)+"/.."; |
|---|
| 240 | } |
|---|
| 241 | |
|---|
| 242 | |
|---|
| 243 | // Setup the global MaterialManager pointer. |
|---|
| 244 | static MaterialManagerImplT MatManImpl; |
|---|
| 245 | |
|---|
| 246 | MaterialManager=&MatManImpl; |
|---|
| 247 | |
|---|
| 248 | if (MaterialManager->RegisterMaterialScriptsInDir(GameDirectory+"/Materials", GameDirectory+"/").Size()==0) |
|---|
| 249 | { |
|---|
| 250 | Console->Print(std::string("\nNo materials found in scripts in \"")+GameDirectory+"/Materials\".\n"); |
|---|
| 251 | Error("No materials found."); |
|---|
| 252 | } |
|---|
| 253 | |
|---|
| 254 | |
|---|
| 255 | ModelManagerT ModelMan; |
|---|
| 256 | WorldT World; |
|---|
| 257 | ArrayT<VectorT> DrawWorldOutsidePointSamples; |
|---|
| 258 | |
|---|
| 259 | LoadWorld(ArgV[1], GameDirectory, ModelMan, World, DrawWorldOutsidePointSamples); |
|---|
| 260 | |
|---|
| 261 | // What we need: |
|---|
| 262 | // For each entity: The BspTree itself, OutsidePointSamples, FloodFillSources. |
|---|
| 263 | // One common instance, shared for all: LeakDetectMat |
|---|
| 264 | BspTreeBuilderT BspTreeBuilder(World.BspTree, Option_MostSimpleTree, Option_MinimizeFaceSplits); |
|---|
| 265 | |
|---|
| 266 | ArrayT<Vector3dT> FloodFillSources; |
|---|
| 267 | for (unsigned long IPSNr=0; IPSNr<World.InfoPlayerStarts.Size(); IPSNr++) |
|---|
| 268 | FloodFillSources.PushBack(World.InfoPlayerStarts[IPSNr].Origin); |
|---|
| 269 | |
|---|
| 270 | BspTreeBuilder.Build(true /*yes, this is the worldspawn entity*/, FloodFillSources, DrawWorldOutsidePointSamples, ArgV[1]); |
|---|
| 271 | |
|---|
| 272 | try |
|---|
| 273 | { |
|---|
| 274 | Console->Print(cf::va("\n%-50s %s\n", "*** Save World ***", GetTimeSinceProgramStart())); |
|---|
| 275 | Console->Print(std::string(ArgV[2])+"\n"); |
|---|
| 276 | World.SaveToDisk(ArgV[2]); |
|---|
| 277 | |
|---|
| 278 | WriteLogFileEntry(ArgV[2]); |
|---|
| 279 | Console->Print(cf::va("\n%-50s %s\n", "COMPLETED.", GetTimeSinceProgramStart())); |
|---|
| 280 | } |
|---|
| 281 | catch (const WorldT::SaveErrorT& E) { Error(E.Msg); } |
|---|
| 282 | |
|---|
| 283 | return 0; |
|---|
| 284 | } |
|---|