| 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 | #include "ChildFrame.hpp" |
|---|
| 23 | #include "GameConfig.hpp" |
|---|
| 24 | #include "ParentFrame.hpp" |
|---|
| 25 | #include "MapDocument.hpp" |
|---|
| 26 | #include "Options.hpp" |
|---|
| 27 | #include "DialogOptions.hpp" |
|---|
| 28 | |
|---|
| 29 | #include "GuiEditor/ChildFrame.hpp" |
|---|
| 30 | #include "GuiEditor/GuiDocument.hpp" |
|---|
| 31 | #include "FontWizard/FontWizard.hpp" |
|---|
| 32 | #include "ModelEditor/ChildFrame.hpp" |
|---|
| 33 | #include "ModelEditor/ModelDocument.hpp" |
|---|
| 34 | |
|---|
| 35 | #include "ConsoleCommands/Console.hpp" |
|---|
| 36 | #include "FileSys/FileManImpl.hpp" |
|---|
| 37 | #include "GuiSys/GuiImpl.hpp" // Needed to catch InitErrorT if GUI document creation fails. |
|---|
| 38 | #include "MaterialSystem/MapComposition.hpp" |
|---|
| 39 | #include "MaterialSystem/Renderer.hpp" |
|---|
| 40 | #include "MaterialSystem/TextureMap.hpp" |
|---|
| 41 | #include "Models/Loader.hpp" // Needed for the ModelLoaderT::LoadErrorT exception that must be caught when model loading failed. |
|---|
| 42 | #include "TextParser/TextParser.hpp" |
|---|
| 43 | #include "PlatformAux.hpp" |
|---|
| 44 | |
|---|
| 45 | #include "wx/wx.h" |
|---|
| 46 | #include "wx/aboutdlg.h" |
|---|
| 47 | #include "wx/busyinfo.h" |
|---|
| 48 | #include "wx/cmdline.h" |
|---|
| 49 | #include "wx/confbase.h" |
|---|
| 50 | #include "wx/dir.h" |
|---|
| 51 | #include "wx/filename.h" |
|---|
| 52 | #include "wx/glcanvas.h" |
|---|
| 53 | #include "wx/numdlg.h" |
|---|
| 54 | #include "wx/progdlg.h" |
|---|
| 55 | #include "wx/stdpaths.h" |
|---|
| 56 | #include "wx/utils.h" |
|---|
| 57 | |
|---|
| 58 | #include <fstream> |
|---|
| 59 | |
|---|
| 60 | #ifdef _WIN32 |
|---|
| 61 | #define WIN32_LEAN_AND_MEAN |
|---|
| 62 | #include <windows.h> |
|---|
| 63 | #include <direct.h> |
|---|
| 64 | #elif __linux__ |
|---|
| 65 | #include <dirent.h> |
|---|
| 66 | #include <dlfcn.h> |
|---|
| 67 | #define __stdcall |
|---|
| 68 | #define GetProcAddress dlsym |
|---|
| 69 | #define FreeLibrary dlclose |
|---|
| 70 | #endif |
|---|
| 71 | |
|---|
| 72 | |
|---|
| 73 | BEGIN_EVENT_TABLE(ParentFrameT, wxMDIParentFrame) |
|---|
| 74 | #ifdef __WXGTK__ |
|---|
| 75 | EVT_SIZE(ParentFrameT::OnSize) |
|---|
| 76 | #endif |
|---|
| 77 | EVT_SHOW(ParentFrameT::OnShow) |
|---|
| 78 | EVT_CLOSE(ParentFrameT::OnClose) |
|---|
| 79 | EVT_MENU_RANGE(ID_MENU_FILE_NEW_MAP, ID_MENU_FILE_EXIT, ParentFrameT::OnMenuFile) |
|---|
| 80 | EVT_MENU_RANGE(wxID_FILE1, wxID_FILE9, ParentFrameT::OnMenuFile) |
|---|
| 81 | EVT_MENU_RANGE(ID_MENU_HELP_CONTENTS, ID_MENU_HELP_ABOUT, ParentFrameT::OnMenuHelp) |
|---|
| 82 | END_EVENT_TABLE() |
|---|
| 83 | |
|---|
| 84 | |
|---|
| 85 | int ParentFrameT::OpenGLAttributeList[]= |
|---|
| 86 | { |
|---|
| 87 | WX_GL_RGBA, |
|---|
| 88 | WX_GL_DOUBLEBUFFER, |
|---|
| 89 | WX_GL_MIN_RED, 8, |
|---|
| 90 | WX_GL_MIN_GREEN, 8, |
|---|
| 91 | WX_GL_MIN_BLUE, 8, |
|---|
| 92 | WX_GL_MIN_ALPHA, 8, |
|---|
| 93 | WX_GL_DEPTH_SIZE, 16, |
|---|
| 94 | WX_GL_STENCIL_SIZE, 8, |
|---|
| 95 | 0 // Zero indicates the end of the array. |
|---|
| 96 | }; |
|---|
| 97 | |
|---|
| 98 | |
|---|
| 99 | ParentFrameT::ParentFrameT(wxCmdLineParser& Parser) |
|---|
| 100 | #ifdef DEBUG |
|---|
| 101 | // We need the wxMAXIMIZE window style here, or else the window is not properly maximized (looks like a bug?). |
|---|
| 102 | : wxMDIParentFrame(NULL /*parent*/, -1 /*id*/, wxString("Cafu World Editor ") + "[DEBUG] - " + __DATE__, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxMAXIMIZE), |
|---|
| 103 | #else |
|---|
| 104 | // We need the wxMAXIMIZE window style here, or else the window is not properly maximized (looks like a bug?). |
|---|
| 105 | : wxMDIParentFrame(NULL /*parent*/, -1 /*id*/, wxString("Cafu World Editor - ") + __DATE__, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxMAXIMIZE), |
|---|
| 106 | #endif |
|---|
| 107 | m_GLCanvas(NULL), |
|---|
| 108 | m_GLContext(NULL), |
|---|
| 109 | m_WhiteTexture(NULL), |
|---|
| 110 | m_CmdLineParser(Parser), |
|---|
| 111 | m_RendererDLL(NULL) |
|---|
| 112 | { |
|---|
| 113 | wxMenuBar *item0 = new wxMenuBar; |
|---|
| 114 | |
|---|
| 115 | wxMenu* item1 = new wxMenu; |
|---|
| 116 | |
|---|
| 117 | wxMenu* NewMenu = new wxMenu; |
|---|
| 118 | |
|---|
| 119 | NewMenu->Append(ID_MENU_FILE_NEW_MAP, wxT("New &Map\tCtrl+N"), wxT("")); |
|---|
| 120 | NewMenu->Append(ID_MENU_FILE_NEW_MODEL, wxT("New M&odel\tCtrl+Shift+N"), wxT("")); |
|---|
| 121 | NewMenu->Append(ID_MENU_FILE_NEW_GUI, wxT("New &GUI\tCtrl+Alt+N"), wxT("")); |
|---|
| 122 | NewMenu->Append(ID_MENU_FILE_NEW_FONT, wxT("New &Font"), wxT("")); |
|---|
| 123 | |
|---|
| 124 | item1->AppendSubMenu(NewMenu, wxT("&New")); |
|---|
| 125 | item1->Append(ID_MENU_FILE_OPEN, wxT("&Open...\tCtrl+O"), wxT("")); |
|---|
| 126 | |
|---|
| 127 | item1->AppendSeparator(); |
|---|
| 128 | item1->Append(ID_MENU_FILE_CONFIGURE, wxT("Conf&igure CaWE..."), wxT("") ); |
|---|
| 129 | item1->Append(ID_MENU_FILE_EXIT, wxT("E&xit"), wxT("") ); |
|---|
| 130 | m_FileHistory.Load(*wxConfigBase::Get()); |
|---|
| 131 | m_FileHistory.UseMenu(item1); |
|---|
| 132 | m_FileHistory.AddFilesToMenu(item1); |
|---|
| 133 | item0->Append(item1, wxT("&File") ); |
|---|
| 134 | |
|---|
| 135 | wxMenu* item2 = new wxMenu; |
|---|
| 136 | item2->Append(ID_MENU_HELP_CONTENTS, wxT("&CaWE Help\tF1"), wxT("") ); |
|---|
| 137 | item2->AppendSeparator(); |
|---|
| 138 | item2->Append(ID_MENU_HELP_CAFU_WEBSITE, wxT("Cafu &Website"), wxT("") ); |
|---|
| 139 | item2->Append(ID_MENU_HELP_CAFU_FORUM, wxT("Cafu &Forum"), wxT("") ); |
|---|
| 140 | item2->AppendSeparator(); |
|---|
| 141 | if (wxConfigBase::Get()->Read("General/Activate Hidden", 0L)==0x1978) |
|---|
| 142 | { |
|---|
| 143 | // "Secret, hidden" CaWE functions... |
|---|
| 144 | item2->Append(ID_MENU_HELP_SET_FRAME_SIZE, wxT("Set frame size..."), wxT("Set the size of the parent frame. Useful for taking screen-shots.") ); |
|---|
| 145 | item2->Append(ID_MENU_HELP_D3_MTR_CONVERTER, wxT("Doom3 materials converter"), wxT("") ); |
|---|
| 146 | item2->AppendSeparator(); |
|---|
| 147 | } |
|---|
| 148 | item2->Append(ID_MENU_HELP_ABOUT, wxT("&About..."), wxT("") ); |
|---|
| 149 | item0->Append(item2, wxT("&Help") ); |
|---|
| 150 | |
|---|
| 151 | SetMenuBar(item0); // According to the documentation of wxFrame::SetMenuBar(), this generates a call to OnSize()... |
|---|
| 152 | |
|---|
| 153 | |
|---|
| 154 | #ifdef __WXMSW__ |
|---|
| 155 | #if 0 |
|---|
| 156 | wxIconBundle Icons; |
|---|
| 157 | |
|---|
| 158 | Icons.AddIcon(wxIcon("aaaa", wxBITMAP_TYPE_ICO_RESOURCE, 48, 48)); |
|---|
| 159 | Icons.AddIcon(wxIcon("aaaa", wxBITMAP_TYPE_ICO_RESOURCE, 32, 32)); |
|---|
| 160 | Icons.AddIcon(wxIcon("aaaa", wxBITMAP_TYPE_ICO_RESOURCE, 16, 16)); |
|---|
| 161 | |
|---|
| 162 | SetIcons(Icons); |
|---|
| 163 | #else |
|---|
| 164 | SetIcon(wxIcon("aaaa", wxBITMAP_TYPE_ICO_RESOURCE)); |
|---|
| 165 | #endif |
|---|
| 166 | #endif |
|---|
| 167 | |
|---|
| 168 | |
|---|
| 169 | // Create the parent GL canvas and a GL context. |
|---|
| 170 | m_GLCanvas =new wxGLCanvas(this, -1, OpenGLAttributeList, wxPoint(600, 5), wxSize(10, 10), 0, "ParentGLCanvas"); |
|---|
| 171 | m_GLContext=new wxGLContext(m_GLCanvas); |
|---|
| 172 | |
|---|
| 173 | Maximize(); // Under wxGTK, the wxMAXIMIZE frame style does not seem to suffice... |
|---|
| 174 | Show(); // Without this, the parent frame is not shown... |
|---|
| 175 | |
|---|
| 176 | #ifdef __WXMSW__ |
|---|
| 177 | // ARGH! See my message "wx 2.9.0, Showing parent frame during app init" to wx-users |
|---|
| 178 | // at http://article.gmane.org/gmane.comp.lib.wxwindows.general/68490 for details. |
|---|
| 179 | wxShowEvent SE(0, true); |
|---|
| 180 | OnShow(SE); |
|---|
| 181 | #endif |
|---|
| 182 | } |
|---|
| 183 | |
|---|
| 184 | |
|---|
| 185 | ParentFrameT::~ParentFrameT() |
|---|
| 186 | { |
|---|
| 187 | m_FileHistory.Save(*wxConfigBase::Get()); |
|---|
| 188 | |
|---|
| 189 | // Release the resources in the game configs before releasing the material system below. |
|---|
| 190 | Options.DeleteGameConfigs(); |
|---|
| 191 | |
|---|
| 192 | // Release the Cafu Material System. |
|---|
| 193 | if (MatSys::TextureMapManager!=NULL) |
|---|
| 194 | { |
|---|
| 195 | MatSys::TextureMapManager->FreeTextureMap(m_WhiteTexture); |
|---|
| 196 | MatSys::TextureMapManager=NULL; |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | if (MatSys::Renderer!=NULL) |
|---|
| 200 | { |
|---|
| 201 | MatSys::Renderer->Release(); |
|---|
| 202 | MatSys::Renderer=NULL; |
|---|
| 203 | } |
|---|
| 204 | |
|---|
| 205 | if (m_RendererDLL!=NULL) |
|---|
| 206 | { |
|---|
| 207 | FreeLibrary(m_RendererDLL); |
|---|
| 208 | m_RendererDLL=NULL; |
|---|
| 209 | } |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | |
|---|
| 213 | ChildFrameT* ParentFrameT::GetActiveMapChildFrame() const |
|---|
| 214 | { |
|---|
| 215 | wxMDIChildFrame* ActiveChildFrame=GetActiveChild(); |
|---|
| 216 | if (ActiveChildFrame==NULL) return NULL; |
|---|
| 217 | |
|---|
| 218 | ChildFrameT* ActiveChildFrameCast=dynamic_cast<ChildFrameT*>(ActiveChildFrame); |
|---|
| 219 | |
|---|
| 220 | return ActiveChildFrameCast; |
|---|
| 221 | } |
|---|
| 222 | |
|---|
| 223 | |
|---|
| 224 | MapDocumentT* ParentFrameT::GetActiveMapDoc() const |
|---|
| 225 | { |
|---|
| 226 | ChildFrameT* ACF=GetActiveMapChildFrame(); |
|---|
| 227 | |
|---|
| 228 | if (ACF==NULL) return NULL; |
|---|
| 229 | |
|---|
| 230 | return ACF->GetDoc(); |
|---|
| 231 | } |
|---|
| 232 | |
|---|
| 233 | |
|---|
| 234 | /**********************/ |
|---|
| 235 | /*** Event Handlers ***/ |
|---|
| 236 | /**********************/ |
|---|
| 237 | |
|---|
| 238 | #ifdef __WXGTK__ |
|---|
| 239 | // This is the default behaviour under WXMSW, but apparently not under WXGTK... |
|---|
| 240 | void ParentFrameT::OnSize(wxSizeEvent& SE) |
|---|
| 241 | { |
|---|
| 242 | if (GetClientWindow()) |
|---|
| 243 | { |
|---|
| 244 | GetClientWindow()->SetSize(GetClientSize()); |
|---|
| 245 | } |
|---|
| 246 | } |
|---|
| 247 | #endif |
|---|
| 248 | |
|---|
| 249 | |
|---|
| 250 | void ParentFrameT::OnShow(wxShowEvent& SE) |
|---|
| 251 | { |
|---|
| 252 | if (SE.IsShown() && m_RendererDLL==NULL) |
|---|
| 253 | { |
|---|
| 254 | // Initialize the Material System. |
|---|
| 255 | // This code is in this place due to a few peculiarities of OpenGL under GTK that do not exist under MSW: |
|---|
| 256 | // - First, an OpenGL context can only be made current with a canvas that is shown on the screen. |
|---|
| 257 | // - Second, calling Show() in the ctor above doesn't show the frame immediately - that requires |
|---|
| 258 | // getting back to the main event loop first. |
|---|
| 259 | // Consequently, the first and best opportunity for initializing the MatSys is here. |
|---|
| 260 | wxASSERT(m_GLCanvas->IsShownOnScreen()); |
|---|
| 261 | |
|---|
| 262 | // If this was in the ctor, it would trigger an assertion in debug build and yield an invalid (unusable) |
|---|
| 263 | // OpenGL context in release builds (the GL code in the MatSys::Renderer->IsSupported() methods would fail). |
|---|
| 264 | m_GLCanvas->SetCurrent(*m_GLContext); |
|---|
| 265 | |
|---|
| 266 | |
|---|
| 267 | // Obtain the specified MatSys renderer (or if none is specified, automatically find the "best"). |
|---|
| 268 | const wxString RendererName=wxConfigBase::Get()->Read("Global/Renderer", "").Trim(); |
|---|
| 269 | |
|---|
| 270 | if (RendererName!="" && !RendererName.StartsWith("#")) |
|---|
| 271 | MatSys::Renderer=PlatformAux::GetRenderer(std::string(RendererName), m_RendererDLL); |
|---|
| 272 | |
|---|
| 273 | if (MatSys::Renderer==NULL || m_RendererDLL==NULL) |
|---|
| 274 | MatSys::Renderer=PlatformAux::GetBestRenderer(m_RendererDLL); |
|---|
| 275 | |
|---|
| 276 | if (MatSys::Renderer==NULL || m_RendererDLL==NULL) |
|---|
| 277 | { |
|---|
| 278 | wxMessageBox("Could not find a renderer that is supported on your system.", "Material System Error", wxOK | wxICON_ERROR); |
|---|
| 279 | Destroy(); |
|---|
| 280 | return; |
|---|
| 281 | } |
|---|
| 282 | |
|---|
| 283 | MatSys::Renderer->Initialize(); |
|---|
| 284 | |
|---|
| 285 | |
|---|
| 286 | // Obtain the texture manager for the previously loaded renderer. |
|---|
| 287 | MatSys::TextureMapManager=PlatformAux::GetTextureMapManager(m_RendererDLL); |
|---|
| 288 | |
|---|
| 289 | if (MatSys::TextureMapManager==NULL) |
|---|
| 290 | { |
|---|
| 291 | wxMessageBox("No TextureMapManager obtained.", "ERROR"); |
|---|
| 292 | Destroy(); |
|---|
| 293 | return; |
|---|
| 294 | } |
|---|
| 295 | |
|---|
| 296 | |
|---|
| 297 | // Create a very simple lightmap for the materials that need one, and register it with the renderer. |
|---|
| 298 | char Data[]={ 255, 255, 255, 255, 255, 255, 0, 0, |
|---|
| 299 | 255, 255, 255, 255, 255, 255, 0, 0 }; |
|---|
| 300 | |
|---|
| 301 | m_WhiteTexture=MatSys::TextureMapManager->GetTextureMap2D(Data, 2, 2, 3, true, MapCompositionT(MapCompositionT::Linear, MapCompositionT::Linear)); |
|---|
| 302 | |
|---|
| 303 | MatSys::Renderer->SetCurrentLightMap(m_WhiteTexture); |
|---|
| 304 | MatSys::Renderer->SetCurrentLightDirMap(NULL); // The MatSys provides a default for LightDirMaps when NULL is set. |
|---|
| 305 | |
|---|
| 306 | |
|---|
| 307 | // The files specified at the command line can only be loaded after both |
|---|
| 308 | // - our m_ParentFrame member is set, i.e. the ParentFrameT() ctor returned, |
|---|
| 309 | // - and the MatSys is inited, i.e. ParentFrameT::OnShow() has been called. |
|---|
| 310 | // Due to the different times things happen on Windows vs. Linux, putting this |
|---|
| 311 | // as an event into the command queue seems to be the only way to get it right. |
|---|
| 312 | GetEventHandler()->QueueEvent(new wxCommandEvent(wxEVT_COMMAND_MENU_SELECTED, ID_MENU_FILE_OPEN_CMDLINE)); |
|---|
| 313 | } |
|---|
| 314 | } |
|---|
| 315 | |
|---|
| 316 | |
|---|
| 317 | void ParentFrameT::OnClose(wxCloseEvent& CE) |
|---|
| 318 | { |
|---|
| 319 | if (!CE.CanVeto()) |
|---|
| 320 | { |
|---|
| 321 | Destroy(); |
|---|
| 322 | return; |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | |
|---|
| 326 | // Have to work with a copy of the list, because child frames remove themselves from the original list |
|---|
| 327 | // when they are successfully closed during the following process. |
|---|
| 328 | ArrayT<ChildFrameT*> ChildFrames=m_ChildFrames; |
|---|
| 329 | |
|---|
| 330 | for (unsigned long ChildNr=0; ChildNr<ChildFrames.Size(); ChildNr++) |
|---|
| 331 | if (!ChildFrames[ChildNr]->Close()) |
|---|
| 332 | { |
|---|
| 333 | // If the child was not closed, e.g. because the user vetoed against it, |
|---|
| 334 | // veto in turn against the close of the parent frame and stop trying to close the remaining children. |
|---|
| 335 | CE.Veto(); |
|---|
| 336 | return; |
|---|
| 337 | } |
|---|
| 338 | |
|---|
| 339 | |
|---|
| 340 | // See comment above. |
|---|
| 341 | ArrayT<ModelEditor::ChildFrameT*> MdlChildFrames=m_MdlChildFrames; |
|---|
| 342 | |
|---|
| 343 | for (unsigned long ChildNr=0; ChildNr<MdlChildFrames.Size(); ChildNr++) |
|---|
| 344 | if (!MdlChildFrames[ChildNr]->Close()) |
|---|
| 345 | { |
|---|
| 346 | // If the child was not closed, e.g. because the user vetoed against it, |
|---|
| 347 | // veto in turn against the close of the parent frame and stop trying to close the remaining children. |
|---|
| 348 | CE.Veto(); |
|---|
| 349 | return; |
|---|
| 350 | } |
|---|
| 351 | |
|---|
| 352 | |
|---|
| 353 | // See comment above. |
|---|
| 354 | ArrayT<GuiEditor::ChildFrameT*> GuiChildFrames=m_GuiChildFrames; |
|---|
| 355 | |
|---|
| 356 | for (unsigned long ChildNr=0; ChildNr<GuiChildFrames.Size(); ChildNr++) |
|---|
| 357 | if (!GuiChildFrames[ChildNr]->Close()) |
|---|
| 358 | { |
|---|
| 359 | // If the child was not closed, e.g. because the user vetoed against it, |
|---|
| 360 | // veto in turn against the close of the parent frame and stop trying to close the remaining children. |
|---|
| 361 | CE.Veto(); |
|---|
| 362 | return; |
|---|
| 363 | } |
|---|
| 364 | |
|---|
| 365 | |
|---|
| 366 | // All child frames were successfully closed. |
|---|
| 367 | // It's safe now to also close this parent window, which will gracefully exit the application. |
|---|
| 368 | Destroy(); |
|---|
| 369 | } |
|---|
| 370 | |
|---|
| 371 | |
|---|
| 372 | GameConfigT* ParentFrameT::AskUserForGameConfig(const wxFileName& DocumentPath) const |
|---|
| 373 | { |
|---|
| 374 | if (Options.GameConfigs.Size()==0) |
|---|
| 375 | { |
|---|
| 376 | wxMessageBox("I scanned the Games subdirectory for game configurations,\n" |
|---|
| 377 | "but didn't find any.\n" |
|---|
| 378 | "You need to have at least one game configuration inside your\n" |
|---|
| 379 | "Games directory for the editor to work.\n" |
|---|
| 380 | "Please re-install Cafu or contact the Cafu forums for help.", "No game configurations found", wxOK | wxICON_ERROR); |
|---|
| 381 | |
|---|
| 382 | return NULL; |
|---|
| 383 | } |
|---|
| 384 | |
|---|
| 385 | |
|---|
| 386 | // If there is only exactly one game configuration, take it. |
|---|
| 387 | if (Options.GameConfigs.Size()==1) return Options.GameConfigs[0]; |
|---|
| 388 | |
|---|
| 389 | |
|---|
| 390 | // If there is more than one game configuration, try to choose one based on the DocumentPath. |
|---|
| 391 | ArrayT<GameConfigT*> Candidates; |
|---|
| 392 | |
|---|
| 393 | for (unsigned int i=0; i+1<DocumentPath.GetDirCount(); i++) |
|---|
| 394 | { |
|---|
| 395 | if (DocumentPath.GetDirs()[i]=="Games") |
|---|
| 396 | { |
|---|
| 397 | for (unsigned long CfgNr=0; CfgNr<Options.GameConfigs.Size(); CfgNr++) |
|---|
| 398 | { |
|---|
| 399 | if (DocumentPath.GetDirs()[i+1]==Options.GameConfigs[CfgNr]->Name) |
|---|
| 400 | Candidates.PushBack(Options.GameConfigs[CfgNr]); |
|---|
| 401 | } |
|---|
| 402 | } |
|---|
| 403 | } |
|---|
| 404 | |
|---|
| 405 | // Only if there is exactly one candidate can we unambiguously pick one. |
|---|
| 406 | if (Candidates.Size()==1) return Candidates[0]; |
|---|
| 407 | |
|---|
| 408 | |
|---|
| 409 | // If there is more than one game configuration, prompt the user to select one. |
|---|
| 410 | ArrayT<wxString> Choices; |
|---|
| 411 | |
|---|
| 412 | for (unsigned long CfgNr=0; CfgNr<Options.GameConfigs.Size(); CfgNr++) |
|---|
| 413 | Choices.PushBack(wxString(Options.GameConfigs[CfgNr]->Name)); |
|---|
| 414 | |
|---|
| 415 | const int SelectionNr=wxGetSingleChoiceIndex("Available configurations:", wxString("Please select a game for ")+DocumentPath.GetFullName(), Choices.Size(), &Choices[0]); |
|---|
| 416 | |
|---|
| 417 | if (SelectionNr==-1) return NULL; |
|---|
| 418 | return Options.GameConfigs[SelectionNr]; |
|---|
| 419 | } |
|---|
| 420 | |
|---|
| 421 | |
|---|
| 422 | namespace |
|---|
| 423 | { |
|---|
| 424 | /// This class is a wrapper around a wxProgressDialog. |
|---|
| 425 | /// It is necessary because the wxProgressDialog has several bugs, see code below for details. |
|---|
| 426 | class ProgDlgT |
|---|
| 427 | { |
|---|
| 428 | public: |
|---|
| 429 | |
|---|
| 430 | ProgDlgT(wxWindow* Parent, const wxString& Msg) |
|---|
| 431 | : m_ProgDlg(NULL), |
|---|
| 432 | m_BusyInfo(NULL) |
|---|
| 433 | { |
|---|
| 434 | #if defined(__WXGTK__) |
|---|
| 435 | // Under wxGTK, the wxProgressDialog cannot be used at all, see http://trac.wxwidgets.org/ticket/12500 for details. |
|---|
| 436 | // To make matters worse, the wxBusyCursor and the wxBusyInfo don't work as well: |
|---|
| 437 | // - wxBusyCursor: http://trac.wxwidgets.org/ticket/13390 |
|---|
| 438 | // - wxBusyInfo: http://trac.wxwidgets.org/ticket/13262 |
|---|
| 439 | #elif defined(__WXMSW__) |
|---|
| 440 | /// Under wxMSW, the wxProgressDialog sometimes hangs when a 32-bit program is run under WOW64, |
|---|
| 441 | /// see http://trac.cafu.de/ticket/47 for details. |
|---|
| 442 | if (sizeof(void*)==4 && wxIsPlatform64Bit()) |
|---|
| 443 | m_BusyInfo=new wxBusyInfo(Msg); |
|---|
| 444 | else |
|---|
| 445 | m_ProgDlg=new wxProgressDialog(Msg, "Almost done...", 100, Parent, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_SMOOTH); |
|---|
| 446 | #else |
|---|
| 447 | m_ProgDlg=new wxProgressDialog(Msg, "Almost done...", 100, Parent, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_SMOOTH); |
|---|
| 448 | #endif |
|---|
| 449 | } |
|---|
| 450 | |
|---|
| 451 | ~ProgDlgT() |
|---|
| 452 | { |
|---|
| 453 | delete m_ProgDlg; |
|---|
| 454 | delete m_BusyInfo; |
|---|
| 455 | } |
|---|
| 456 | |
|---|
| 457 | wxProgressDialog* GetPtr() |
|---|
| 458 | { |
|---|
| 459 | return m_ProgDlg; |
|---|
| 460 | } |
|---|
| 461 | |
|---|
| 462 | |
|---|
| 463 | private: |
|---|
| 464 | |
|---|
| 465 | ProgDlgT(const ProgDlgT&); // Use of the Copy Constructor is not allowed. |
|---|
| 466 | void operator = (const ProgDlgT&); // Use of the Assignment Operator is not allowed. |
|---|
| 467 | |
|---|
| 468 | wxProgressDialog* m_ProgDlg; |
|---|
| 469 | wxBusyInfo* m_BusyInfo; |
|---|
| 470 | }; |
|---|
| 471 | } |
|---|
| 472 | |
|---|
| 473 | |
|---|
| 474 | wxMDIChildFrame* ParentFrameT::OpenFile(GameConfigT* GameConfig, wxString FileName) |
|---|
| 475 | { |
|---|
| 476 | static const char* SPECIFIER_LIST_C[]={ "D3", "HL1", "HL2" }; |
|---|
| 477 | static const wxArrayString SPECIFIER_LIST(3, SPECIFIER_LIST_C); |
|---|
| 478 | |
|---|
| 479 | // Don't attempt to open the file if the game config is not given. |
|---|
| 480 | if (GameConfig==NULL) return NULL; |
|---|
| 481 | |
|---|
| 482 | // Separate the filename and the disambiguation specifier. |
|---|
| 483 | wxString Specifier=""; |
|---|
| 484 | |
|---|
| 485 | for (size_t i=0; i<SPECIFIER_LIST.GetCount(); i++) |
|---|
| 486 | { |
|---|
| 487 | wxString Rest; |
|---|
| 488 | |
|---|
| 489 | if (FileName.EndsWith(" ("+SPECIFIER_LIST[i]+")", &Rest)) |
|---|
| 490 | { |
|---|
| 491 | FileName =Rest; |
|---|
| 492 | Specifier=SPECIFIER_LIST[i]; |
|---|
| 493 | break; |
|---|
| 494 | } |
|---|
| 495 | } |
|---|
| 496 | |
|---|
| 497 | // Load the specified file. |
|---|
| 498 | try |
|---|
| 499 | { |
|---|
| 500 | if (FileName.EndsWith(".cmap")) |
|---|
| 501 | { |
|---|
| 502 | ProgDlgT ProgDlg(this, "Loading Cafu Map File"); |
|---|
| 503 | |
|---|
| 504 | return new ChildFrameT(this, FileName, new MapDocumentT(GameConfig, ProgDlg.GetPtr(), FileName)); |
|---|
| 505 | } |
|---|
| 506 | |
|---|
| 507 | if (FileName.EndsWith(".cmdl")) |
|---|
| 508 | { |
|---|
| 509 | wxBusyInfo BusyInfo("Loading..."); |
|---|
| 510 | |
|---|
| 511 | return new ModelEditor::ChildFrameT(this, FileName, new ModelEditor::ModelDocumentT(GameConfig, FileName)); |
|---|
| 512 | } |
|---|
| 513 | |
|---|
| 514 | if (FileName.EndsWith(".cgui")) |
|---|
| 515 | { |
|---|
| 516 | if (FileName.EndsWith("_init.cgui")) |
|---|
| 517 | { |
|---|
| 518 | wxBusyInfo BusyInfo("Loading..."); |
|---|
| 519 | |
|---|
| 520 | return new GuiEditor::ChildFrameT(this, FileName, new GuiEditor::GuiDocumentT(GameConfig, FileName)); |
|---|
| 521 | } |
|---|
| 522 | |
|---|
| 523 | wxMessageBox("In order to load this GUI, please open the related file whose name ends with _init.cgui\n" |
|---|
| 524 | "(instead of "+FileName+").\n\n" |
|---|
| 525 | "CaWE always deals with the *_init.cgui files, everything else is for your customizations (hand-written script code).\n" |
|---|
| 526 | "This way the files never overwrite each other.", "*_init.gui file expected"); |
|---|
| 527 | return NULL; |
|---|
| 528 | } |
|---|
| 529 | |
|---|
| 530 | if (FileName.EndsWith(".map")) |
|---|
| 531 | { |
|---|
| 532 | if (Specifier=="") |
|---|
| 533 | { |
|---|
| 534 | const int Choice=wxGetSingleChoiceIndex("Please select this map files source format:", "Import map file", SPECIFIER_LIST); |
|---|
| 535 | |
|---|
| 536 | if (Choice<0) return NULL; // The user pressed Cancel. |
|---|
| 537 | Specifier=SPECIFIER_LIST[Choice]; |
|---|
| 538 | } |
|---|
| 539 | |
|---|
| 540 | ProgDlgT ProgDlg(this, "Importing "+Specifier+" map file"); |
|---|
| 541 | |
|---|
| 542 | if (Specifier=="HL1") |
|---|
| 543 | { |
|---|
| 544 | return new ChildFrameT(this, FileName, MapDocumentT::ImportHalfLife1Map(GameConfig, ProgDlg.GetPtr(), FileName)); |
|---|
| 545 | } |
|---|
| 546 | |
|---|
| 547 | if (Specifier=="D3") |
|---|
| 548 | { |
|---|
| 549 | return new ChildFrameT(this, FileName, MapDocumentT::ImportDoom3Map(GameConfig, ProgDlg.GetPtr(), FileName)); |
|---|
| 550 | } |
|---|
| 551 | } |
|---|
| 552 | |
|---|
| 553 | if (FileName.EndsWith(".vmf")) |
|---|
| 554 | { |
|---|
| 555 | ProgDlgT ProgDlg(this, "Importing HL2 vmf file"); |
|---|
| 556 | |
|---|
| 557 | return new ChildFrameT(this, FileName, MapDocumentT::ImportHalfLife2Vmf(GameConfig, ProgDlg.GetPtr(), FileName)); |
|---|
| 558 | } |
|---|
| 559 | |
|---|
| 560 | // We've tried all known filename suffixes. Now assume that FileName specifies a model file and try that. |
|---|
| 561 | wxBusyInfo BusyInfo("Loading..."); |
|---|
| 562 | |
|---|
| 563 | return new ModelEditor::ChildFrameT(this, FileName, new ModelEditor::ModelDocumentT(GameConfig, FileName)); |
|---|
| 564 | } |
|---|
| 565 | catch (const MapDocumentT::LoadErrorT& /*E*/) |
|---|
| 566 | { |
|---|
| 567 | wxMessageBox(wxString("The map file \"")+FileName+"\" could not be loaded!", "Couldn't load the map"); |
|---|
| 568 | } |
|---|
| 569 | catch (const ModelLoaderT::LoadErrorT& LE) |
|---|
| 570 | { |
|---|
| 571 | wxMessageBox(wxString("The model file \"")+FileName+"\" could not be loaded:\n"+LE.what(), "Couldn't load or import model"); |
|---|
| 572 | } |
|---|
| 573 | catch (const cf::GuiSys::GuiImplT::InitErrorT& IE) |
|---|
| 574 | { |
|---|
| 575 | wxMessageBox(wxString("The GUI script \"")+FileName+"\" could not be loaded:\n"+IE.what(), "Couldn't load GUI script"); |
|---|
| 576 | } |
|---|
| 577 | |
|---|
| 578 | return NULL; |
|---|
| 579 | } |
|---|
| 580 | |
|---|
| 581 | |
|---|
| 582 | void ParentFrameT::OnMenuFile(wxCommandEvent& CE) |
|---|
| 583 | { |
|---|
| 584 | wxASSERT(m_RendererDLL!=NULL && MatSys::Renderer!=NULL); |
|---|
| 585 | |
|---|
| 586 | switch (CE.GetId()) |
|---|
| 587 | { |
|---|
| 588 | case ID_MENU_FILE_NEW_MAP: |
|---|
| 589 | { |
|---|
| 590 | GameConfigT* GameConfig=AskUserForGameConfig(wxFileName("the new map")); |
|---|
| 591 | if (GameConfig==NULL) break; |
|---|
| 592 | |
|---|
| 593 | // Create the new, empty document. |
|---|
| 594 | MapDocumentT* NewDocument=new MapDocumentT(GameConfig); |
|---|
| 595 | |
|---|
| 596 | // Create the child frame and give the NewDocument to it (the child frame becomes the owner of the NewDocument). |
|---|
| 597 | new ChildFrameT(this, "New Document", NewDocument); |
|---|
| 598 | break; |
|---|
| 599 | } |
|---|
| 600 | |
|---|
| 601 | case ID_MENU_FILE_NEW_MODEL: |
|---|
| 602 | { |
|---|
| 603 | wxFileDialog FileDialog(this, "Open File", "", "", "Model files (*.*)|*.*", wxFD_OPEN | wxFD_FILE_MUST_EXIST); |
|---|
| 604 | |
|---|
| 605 | if (FileDialog.ShowModal()!=wxID_OK) break; |
|---|
| 606 | |
|---|
| 607 | GameConfigT* GameConfig=AskUserForGameConfig(wxFileName(FileDialog.GetPath())); |
|---|
| 608 | wxString FileName =FileDialog.GetPath(); |
|---|
| 609 | |
|---|
| 610 | if (OpenFile(GameConfig, FileName)) |
|---|
| 611 | { |
|---|
| 612 | // The file was successfully opened, now add it to the MRU list. |
|---|
| 613 | m_FileHistory.AddFileToHistory(FileName); |
|---|
| 614 | } |
|---|
| 615 | break; |
|---|
| 616 | } |
|---|
| 617 | |
|---|
| 618 | case ID_MENU_FILE_NEW_GUI: |
|---|
| 619 | { |
|---|
| 620 | GameConfigT* GameConfig=AskUserForGameConfig(wxFileName("the new gui")); |
|---|
| 621 | if (GameConfig==NULL) break; |
|---|
| 622 | |
|---|
| 623 | try |
|---|
| 624 | { |
|---|
| 625 | new GuiEditor::ChildFrameT(this, "New GUI", new GuiEditor::GuiDocumentT(GameConfig)); |
|---|
| 626 | } |
|---|
| 627 | catch (const cf::GuiSys::GuiImplT::InitErrorT& /*E*/) |
|---|
| 628 | { |
|---|
| 629 | wxMessageBox(wxString("An error occured during GUI creation, no GUI has been created!", "Couldn't create GUI")); |
|---|
| 630 | } |
|---|
| 631 | break; |
|---|
| 632 | } |
|---|
| 633 | |
|---|
| 634 | case ID_MENU_FILE_NEW_FONT: |
|---|
| 635 | { |
|---|
| 636 | FontWizardT* FontWizard=new FontWizardT(this); |
|---|
| 637 | |
|---|
| 638 | FontWizard->Run(); |
|---|
| 639 | FontWizard->Destroy(); |
|---|
| 640 | break; |
|---|
| 641 | } |
|---|
| 642 | |
|---|
| 643 | case ID_MENU_FILE_OPEN: |
|---|
| 644 | { |
|---|
| 645 | wxFileDialog FileDialog(this, // The window parent. |
|---|
| 646 | "Open File", // Message. |
|---|
| 647 | "", // The default directory. |
|---|
| 648 | "", // The default file name. |
|---|
| 649 | "All files (*.*)|*.*" // The wildcard. |
|---|
| 650 | "|Map files|*.cmap;*.map;*.vmf" |
|---|
| 651 | "|Model files|*.*" |
|---|
| 652 | "|GUI files|*.cgui" |
|---|
| 653 | "|-----------------|*.*" |
|---|
| 654 | "|Cafu map (*.cmap)|*.cmap" |
|---|
| 655 | "|Doom3 map (*.map)|*.map" |
|---|
| 656 | "|Hammer 3.x (HL1) map (*.map)|*.map" |
|---|
| 657 | "|Hammer 4.x (HL2) map (*.vmf)|*.vmf", |
|---|
| 658 | wxFD_OPEN | wxFD_FILE_MUST_EXIST); |
|---|
| 659 | |
|---|
| 660 | if (FileDialog.ShowModal()!=wxID_OK) break; |
|---|
| 661 | |
|---|
| 662 | GameConfigT* GameConfig=AskUserForGameConfig(wxFileName(FileDialog.GetPath())); |
|---|
| 663 | wxString FileName =FileDialog.GetPath(); |
|---|
| 664 | |
|---|
| 665 | switch (FileDialog.GetFilterIndex()) |
|---|
| 666 | { |
|---|
| 667 | case 6: FileName+=" (D3)"; break; |
|---|
| 668 | case 7: FileName+=" (HL1)"; break; |
|---|
| 669 | case 8: FileName+=" (HL2)"; break; |
|---|
| 670 | } |
|---|
| 671 | |
|---|
| 672 | if (OpenFile(GameConfig, FileName)) |
|---|
| 673 | { |
|---|
| 674 | // The file was successfully opened, now add it to the MRU list (with the Specifier, if present). |
|---|
| 675 | // We do this both for "native" Cafu files as well as for "foreign" imported files - it's useful either way. |
|---|
| 676 | m_FileHistory.AddFileToHistory(FileName); |
|---|
| 677 | } |
|---|
| 678 | break; |
|---|
| 679 | } |
|---|
| 680 | |
|---|
| 681 | case ID_MENU_FILE_OPEN_CMDLINE: |
|---|
| 682 | { |
|---|
| 683 | // Open the files specified at the command line. |
|---|
| 684 | for (size_t ParamNr=0; ParamNr<m_CmdLineParser.GetParamCount(); ParamNr++) |
|---|
| 685 | { |
|---|
| 686 | wxString FileName=m_CmdLineParser.GetParam(ParamNr); |
|---|
| 687 | |
|---|
| 688 | if (!wxFileExists(FileName)) |
|---|
| 689 | { |
|---|
| 690 | wxMessageBox("File not found\n\n" + FileName, "Open File", wxOK | wxICON_ERROR); |
|---|
| 691 | continue; |
|---|
| 692 | } |
|---|
| 693 | |
|---|
| 694 | GameConfigT* GameConfig=AskUserForGameConfig(wxFileName(FileName)); |
|---|
| 695 | wxMDIChildFrame* ChildFrame=OpenFile(GameConfig, FileName); |
|---|
| 696 | |
|---|
| 697 | if (ChildFrame) |
|---|
| 698 | { |
|---|
| 699 | // The file was successfully opened, now add it to the MRU list (with the Specifier, if present). |
|---|
| 700 | // We do this both for "native" Cafu files as well as for "foreign" imported files - it's useful either way. |
|---|
| 701 | m_FileHistory.AddFileToHistory(FileName); |
|---|
| 702 | } |
|---|
| 703 | } |
|---|
| 704 | |
|---|
| 705 | break; |
|---|
| 706 | } |
|---|
| 707 | |
|---|
| 708 | case ID_MENU_FILE_CONFIGURE: |
|---|
| 709 | { |
|---|
| 710 | OptionsDialogT OptionsDialog(this); |
|---|
| 711 | |
|---|
| 712 | if (OptionsDialog.ShowModal()==wxID_OK) Options.Write(); |
|---|
| 713 | break; |
|---|
| 714 | } |
|---|
| 715 | |
|---|
| 716 | case ID_MENU_FILE_EXIT: |
|---|
| 717 | { |
|---|
| 718 | // Close() generates a EVT_CLOSE event which is handled by our OnClose() handler. |
|---|
| 719 | // See wx Window Deletion Overview for more details. |
|---|
| 720 | Close(); |
|---|
| 721 | break; |
|---|
| 722 | } |
|---|
| 723 | |
|---|
| 724 | default: |
|---|
| 725 | { |
|---|
| 726 | if (CE.GetId()>=wxID_FILE1 && CE.GetId()<=wxID_FILE9) |
|---|
| 727 | { |
|---|
| 728 | // Handle a selection from the file history (MRU list). |
|---|
| 729 | wxString FileName =m_FileHistory.GetHistoryFile(CE.GetId()-wxID_FILE1); |
|---|
| 730 | GameConfigT* GameConfig=AskUserForGameConfig(wxFileName(FileName)); |
|---|
| 731 | |
|---|
| 732 | // If the user cancelled the "select game configuration" dialog, |
|---|
| 733 | // don't remove the file from the file history. |
|---|
| 734 | if (!GameConfig) break; |
|---|
| 735 | |
|---|
| 736 | // We remove the file from the file history beforehand. |
|---|
| 737 | // It is added back below when it could be successfully opened, and left out otherwise. |
|---|
| 738 | m_FileHistory.RemoveFileFromHistory(CE.GetId()-wxID_FILE1); |
|---|
| 739 | |
|---|
| 740 | if (OpenFile(GameConfig, FileName)) |
|---|
| 741 | { |
|---|
| 742 | // The file was successfully opened, now add it back to the MRU list. |
|---|
| 743 | m_FileHistory.AddFileToHistory(FileName); |
|---|
| 744 | } |
|---|
| 745 | } |
|---|
| 746 | |
|---|
| 747 | break; |
|---|
| 748 | } |
|---|
| 749 | } |
|---|
| 750 | } |
|---|
| 751 | |
|---|
| 752 | |
|---|
| 753 | /// Skips a block in TP, e.g. a { ... } or ( ... ) block. The block can contain nested blocks. |
|---|
| 754 | /// It can (and must) be stated if the caller has already read the opening token. |
|---|
| 755 | static void SkipBlock(TextParserT& TP, const std::string& OpeningToken, const std::string& ClosingToken, bool CallerAlreadyReadOpeningToken) |
|---|
| 756 | { |
|---|
| 757 | if (!CallerAlreadyReadOpeningToken) TP.GetNextToken(); |
|---|
| 758 | |
|---|
| 759 | unsigned long NestedLevel=1; |
|---|
| 760 | |
|---|
| 761 | while (true) |
|---|
| 762 | { |
|---|
| 763 | const std::string Token=TP.GetNextToken(); |
|---|
| 764 | |
|---|
| 765 | if (Token==OpeningToken) |
|---|
| 766 | { |
|---|
| 767 | NestedLevel++; |
|---|
| 768 | } |
|---|
| 769 | else if (Token==ClosingToken) |
|---|
| 770 | { |
|---|
| 771 | NestedLevel--; |
|---|
| 772 | if (NestedLevel==0) break; |
|---|
| 773 | } |
|---|
| 774 | } |
|---|
| 775 | } |
|---|
| 776 | |
|---|
| 777 | |
|---|
| 778 | /// Replaces the "textures/" prefix of Doom3 texture image paths with "texturesD3/", |
|---|
| 779 | /// and "models/" with "modelsD3/". For all other texture names, simply "D3/" is prepended. |
|---|
| 780 | static wxString ReplaceTexturePrefix(const wxString& D3TexPath) |
|---|
| 781 | { |
|---|
| 782 | if (D3TexPath.StartsWith("textures/") || D3TexPath.StartsWith("textures\\")) |
|---|
| 783 | { |
|---|
| 784 | wxString FixedString=D3TexPath; |
|---|
| 785 | FixedString.insert(8, "D3"); |
|---|
| 786 | return FixedString; |
|---|
| 787 | } |
|---|
| 788 | else if (D3TexPath.StartsWith("models/") || D3TexPath.StartsWith("models\\")) |
|---|
| 789 | { |
|---|
| 790 | wxString FixedString=D3TexPath; |
|---|
| 791 | FixedString.insert(6, "D3"); |
|---|
| 792 | return FixedString; |
|---|
| 793 | } |
|---|
| 794 | else |
|---|
| 795 | { |
|---|
| 796 | // There are countless other prefixes, and a few apparently built-in "textures": |
|---|
| 797 | // _scratch _fog _cubicLight _spotlight _pointLight1,2,3 _black _quadratic _default _flat |
|---|
| 798 | return wxString("D3/")+D3TexPath; |
|---|
| 799 | } |
|---|
| 800 | } |
|---|
| 801 | |
|---|
| 802 | |
|---|
| 803 | static wxString ParseD3MapComposition(TextParserT& TP) |
|---|
| 804 | { |
|---|
| 805 | std::string Token=TP.GetNextToken(); |
|---|
| 806 | |
|---|
| 807 | if (Token=="addnormals") |
|---|
| 808 | { |
|---|
| 809 | TP.AssertAndSkipToken("("); |
|---|
| 810 | wxString NMap1=ParseD3MapComposition(TP); |
|---|
| 811 | TP.AssertAndSkipToken(","); |
|---|
| 812 | wxString NMap2=ParseD3MapComposition(TP); |
|---|
| 813 | TP.AssertAndSkipToken(")"); |
|---|
| 814 | |
|---|
| 815 | return wxString("combineNMs(")+NMap1+", "+NMap2+")"; |
|---|
| 816 | } |
|---|
| 817 | else if (Token=="heightmap") |
|---|
| 818 | { |
|---|
| 819 | TP.AssertAndSkipToken("("); |
|---|
| 820 | wxString HeightMap=ParseD3MapComposition(TP); |
|---|
| 821 | TP.AssertAndSkipToken(","); |
|---|
| 822 | wxString Scale=TP.GetNextToken().c_str(); |
|---|
| 823 | TP.AssertAndSkipToken(")"); |
|---|
| 824 | |
|---|
| 825 | return wxString("hm2nm(")+HeightMap+", "+Scale+")"; |
|---|
| 826 | } |
|---|
| 827 | else if (Token=="makealpha") |
|---|
| 828 | { |
|---|
| 829 | TP.AssertAndSkipToken("("); |
|---|
| 830 | wxString Map=ParseD3MapComposition(TP); |
|---|
| 831 | TP.AssertAndSkipToken(")"); |
|---|
| 832 | |
|---|
| 833 | return wxString("blue2alpha(")+Map+")"; |
|---|
| 834 | } |
|---|
| 835 | else if (Token=="makeintensity") |
|---|
| 836 | { |
|---|
| 837 | TP.AssertAndSkipToken("("); |
|---|
| 838 | wxString Map=ParseD3MapComposition(TP); |
|---|
| 839 | TP.AssertAndSkipToken(")"); |
|---|
| 840 | |
|---|
| 841 | // Don't know exactly what makeintensity means (for a normal texture). |
|---|
| 842 | return Map; |
|---|
| 843 | } |
|---|
| 844 | else |
|---|
| 845 | { |
|---|
| 846 | // It's a texture name. |
|---|
| 847 | wxString TexName=ReplaceTexturePrefix(Token.c_str()); |
|---|
| 848 | |
|---|
| 849 | if (TexName.Right(4)!=".tga") TexName+=".tga"; |
|---|
| 850 | |
|---|
| 851 | return TexName; |
|---|
| 852 | } |
|---|
| 853 | } |
|---|
| 854 | |
|---|
| 855 | |
|---|
| 856 | static wxString ParseD3RenderStage(TextParserT& TP, char& MapID) |
|---|
| 857 | { |
|---|
| 858 | MapID=0; |
|---|
| 859 | wxString TexName=""; |
|---|
| 860 | |
|---|
| 861 | while (true) |
|---|
| 862 | { |
|---|
| 863 | std::string Token=TP.GetNextToken(); |
|---|
| 864 | |
|---|
| 865 | if (Token=="}") |
|---|
| 866 | { |
|---|
| 867 | return TexName; |
|---|
| 868 | } |
|---|
| 869 | else if (Token=="{") |
|---|
| 870 | { |
|---|
| 871 | SkipBlock(TP, "{", "}", true); |
|---|
| 872 | } |
|---|
| 873 | else if (Token=="if") |
|---|
| 874 | { |
|---|
| 875 | if (TP.PeekNextToken()=="(") SkipBlock(TP, "(", ")", false); |
|---|
| 876 | else TP.GetNextToken(); |
|---|
| 877 | } |
|---|
| 878 | else if (Token=="blend") |
|---|
| 879 | { |
|---|
| 880 | wxString MapToken=wxString(TP.GetNextToken().c_str()).Lower(); |
|---|
| 881 | |
|---|
| 882 | if (MapToken=="diffusemap" ) MapID='d'; |
|---|
| 883 | else if (MapToken=="bumpmap" ) MapID='b'; |
|---|
| 884 | else if (MapToken=="specularmap") MapID='s'; |
|---|
| 885 | else MapID='c'; // Custom blend mode. |
|---|
| 886 | } |
|---|
| 887 | else if (Token=="map") |
|---|
| 888 | { |
|---|
| 889 | TexName=ParseD3MapComposition(TP); |
|---|
| 890 | if (MapID==0) MapID='c'; |
|---|
| 891 | } |
|---|
| 892 | } |
|---|
| 893 | } |
|---|
| 894 | |
|---|
| 895 | |
|---|
| 896 | void ParentFrameT::OnMenuHelp(wxCommandEvent& CE) |
|---|
| 897 | { |
|---|
| 898 | wxASSERT(m_RendererDLL!=NULL && MatSys::Renderer!=NULL); |
|---|
| 899 | |
|---|
| 900 | switch (CE.GetId()) |
|---|
| 901 | { |
|---|
| 902 | case ID_MENU_HELP_CONTENTS: |
|---|
| 903 | if (!wxLaunchDefaultBrowser("http://www.cafu.de/wiki")) |
|---|
| 904 | wxMessageBox("Sorry, I could not open www.cafu.de/wiki in your default browser automatically."); |
|---|
| 905 | break; |
|---|
| 906 | |
|---|
| 907 | case ID_MENU_HELP_CAFU_WEBSITE: |
|---|
| 908 | if (!wxLaunchDefaultBrowser("http://www.cafu.de")) |
|---|
| 909 | wxMessageBox("Sorry, I could not open www.cafu.de in your default browser automatically."); |
|---|
| 910 | break; |
|---|
| 911 | |
|---|
| 912 | case ID_MENU_HELP_CAFU_FORUM: |
|---|
| 913 | if (!wxLaunchDefaultBrowser("http://www.cafu.de/forum")) |
|---|
| 914 | wxMessageBox("Sorry, I could not open www.cafu.de/forum in your default browser automatically."); |
|---|
| 915 | break; |
|---|
| 916 | |
|---|
| 917 | case ID_MENU_HELP_SET_FRAME_SIZE: |
|---|
| 918 | { |
|---|
| 919 | const long w=wxGetNumberFromUser("Enter the new width for the parent frame, 0 for no change.", "new width:", "Set frame width", 1024, 20, 4096, this); |
|---|
| 920 | const long h=wxGetNumberFromUser("Enter the new height for the parent frame, 0 for no change.", "new height:", "Set frame height", 768, 20, 4096, this); |
|---|
| 921 | |
|---|
| 922 | if (w>=20 || h>=20) |
|---|
| 923 | SetSize(w, h); |
|---|
| 924 | break; |
|---|
| 925 | } |
|---|
| 926 | |
|---|
| 927 | case ID_MENU_HELP_D3_MTR_CONVERTER: |
|---|
| 928 | { |
|---|
| 929 | /// This method converts Doom3 *.mtr materials to Cafu *.cmat materials. |
|---|
| 930 | /// The source and destination folders can be chosen. |
|---|
| 931 | /// The file names are kept, just the extention is replaced (e.g. "senetemp.mtr" in the source folder becomes "senetemp.cmat" in the dest folder). |
|---|
| 932 | /// For a Doom3 *.mtr material name of the form "textures/xy/z", the Cafu *.cmat material name "D3conv/textures/xy/z" is generated, |
|---|
| 933 | /// i.e. "D3conv/" is prepended. |
|---|
| 934 | /// For a Doom3 texture name of the form "textures/xy/z.tga", the texture name "texturesD3/xy/z.tga" is written into the Cafu *.cmat file, |
|---|
| 935 | /// i.e. "D3" is inserted. |
|---|
| 936 | wxString SourceMtrDirName=wxDirSelector("Please choose the folder with the Doom3 *.mtr source material scripts."); if (SourceMtrDirName=="") break; |
|---|
| 937 | wxString DestCmatDirName =wxDirSelector("Please choose the folder for the Cafu *.cmat destination material scripts."); if (DestCmatDirName=="") break; |
|---|
| 938 | |
|---|
| 939 | // This sets the cursor to the busy cursor in its ctor, and back to the default cursor in the dtor. |
|---|
| 940 | wxBusyCursor BusyCursor; |
|---|
| 941 | |
|---|
| 942 | wxDir SourceMtrDir(SourceMtrDirName); |
|---|
| 943 | if (!SourceMtrDir.IsOpened()) break; |
|---|
| 944 | |
|---|
| 945 | unsigned long CountMaterialFiles =0; |
|---|
| 946 | unsigned long CountMaterialDefs =0; |
|---|
| 947 | unsigned long CountMatsWithNoDiffuseMap =0; |
|---|
| 948 | unsigned long CountMatsThatStartWithTextures=0; |
|---|
| 949 | |
|---|
| 950 | wxString MtrName; |
|---|
| 951 | for (bool more=SourceMtrDir.GetFirst(&MtrName, "*.mtr", wxDIR_FILES); more; more=SourceMtrDir.GetNext(&MtrName)) |
|---|
| 952 | { |
|---|
| 953 | wxString MtrPathName =SourceMtrDirName+"/"+MtrName; |
|---|
| 954 | wxString CMatName =MtrName; CMatName.replace(MtrName.length()-3, 3, "cmat"); |
|---|
| 955 | wxString CMatPathName=DestCmatDirName+"/"+CMatName; |
|---|
| 956 | |
|---|
| 957 | TextParserT TP(MtrPathName.c_str(), "({,})"); |
|---|
| 958 | std::ofstream CMatFile(CMatPathName.fn_str()); |
|---|
| 959 | CMatFile << "// Cafu material definition file.\n"; |
|---|
| 960 | CMatFile << "// Created by the CaWE built-in D3 mtr converter.\n\n"; |
|---|
| 961 | CountMaterialFiles++; |
|---|
| 962 | |
|---|
| 963 | try |
|---|
| 964 | { |
|---|
| 965 | while (!TP.IsAtEOF()) |
|---|
| 966 | { |
|---|
| 967 | if (TP.PeekNextToken()=="table") |
|---|
| 968 | { |
|---|
| 969 | // Overread the table. |
|---|
| 970 | TP.AssertAndSkipToken("table"); |
|---|
| 971 | TP.GetNextToken(); // Table name. |
|---|
| 972 | SkipBlock(TP, "{", "}", false); |
|---|
| 973 | } |
|---|
| 974 | else if (TP.PeekNextToken()=="/*" ) SkipBlock(TP, "/*", "*/", false); |
|---|
| 975 | else if (TP.PeekNextToken()=="/**" ) SkipBlock(TP, "/**", "**/", false); |
|---|
| 976 | else if (TP.PeekNextToken()=="/***") SkipBlock(TP, "/***", "***/", false); |
|---|
| 977 | else if (TP.PeekNextToken()=="skin") |
|---|
| 978 | { |
|---|
| 979 | TP.GetNextToken(); |
|---|
| 980 | TP.GetNextToken(); |
|---|
| 981 | SkipBlock(TP, "{", "}", false); |
|---|
| 982 | } |
|---|
| 983 | else if (TP.PeekNextToken()=="particle") |
|---|
| 984 | { |
|---|
| 985 | TP.GetNextToken(); |
|---|
| 986 | TP.GetNextToken(); |
|---|
| 987 | SkipBlock(TP, "{", "}", false); |
|---|
| 988 | } |
|---|
| 989 | else |
|---|
| 990 | { |
|---|
| 991 | // It's a material definition. |
|---|
| 992 | CountMaterialDefs++; |
|---|
| 993 | wxString MaterialName=TP.GetNextToken().c_str(); |
|---|
| 994 | if (MaterialName=="material") MaterialName=TP.GetNextToken().c_str(); |
|---|
| 995 | if (MaterialName.StartsWith("textures")) CountMatsThatStartWithTextures++; |
|---|
| 996 | |
|---|
| 997 | // It seems that some material names in D3 end with ".tga", which they shouldn't. |
|---|
| 998 | if (MaterialName.Right(4)==".tga") MaterialName.erase(MaterialName.length()-4); |
|---|
| 999 | |
|---|
| 1000 | TP.AssertAndSkipToken("{"); |
|---|
| 1001 | |
|---|
| 1002 | CMatFile << "D3conv/" << MaterialName.c_str() << "\n"; |
|---|
| 1003 | CMatFile << "{\n"; |
|---|
| 1004 | |
|---|
| 1005 | wxString diffuseMap =""; |
|---|
| 1006 | wxString normalMap =""; |
|---|
| 1007 | wxString specularMap =""; |
|---|
| 1008 | wxString customBlendMap=""; |
|---|
| 1009 | wxString editorImage =""; |
|---|
| 1010 | |
|---|
| 1011 | while (true) |
|---|
| 1012 | { |
|---|
| 1013 | std::string Token=TP.GetNextToken(); |
|---|
| 1014 | |
|---|
| 1015 | if (Token=="}") |
|---|
| 1016 | { |
|---|
| 1017 | break; |
|---|
| 1018 | } |
|---|
| 1019 | else if (Token=="/*" ) SkipBlock(TP, "/*", "*/", true); |
|---|
| 1020 | else if (Token=="/**") SkipBlock(TP, "/**", "**/", true); |
|---|
| 1021 | else if (Token=="{") |
|---|
| 1022 | { |
|---|
| 1023 | // See if this is a stage / renderpass definition of a map that we have not (yet) found by the simple keywords. |
|---|
| 1024 | char MapID =0; |
|---|
| 1025 | wxString Result=ParseD3RenderStage(TP, MapID); |
|---|
| 1026 | |
|---|
| 1027 | if (Result!="" && MapID!=0) |
|---|
| 1028 | { |
|---|
| 1029 | switch (MapID) |
|---|
| 1030 | { |
|---|
| 1031 | // The "if (...=="")" is there for two purposes: |
|---|
| 1032 | // a) the simple keywords should have priority over the stage definitions, and |
|---|
| 1033 | // b) if there are multiple stage definitions for the same map, always stay with the first. |
|---|
| 1034 | case 'd': if (diffuseMap =="") diffuseMap =Result; break; |
|---|
| 1035 | case 'b': if (normalMap =="") normalMap =Result; break; |
|---|
| 1036 | case 's': if (specularMap =="") specularMap =Result; break; |
|---|
| 1037 | case 'c': if (customBlendMap=="") customBlendMap=Result; break; |
|---|
| 1038 | default: break; |
|---|
| 1039 | } |
|---|
| 1040 | } |
|---|
| 1041 | } |
|---|
| 1042 | else if (wxString(Token.c_str()).Lower()=="diffusemap" ) diffuseMap =ParseD3MapComposition(TP); |
|---|
| 1043 | else if (wxString(Token.c_str()).Lower()=="bumpmap" ) normalMap =ParseD3MapComposition(TP); |
|---|
| 1044 | else if (wxString(Token.c_str()).Lower()=="specularmap" ) specularMap=ParseD3MapComposition(TP); |
|---|
| 1045 | else if (wxString(Token.c_str()).Lower()=="qer_editorimage") editorImage=ParseD3MapComposition(TP); |
|---|
| 1046 | else |
|---|
| 1047 | { |
|---|
| 1048 | // Ignore everything else. |
|---|
| 1049 | } |
|---|
| 1050 | } |
|---|
| 1051 | |
|---|
| 1052 | if (diffuseMap=="") diffuseMap=editorImage; |
|---|
| 1053 | if (diffuseMap=="") diffuseMap=customBlendMap; |
|---|
| 1054 | |
|---|
| 1055 | |
|---|
| 1056 | if (diffuseMap=="") |
|---|
| 1057 | { |
|---|
| 1058 | // wxMessageBox(wxString("No diffusemap found for mtr material\n")+MaterialName+"\nin file\n"+MtrPathName); |
|---|
| 1059 | CountMatsWithNoDiffuseMap++; |
|---|
| 1060 | } |
|---|
| 1061 | |
|---|
| 1062 | if (diffuseMap !="") CMatFile << " diffusemap " << diffuseMap .c_str() << "\n"; |
|---|
| 1063 | if (diffuseMap !="") CMatFile << " lightmap " << "$lightmap" << "\n"; |
|---|
| 1064 | if (normalMap !="") CMatFile << " normalmap " << normalMap .c_str() << "\n"; |
|---|
| 1065 | if (specularMap!="") CMatFile << " specularmap " << specularMap.c_str() << "\n"; |
|---|
| 1066 | |
|---|
| 1067 | CMatFile << "}\n\n"; |
|---|
| 1068 | } |
|---|
| 1069 | } |
|---|
| 1070 | } |
|---|
| 1071 | catch (const TextParserT::ParseError&) |
|---|
| 1072 | { |
|---|
| 1073 | wxMessageBox(wxString("There was an error parsing the file\n")+MtrPathName+wxString::Format("\nnear byte %lu.", TP.GetReadPosByte())); |
|---|
| 1074 | } |
|---|
| 1075 | } |
|---|
| 1076 | |
|---|
| 1077 | wxMessageBox(wxString("The material conversion is complete.\n")+ |
|---|
| 1078 | wxString::Format("Converted %lu mtr files to cmat files which contained a total of %lu materials.\n", CountMaterialFiles, CountMaterialDefs)+ |
|---|
| 1079 | wxString::Format("For %lu materials no diffusemap was found (e.g. video materials etc.).\n", CountMatsWithNoDiffuseMap)+ |
|---|
| 1080 | wxString::Format("The names of %lu materials start with \"textures/\".\n\n", CountMatsThatStartWithTextures)+ |
|---|
| 1081 | "ALL materials with a diffusemap also got a \"lightmap $lightmap\" line\n"+ |
|---|
| 1082 | "(they are intended to be *temporarily* used in *maps*, after all).\n\n"+ |
|---|
| 1083 | "Note that you must restart CaWE in order to use the new materials."); |
|---|
| 1084 | break; |
|---|
| 1085 | } |
|---|
| 1086 | |
|---|
| 1087 | case ID_MENU_HELP_ABOUT: |
|---|
| 1088 | { |
|---|
| 1089 | wxString TextTop= |
|---|
| 1090 | "CaWE, the Cafu World Editor.\n" |
|---|
| 1091 | "Copyright (C) 2002-"+wxString(__DATE__+7)+" Carsten Fuchs Software.\n" |
|---|
| 1092 | "\n" |
|---|
| 1093 | "Please report all encountered bugs.\n"; |
|---|
| 1094 | |
|---|
| 1095 | wxString TextBottom= |
|---|
| 1096 | "Build date: "+wxString(__DATE__)+wxString::Format(". CaWE is installed since %lu days.", Options.DaysSinceInstalled) |
|---|
| 1097 | +wxString("\n\nYour CaWE configuration files are located at:\n")+wxStandardPaths::Get().GetUserDataDir(); |
|---|
| 1098 | |
|---|
| 1099 | wxDialog AboutDialog(this, -1, "CaWE, the Cafu World Editor - "+wxString(__DATE__), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); |
|---|
| 1100 | wxWindow* parent=&AboutDialog; |
|---|
| 1101 | |
|---|
| 1102 | // ### BEGIN of code generated by and copied from wxDesigner. |
|---|
| 1103 | wxBoxSizer *item0 = new wxBoxSizer( wxHORIZONTAL ); |
|---|
| 1104 | |
|---|
| 1105 | wxStaticBitmap *item1 = new wxStaticBitmap( parent, -1, GetIcon() /*Icon of parent frame.*/, wxDefaultPosition, wxDefaultSize ); |
|---|
| 1106 | item0->Add( item1, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); |
|---|
| 1107 | |
|---|
| 1108 | wxBoxSizer *item2 = new wxBoxSizer( wxVERTICAL ); |
|---|
| 1109 | |
|---|
| 1110 | wxStaticText *item3 = new wxStaticText( parent, -1, TextTop, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE ); |
|---|
| 1111 | item2->Add( item3, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); |
|---|
| 1112 | |
|---|
| 1113 | wxStaticBox *item5 = new wxStaticBox( parent, -1, wxT("External Libraries and Artwork") ); |
|---|
| 1114 | wxStaticBoxSizer *item4 = new wxStaticBoxSizer( item5, wxVERTICAL ); |
|---|
| 1115 | |
|---|
| 1116 | class AutoUrlTextCtrlT : public wxTextCtrl |
|---|
| 1117 | { |
|---|
| 1118 | public: |
|---|
| 1119 | |
|---|
| 1120 | AutoUrlTextCtrlT(wxWindow* Parent, wxWindowID ID, const wxString& Value="", const wxPoint& Pos=wxDefaultPosition, const wxSize& Size=wxDefaultSize, long Style=0) |
|---|
| 1121 | : wxTextCtrl(Parent, ID, Value, Pos, Size, Style) |
|---|
| 1122 | { |
|---|
| 1123 | Connect(wxEVT_COMMAND_TEXT_URL, wxTextUrlEventHandler(AutoUrlTextCtrlT::OnAboutUrl)); |
|---|
| 1124 | } |
|---|
| 1125 | |
|---|
| 1126 | /// Event handler for "text URL" events. |
|---|
| 1127 | void OnAboutUrl(wxTextUrlEvent& TUE) |
|---|
| 1128 | { |
|---|
| 1129 | const wxMouseEvent& ME=TUE.GetMouseEvent(); |
|---|
| 1130 | |
|---|
| 1131 | // Filter out mouse moves, too many of them. |
|---|
| 1132 | if (ME.Moving()) return; |
|---|
| 1133 | if (!ME.LeftDown()) return; |
|---|
| 1134 | |
|---|
| 1135 | const int UrlStart=TUE.GetURLStart(); |
|---|
| 1136 | const int UrlEnd =TUE.GetURLEnd(); |
|---|
| 1137 | |
|---|
| 1138 | wxLaunchDefaultBrowser(GetValue().Mid(UrlStart, UrlEnd-UrlStart)); |
|---|
| 1139 | } |
|---|
| 1140 | }; |
|---|
| 1141 | |
|---|
| 1142 | wxTextCtrl *item6 = new AutoUrlTextCtrlT(parent, -1, wxT(""), wxDefaultPosition, wxSize(400, 200), |
|---|
| 1143 | wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH2 | wxTE_AUTO_URL | wxBORDER_SUNKEN); |
|---|
| 1144 | |
|---|
| 1145 | item6->LoadFile("CaWE/res/AboutExtLibs.txt"); // This is much simpler and more flexible than any string constant. |
|---|
| 1146 | // item6->SetInsertionPoint(item6->GetLastPosition()); // Scrolls to the bottom of the text. |
|---|
| 1147 | |
|---|
| 1148 | item4->Add( item6, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); |
|---|
| 1149 | |
|---|
| 1150 | item2->Add( item4, 1, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); |
|---|
| 1151 | |
|---|
| 1152 | wxStaticText *item7 = new wxStaticText( parent, -1, TextBottom, wxDefaultPosition, wxDefaultSize, 0 ); |
|---|
| 1153 | item2->Add( item7, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); |
|---|
| 1154 | |
|---|
| 1155 | wxButton *item8 = new wxButton( parent, wxID_OK, wxT("OK"), wxDefaultPosition, wxDefaultSize, 0 ); |
|---|
| 1156 | item8->SetDefault(); |
|---|
| 1157 | item2->Add( item8, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5 ); |
|---|
| 1158 | |
|---|
| 1159 | item0->Add( item2, 1, wxGROW|wxALIGN_CENTER_HORIZONTAL, 5 ); |
|---|
| 1160 | |
|---|
| 1161 | parent->SetSizer( item0 ); |
|---|
| 1162 | item0->SetSizeHints( parent ); |
|---|
| 1163 | // ### END of code generated by and copied from wxDesigner. |
|---|
| 1164 | |
|---|
| 1165 | AboutDialog.CenterOnParent(); |
|---|
| 1166 | AboutDialog.ShowModal(); |
|---|
| 1167 | break; |
|---|
| 1168 | } |
|---|
| 1169 | } |
|---|
| 1170 | } |
|---|