root/cafu/trunk/Ca3DE/MainCanvas.cpp

Revision 455, 38.4 KB (checked in by Carsten, 4 months ago)

Updated copyright banners (in C++ files).

Line 
1/*
2=================================================================================
3This file is part of Cafu, the open-source game engine and graphics engine
4for multiplayer, cross-platform, real-time 3D action.
5Copyright (C) 2002-2012 Carsten Fuchs Software.
6
7Cafu is free software: you can redistribute it and/or modify it under the terms
8of the GNU General Public License as published by the Free Software Foundation,
9either version 3 of the License, or (at your option) any later version.
10
11Cafu is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13PURPOSE. See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with Cafu. If not, see <http://www.gnu.org/licenses/>.
17
18For support and more information about Cafu, visit us at <http://www.cafu.de>.
19=================================================================================
20*/
21
22#include "MainCanvas.hpp"
23#include "AppCafu.hpp"
24#include "MainFrame.hpp"
25#include "Client/Client.hpp"
26#include "Client/ClientWindow.hpp"
27#include "Server/Server.hpp"
28
29#include "Bitmap/Bitmap.hpp"
30#include "ClipSys/CollisionModelMan.hpp"
31#include "ConsoleCommands/Console.hpp"
32#include "ConsoleCommands/ConsoleInterpreter.hpp"
33#include "ConsoleCommands/ConsoleComposite.hpp"
34#include "ConsoleCommands/ConsoleStringBuffer.hpp"
35#include "ConsoleCommands/ConVar.hpp"
36#include "GuiSys/ConsoleByWindow.hpp"
37#include "GuiSys/GuiImpl.hpp"
38#include "GuiSys/GuiManImpl.hpp"
39#include "GuiSys/Window.hpp"
40#include "MaterialSystem/MaterialManager.hpp"
41#include "MaterialSystem/Renderer.hpp"
42#include "MaterialSystem/TextureMap.hpp"
43#include "Models/ModelManager.hpp"
44#include "OpenGL/OpenGLWindow.hpp"  // For CaMouseEventT and CaKeyboardEventT.
45#include "SoundSystem/SoundShaderManager.hpp"
46#include "SoundSystem/SoundSys.hpp"
47#include "../Games/Game.hpp"
48#include "PlatformAux.hpp"
49
50#ifndef _WIN32
51#include <dlfcn.h>
52#define __stdcall
53#define GetProcAddress dlsym
54#define FreeLibrary dlclose
55#endif
56
57
58class SvGuiCallbT : public ServerT::GuiCallbackI
59{
60    public:
61
62    SvGuiCallbT()
63        : MainMenuGui(NULL)
64    {
65    }
66
67    void OnServerStateChanged(const char* NewState) const
68    {
69        if (!MainMenuGui) return;
70
71        MainMenuGui->CallLuaFunc("OnServerStateChanged", "s", NewState);
72    }
73
74    cf::GuiSys::GuiI* MainMenuGui;
75};
76
77
78BEGIN_EVENT_TABLE(MainCanvasT, wxGLCanvas)
79    EVT_PAINT(MainCanvasT::OnPaint)
80    EVT_IDLE(MainCanvasT::OnIdle)
81    EVT_SIZE(MainCanvasT::OnSize)
82
83    EVT_MOTION    (MainCanvasT::OnMouseMove )
84    EVT_MOUSEWHEEL(MainCanvasT::OnMouseWheel)
85    EVT_LEFT_DOWN (MainCanvasT::OnLMouseDown)
86    EVT_LEFT_UP   (MainCanvasT::OnLMouseUp  )
87    EVT_RIGHT_DOWN(MainCanvasT::OnRMouseDown)
88    EVT_RIGHT_UP  (MainCanvasT::OnRMouseUp  )
89
90    EVT_KEY_DOWN(MainCanvasT::OnKeyDown)
91    EVT_KEY_UP  (MainCanvasT::OnKeyUp  )
92    EVT_CHAR    (MainCanvasT::OnKeyChar)
93END_EVENT_TABLE()
94
95
96static int OpenGLAttributeList[]=
97{
98    WX_GL_RGBA,
99    WX_GL_DOUBLEBUFFER,
100    WX_GL_MIN_RED,       8,
101    WX_GL_MIN_GREEN,     8,
102    WX_GL_MIN_BLUE,      8,
103    WX_GL_MIN_ALPHA,     8,
104    WX_GL_DEPTH_SIZE,   16,
105    WX_GL_STENCIL_SIZE,  8,
106    0   // Zero indicates the end of the array.
107};
108
109
110MainCanvasT::MainCanvasT(MainFrameT* Parent)
111    : wxGLCanvas(Parent, wxID_ANY, OpenGLAttributeList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS, "CafuMainCanvas"),
112      m_Parent(Parent),
113      m_InitState(INIT_REQUIRED),
114      m_GLContext(NULL),
115      m_RendererDLL(NULL),
116      m_ModelManager(NULL),
117      m_GuiResources(NULL),
118      m_SoundSysDLL(NULL),
119      m_GameDLL(NULL),
120      m_Client(NULL),
121      m_Server(NULL),
122      m_SvGuiCallback(NULL),
123      m_ConByGuiWin(NULL),
124      m_Timer(),
125      m_TotalTime(0.0),
126      m_LastMousePos(IN_OTHER_2D_GUI)
127{
128    m_GLContext=new wxGLContext(this);
129
130    SetCursor(wxCURSOR_BLANK);
131    SetBackgroundStyle(wxBG_STYLE_PAINT);
132}
133
134
135MainCanvasT::~MainCanvasT()
136{
137    m_InitState=INIT_REQUIRED;
138
139    wxGetApp().GetConComposite().Detach(m_ConByGuiWin);
140    delete m_ConByGuiWin;
141    m_ConByGuiWin=NULL;
142
143    delete m_Client; m_Client=NULL;
144    delete m_Server; m_Server=NULL;
145
146    if (m_SvGuiCallback)
147    {
148        m_SvGuiCallback->MainMenuGui=NULL;
149        delete m_SvGuiCallback; m_SvGuiCallback=NULL;
150    }
151
152
153    // Release the Game.
154    if (cf::GameSys::Game)
155    {
156        cf::GameSys::Game->Release();
157        cf::GameSys::Game=NULL;
158    }
159
160    // This code has been moved down, see there for details.
161    // if (m_GameDLL)
162    // {
163    //     FreeLibrary(m_GameDLL);
164    //     m_GameDLL=NULL;
165    // }
166
167    // When the game has been unloaded, no collision models must be left in the collision model manager.
168    wxASSERT(cf::ClipSys::CollModelMan->GetUniqueCMCount()==0);
169
170
171    // Release the GuiManager (*before* the renderer).
172    if (cf::GuiSys::GuiMan)
173    {
174        delete cf::GuiSys::GuiMan;
175        cf::GuiSys::GuiMan=NULL;
176    }
177
178
179    // Release the Cafu Sound System.
180    if (SoundSystem)
181    {
182        SoundSystem->Release();
183        SoundSystem=NULL;
184    }
185
186    if (m_SoundSysDLL)
187    {
188        FreeLibrary(m_SoundSysDLL);
189        m_SoundSysDLL=NULL;
190    }
191
192
193    // Release the GUI resources.
194    if (m_GuiResources)
195    {
196        delete m_GuiResources;
197        m_GuiResources=NULL;
198    }
199
200
201    // Release the model manager.
202    if (m_ModelManager)
203    {
204        delete m_ModelManager;
205        m_ModelManager=NULL;
206    }
207
208
209    // Release the Cafu Material System.
210    if (MatSys::TextureMapManager)
211    {
212        // MatSys::TextureMapManager->FreeTextureMap(m_WhiteTexture);
213        MatSys::TextureMapManager=NULL;
214    }
215
216    if (MatSys::Renderer)
217    {
218        MatSys::Renderer->Release();
219        MatSys::Renderer=NULL;
220    }
221
222    if (m_RendererDLL)
223    {
224        FreeLibrary(m_RendererDLL);
225        m_RendererDLL=NULL;
226    }
227
228    // This code used to be further up, but under Windows at r423, the following problem exists:
229    // Class CafuModelT is derived from class ModelT, and thus has a virtual destructor.
230    // When we call ModelManagerT::GetModel() in the game DLL, everything works all right,
231    // but the code linked to the game DLL is used to create the new model, which causes
232    // the vtable of the newly created model to point to the destructor code in the game DLL.
233    // When the call FreeLibrary(m_GameDLL); before the destructors of the models in the
234    // m_ModelManager are run, we essentially remove the code that the virtual destructors
235    // are pointing to, causing access violation.
236    // Moving the call to FreeLibrary() below the "delete m_ModelManager;" fixes the problem.
237    //
238    // Also see this report of someone else experiencing the same problem:
239    // http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/dacc7dbd-2775-4e86-a429-8dd32fae0e33
240    if (m_GameDLL)
241    {
242        FreeLibrary(m_GameDLL);
243        m_GameDLL=NULL;
244    }
245}
246
247
248// A helper function modelled analogous to the PlatformAux::GetRenderer() function.
249static cf::GameSys::GameI* LoadGameDLL(const std::string& GameDllName, HMODULE& GameDLL)
250{
251#ifdef SCONS_BUILD_DIR
252    #define QUOTE(str) QUOTE_HELPER(str)
253    #define QUOTE_HELPER(str) #str
254
255    #ifdef _WIN32
256    const std::string GameDllPathName=std::string("Games/")+GameDllName+"/Code/"+QUOTE(SCONS_BUILD_DIR)+"/"+GameDllName+".dll";
257    #else
258    const std::string GameDllPathName=std::string("Games/")+GameDllName+"/Code/"+QUOTE(SCONS_BUILD_DIR)+"/lib"+GameDllName+".so";
259    #endif
260
261    #undef QUOTE
262    #undef QUOTE_HELPER
263#else
264    const std::string GameDllPathName=std::string("Games/")+GameDllName+"/Code/"+GameDllName+PlatformAux::GetEnvFileSuffix()+".dll";
265#endif
266
267
268    #ifdef _WIN32
269        GameDLL=LoadLibraryA(GameDllPathName.c_str());
270        if (!GameDLL) { Console->Warning(cf::va("Could not load the game DLL at %s.\n", GameDllPathName.c_str())); return NULL; }
271    #else
272        // Note that RTLD_GLOBAL must *not* be passed-in here, or else we get in trouble with subsequently loaded libraries.
273        // (E.g. it causes dlsym(GameDLL, "GetGame") to return identical results for different GameDLLs.)
274        // Please refer to the man page of dlopen for more details.
275        GameDLL=dlopen(GameDllPathName.c_str(), RTLD_NOW);
276        if (!GameDLL) { Console->Warning(cf::va("Could not load the game DLL at %s (%s).\n", GameDllPathName.c_str(), dlerror())); return NULL; }
277    #endif
278
279
280    typedef cf::GameSys::GameI* (__stdcall *GetGameFuncT)(MatSys::RendererI* Renderer,
281        MatSys::TextureMapManagerI* TexMapMan, MaterialManagerI* MatMan, cf::GuiSys::GuiManI* GuiMan_, cf::ConsoleI* Console_,
282        ConsoleInterpreterI* ConInterpreter_, cf::ClipSys::CollModelManI* CollModelMan_, SoundSysI* SoundSystem_,
283        SoundShaderManagerI* SoundShaderManager_);
284
285    #if defined(_WIN32) && !defined(_WIN64)
286        GetGameFuncT GetGameFunc=(GetGameFuncT)GetProcAddress(GameDLL, "_GetGame@36");
287    #else
288        GetGameFuncT GetGameFunc=(GetGameFuncT)GetProcAddress(GameDLL, "GetGame");
289    #endif
290
291    if (!GetGameFunc) { Console->Warning("Could not get the address of the GetGame() function.\n"); FreeLibrary(GameDLL); GameDLL=NULL; return NULL; }
292
293
294    // When we get here, the other interfaces must already have been implementations assigned.
295    assert(MatSys::Renderer);
296    assert(MatSys::TextureMapManager);
297    assert(MaterialManager);
298    assert(cf::GuiSys::GuiMan);
299    assert(Console);
300    assert(ConsoleInterpreter);
301    assert(cf::ClipSys::CollModelMan);
302
303    cf::GameSys::GameI* Game=GetGameFunc(MatSys::Renderer, MatSys::TextureMapManager, MaterialManager, cf::GuiSys::GuiMan, Console, ConsoleInterpreter, cf::ClipSys::CollModelMan, SoundSystem, SoundShaderManager);
304
305    if (!Game) { Console->Warning("Could not get the game implementation.\n"); FreeLibrary(GameDLL); GameDLL=NULL; return NULL; }
306
307    return Game;
308}
309
310
311void MainCanvasT::Initialize()
312{
313    extern ConVarT Options_ClientDesiredRenderer;
314    extern ConVarT Options_ClientTextureDetail;
315    extern ConVarT Options_ClientDesiredSoundSystem;
316    extern ConVarT Options_ServerGameName;
317
318    m_InitState=INIT_FAILED;
319
320    // Initialize the Material System.
321    wxASSERT(this->IsShownOnScreen());
322
323    // If this call was in the ctor, it would trigger an assertion in debug build and yield an invalid (unusable)
324    // OpenGL context in release builds (the GL code in the MatSys::Renderer->IsSupported() methods would fail).
325    this->SetCurrent(*m_GLContext);
326
327
328    try
329    {
330        // Obtain the specified MatSys renderer (or if none is specified, automatically find the "best").
331        const wxString RendererName=wxString(Options_ClientDesiredRenderer.GetValueString()).Trim();
332
333        if (RendererName!="" && !RendererName.StartsWith("#"))
334            MatSys::Renderer=PlatformAux::GetRenderer(std::string(RendererName), m_RendererDLL);
335
336        if (MatSys::Renderer==NULL || m_RendererDLL==NULL)
337            MatSys::Renderer=PlatformAux::GetBestRenderer(m_RendererDLL);
338
339        if (MatSys::Renderer==NULL || m_RendererDLL==NULL)
340            throw std::runtime_error("Could not find a renderer that is supported on your system.");
341
342        MatSys::Renderer->Initialize();
343
344
345        // Obtain the texture map manager from the previously loaded renderer DLL.
346        MatSys::TextureMapManager=PlatformAux::GetTextureMapManager(m_RendererDLL);
347
348        if (MatSys::TextureMapManager==NULL)
349            throw std::runtime_error("Could not get the TextureMapManager from the renderer DLL.");
350
351        switch (Options_ClientTextureDetail.GetValueInt())
352        {
353            case 1: MatSys::TextureMapManager->SetMaxTextureSize(256); break;
354            case 2: MatSys::TextureMapManager->SetMaxTextureSize(128); break;
355        }
356
357
358        // Initialize the model manager and the GUI resources.
359        m_ModelManager=new ModelManagerT();
360        m_GuiResources=new cf::GuiSys::GuiResourcesT(*m_ModelManager);
361
362
363        // Initialize the sound system.
364        const wxString SoundSysName=wxString(Options_ClientDesiredSoundSystem.GetValueString()).Trim();
365
366        if (SoundSysName!="" && !SoundSysName.StartsWith("#"))
367            SoundSystem=PlatformAux::GetSoundSys(std::string(SoundSysName), m_SoundSysDLL);
368
369        if (SoundSystem==NULL || m_SoundSysDLL==NULL)
370            SoundSystem=PlatformAux::GetBestSoundSys(m_SoundSysDLL);
371
372        if (SoundSystem==NULL || m_SoundSysDLL==NULL)
373            throw std::runtime_error("Could not find a sound system that is supported on your system.");
374
375        if (!SoundSystem->Initialize())
376        {
377            Console->Print("WARNING: Sound system failed to initialize!\n");
378            Console->Print("Sorry, but this is probably due to sound driver problems with your computer.\n");
379            Console->Print("We'll proceed anyway, with sound effects disabled.\n");
380
381            SoundSystem=NULL;
382            FreeLibrary(m_SoundSysDLL);
383
384#ifdef SCONS_BUILD_DIR
385            #define QUOTE(str) QUOTE_HELPER(str)
386            #define QUOTE_HELPER(str) #str
387#ifdef _WIN32
388            const std::string NullPaths[]={ std::string("Libs/")+QUOTE(SCONS_BUILD_DIR)+"/SoundSystem/SoundSysNull.dll", "./SoundSysNull.dll" };
389#else
390            const std::string NullPaths[]={ std::string("Libs/")+QUOTE(SCONS_BUILD_DIR)+"/SoundSystem/libSoundSysNull.so", "./libSoundSysNull.so" };
391#endif
392            #undef QUOTE
393            #undef QUOTE_HELPER
394#endif
395
396            SoundSystem=PlatformAux::GetSoundSys(NullPaths[0], m_SoundSysDLL);
397
398            if (SoundSystem==NULL || m_SoundSysDLL==NULL)
399                SoundSystem=PlatformAux::GetSoundSys(NullPaths[1], m_SoundSysDLL);
400
401            // Null sound system should always be there and loadable...
402            if (SoundSystem==NULL || m_SoundSysDLL==NULL)
403                throw std::runtime_error("Could not load the Null sound system.");
404
405            SoundSystem->Initialize();    // Init of Null sound system always succeeds.
406        }
407
408        static ConVarT InitialMasterVolume("snd_InitialMasterVolume", 1.0, ConVarT::FLAG_MAIN_EXE | ConVarT::FLAG_PERSISTENT, "The master volume with which the sound system is initialized.", 0.0, 1.0);
409        SoundSystem->SetMasterVolume(float(InitialMasterVolume.GetValueDouble()));
410
411
412        // Initialize the GUI systems GUI manager.
413        //   - This has to be done *after* all materials are loaded (AppCafuT::OnInit()) and after the MatSys::Renderer
414        //     has been initialized, so that the GuiMan finds its default material and can register it for rendering.
415        //     (This is no longer exactly true: each GUI has now its own local material manager! See r359 from 2011-08-29 for details.)
416        //   - It has to be done *before* the game is initialized, because even the server needs access to it
417        //     when it loads static detail model entities that have world/entity-GUIs.
418        cf::GuiSys::GuiMan=new cf::GuiSys::GuiManImplT(*m_GuiResources);
419
420
421        // Provide a definition for Game, that is, set the global (Cafu.exe-wide) cf::GameSys::Game pointer
422        // to a GameI implementation that is provided by a dynamically loaded game DLL.
423        // This is analogous to the Material System, where Renderer DLLs provide renderer and texture manager implementations.
424        cf::GameSys::Game=LoadGameDLL(Options_ServerGameName.GetValueString(), m_GameDLL);
425
426        if (cf::GameSys::Game==NULL || m_GameDLL==NULL)
427            throw std::runtime_error("Could not load game "+Options_ServerGameName.GetValueString()+".");
428
429        cf::GameSys::Game->Initialize(true /*(Options_RunMode.GetValueInt() & CLIENT_RUNMODE)>0*/,
430                                      true /*(Options_RunMode.GetValueInt() & SERVER_RUNMODE)>0*/,
431                                      *m_ModelManager);
432
433
434        // Create the client and server instances.
435        m_SvGuiCallback=new SvGuiCallbT();
436        m_Server=new ServerT(Options_ServerGameName.GetValueString(), *m_SvGuiCallback, *m_ModelManager);
437        m_Client=new ClientT(*m_ModelManager);  // The client initializes in IDLE state.
438
439
440        // Finish the initialization of the GuiSys.
441        // Note that in the line below, the call to gui:setMousePos() is important, because it sets "MouseOverWindow" in the GUI properly to "Cl".
442        // Without this, a left mouse button click that was not preceeded by a mouse movement would erroneously remove the input focus from "Cl".
443        cf::GuiSys::GuiImplT* ClientGui   =new cf::GuiSys::GuiImplT(*m_GuiResources, "Cl=gui:new('ClientWindowT'); gui:SetRootWindow(Cl); gui:showMouse(false); gui:setMousePos(320, 240); gui:setFocus(Cl); Cl:SetName('Client');", true);
444        cf::GuiSys::WindowT*  ClientWindow=ClientGui->GetRootWindow()->Find("Client");
445        ClientWindowT*        ClWin       =dynamic_cast<ClientWindowT*>(ClientWindow);
446
447        assert(ClWin!=NULL);
448        if (ClWin) ClWin->SetClient(m_Client);
449        cf::GuiSys::GuiMan->Register(ClientGui);
450
451
452        cf::GuiSys::GuiI* MainMenuGui=cf::GuiSys::GuiMan->Find("Games/"+Options_ServerGameName.GetValueString()+"/GUIs/MainMenu/MainMenu_main.cgui", true);
453        if (MainMenuGui==NULL)
454        {
455            MainMenuGui=new cf::GuiSys::GuiImplT(*m_GuiResources, "Err=gui:new('WindowT'); gui:SetRootWindow(Err); gui:activate(true); gui:setInteractive(true); gui:showMouse(false); Err:set('rect', 0, 0, 640, 480); Err:set('textScale', 0.8); Err:set('text', 'Error loading MainMenu_main.cgui,\\nsee console <F1> for details.');", true);
456            cf::GuiSys::GuiMan->Register(MainMenuGui);
457        }
458        m_Client->SetMainMenuGui(MainMenuGui);
459        m_SvGuiCallback->MainMenuGui=MainMenuGui;       // This is the callback for the server, so that it can let the MainMenuGui know about its state changes.
460        m_SvGuiCallback->OnServerStateChanged("idle");  // Argh, this is a HACK for setting the initial state... can we move this / do it better?
461
462        cf::GuiSys::GuiI*    ConsoleGui   =cf::GuiSys::GuiMan->Find("Games/"+Options_ServerGameName.GetValueString()+"/GUIs/Console.cgui", true);
463        cf::GuiSys::WindowT* ConsoleWindow=ConsoleGui ? ConsoleGui->GetRootWindow()->Find("ConsoleOutput") : NULL;
464
465        // Copy the previously collected console output to the new graphical console.
466        m_ConByGuiWin=new cf::GuiSys::ConsoleByWindowT(ConsoleWindow);
467        m_ConByGuiWin->Print(wxGetApp().GetConBuffer().GetBuffer());
468        wxGetApp().GetConComposite().Attach(m_ConByGuiWin);
469
470        m_InitState=INIT_SUCCESS;
471    }
472    catch (const std::runtime_error& RE)
473    {
474        wxMessageDialog Msg(NULL,
475            wxString("While initializing Cafu, the following error occurred:\n\n") + typeid(RE).name() + "\n" + RE.what(),
476            "Cafu Initialization Error",
477            wxOK | wxCANCEL | wxCANCEL_DEFAULT | wxICON_ERROR);
478
479        Msg.SetExtendedMessage("Sorry it didn't work out.\nTo get help, please post this error at the Cafu forums or mailing-list.");
480        Msg.SetOKCancelLabels("Open www.cafu.de", "Close");
481
482        while (Msg.ShowModal()==wxID_OK)
483        {
484            wxLaunchDefaultBrowser("http://www.cafu.de");
485        }
486
487        m_Parent->Destroy();
488    }
489}
490
491
492static const char* ScreenShotSuffixTypes[] = { "jpg", "png", "bmp", NULL };
493static ConVarT ScreenShotSuffix("screenSuffix", "jpg", ConVarT::FLAG_MAIN_EXE, "The suffix to be used for screen-shot image files. Only \"jpg\", \"png\" and \"bmp\" are allowed.", ScreenShotSuffixTypes);
494
495
496void MainCanvasT::TakeScreenshot() const
497{
498    const unsigned int      Width =GetClientSize().GetWidth();
499    const unsigned int      Height=GetClientSize().GetHeight();
500    static ArrayT<uint32_t> FrameBuffer;
501
502    FrameBuffer.Overwrite();
503    FrameBuffer.PushBackEmpty(Width*Height);
504
505    // Read the pixels from the OpenGL back buffer into FrameBuffer.
506    // Note that the first two parameters (0, 0) specify the left BOTTOM corner of the desired rectangle!
507    glReadPixels(0, 0, Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, &FrameBuffer[0]);
508
509    // As mentioned above is the data in the FrameBuffer bottom-up.
510    // Thus now swap all lines (vertical mirroring).
511    for (unsigned int y=0; y<Height/2; y++)
512    {
513        uint32_t* UpperRow=&FrameBuffer[0]+        y   *Width;
514        uint32_t* LowerRow=&FrameBuffer[0]+(Height-y-1)*Width;
515
516        for (unsigned int x=0; x<Width; x++)
517        {
518            const uint32_t Swap=*UpperRow;
519
520            *UpperRow=*LowerRow;
521            *LowerRow=Swap;
522
523            UpperRow++;
524            LowerRow++;
525        }
526    }
527
528    // Find the next free image name and save the file.
529    for (unsigned int Nr=0; Nr<100000; Nr++)
530    {
531        const wxString FileName=wxString::Format("pic%04u", Nr) + "." + ScreenShotSuffix.GetValueString();
532
533        if (!wxFileExists(FileName))
534        {
535            if (BitmapT(Width, Height, &FrameBuffer[0]).SaveToDisk(FileName))
536            {
537                Console->Print(std::string("Screenshot \"")+FileName.ToStdString()+"\" saved.\n");
538            }
539            else
540            {
541                Console->Warning(std::string("Screenshot \"")+FileName.ToStdString()+"\" could not be saved.\n");
542            }
543            return;
544        }
545    }
546}
547
548
549void MainCanvasT::OnPaint(wxPaintEvent& PE)
550{
551    wxPaintDC dc(this);     // It is VERY important not to omit this, or otherwise everything goes havoc.
552
553    if (m_InitState==INIT_REQUIRED)
554    {
555        dc.SetBackground(*wxBLACK_BRUSH);
556        dc.Clear();
557        dc.SetBackgroundMode(wxTRANSPARENT);
558        dc.SetTextForeground(wxColour(255, 200, 0));
559        dc.DrawText("Initializing Cafu...", 10, 40);
560
561        // This code is in this place due to a few peculiarities of OpenGL under GTK that do not exist under MSW:
562        //   - An OpenGL context can only be made current with a canvas that is shown on the screen.
563        //   - Relying on EVT_SHOW however is not a good approach, see the discussions at
564        //     <http://thread.gmane.org/gmane.comp.lib.wxwidgets.general/68490> and
565        //     <http://thread.gmane.org/gmane.comp.lib.wxwidgets.general/70607> for details.
566        // Consequently, the first and best opportunity for initialization is here.
567        Initialize();
568    }
569}
570
571
572void MainCanvasT::OnSize(wxSizeEvent& SE)
573{
574    if (m_InitState!=INIT_SUCCESS) return;
575
576    const wxSize Size=SE.GetSize();
577
578    if (Size.x>0 && Size.y>0)
579        MatSys::Renderer->SetViewport(0, 0, Size.x, Size.y);
580}
581
582
583void MainCanvasT::OnIdle(wxIdleEvent& IE)
584{
585    IE.RequestMore();
586
587    // Beende das Programm, wenn das letzte aktive GUI geschlossen wurde.
588    // if (cf::GuiSys::GuiMan->GetNumberOfActiveGUIs()==0) m_Parent->Close();
589
590    static ConVarT quit("quit", false, ConVarT::FLAG_MAIN_EXE, "The program quits if this variable is set to 1 (true).");
591    if (quit.GetValueBool())
592    {
593        quit.SetValue(false);   // Immediately reset the value, so that we're able to restart the game from a loop that governs the master loop...
594        m_Parent->Close();
595    }
596
597
598    const double FrameTimeD=m_Timer.GetSecondsSinceLastCall();
599    const float  FrameTimeF=float(FrameTimeD);
600
601    m_TotalTime+=FrameTimeD;
602
603    extern ConVarT GlobalTime;
604    GlobalTime.SetValue(m_TotalTime);
605
606
607    if (m_InitState!=INIT_SUCCESS) return;
608
609    cf::GuiSys::GuiMan->DistributeClockTickEvents(FrameTimeF);
610
611    // If the active GUI is the client GUI, see how far the mouse cursor is off center,
612    // derive a mouse event from it, then recenter the mouse cursor.
613    cf::GuiSys::GuiI* ActiveGui=cf::GuiSys::GuiMan->GetTopmostActiveAndInteractive();
614
615    if (ActiveGui && ActiveGui->GetRootWindow()->Name=="Client")
616    {
617        const wxPoint MousePos  =ScreenToClient(wxGetMousePosition());  // Note: ScreenToClient() is a method of wxWindow.
618        const wxSize  WinCenter =GetClientSize()/2;
619        const wxPoint MouseDelta=MousePos-WinCenter;
620
621        if (m_LastMousePos==IN_CLIENT_3D_GUI)
622        {
623            CaMouseEventT ME;
624
625            ME.Type  =CaMouseEventT::CM_MOVE_X;
626            ME.Amount=MouseDelta.x;
627            if (ME.Amount!=0) ActiveGui->ProcessDeviceEvent(ME);
628
629            ME.Type  =CaMouseEventT::CM_MOVE_Y;
630            ME.Amount=MouseDelta.y;
631            if (ME.Amount!=0) ActiveGui->ProcessDeviceEvent(ME);
632        }
633
634        if (MouseDelta.x || MouseDelta.y) WarpPointer(WinCenter.x, WinCenter.y);
635        m_LastMousePos=IN_CLIENT_3D_GUI;
636    }
637
638    if (!m_Parent->IsIconized())
639    {
640        // Render all the GUIs.
641        MatSys::Renderer->BeginFrame(m_TotalTime);
642
643        cf::GuiSys::GuiMan->RenderAll();
644
645        MatSys::Renderer->EndFrame();
646        this->SwapBuffers();
647    }
648    else
649    {
650        // The main window is minimized - give other applications a chance to run.
651        wxMilliSleep(5);
652    }
653
654    // Update the sound system (Reposition sound sources, update streams).
655    SoundSystem->Update();
656
657    // Run a client and a server frame.
658    m_Client->MainLoop(FrameTimeF);
659    if (m_Server) m_Server->MainLoop();
660}
661
662
663void MainCanvasT::OnMouseMove(wxMouseEvent& ME)
664{
665    if (m_InitState!=INIT_SUCCESS) return;
666
667    cf::GuiSys::GuiI* Gui=cf::GuiSys::GuiMan->GetTopmostActiveAndInteractive();
668
669    // This is equivalent to calling cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent),
670    // but computing the amount of mouse movement is much easier (and more precise) like this.
671    if (Gui && Gui->GetRootWindow()->Name!="Client")
672    {
673        float OldMousePosX;
674        float OldMousePosY;
675
676        Gui->GetMousePos(OldMousePosX, OldMousePosY);
677
678        const float NewMousePosX=ME.GetX()*(cf::GuiSys::VIRTUAL_SCREEN_SIZE_X/GetSize().x);
679        const float NewMousePosY=ME.GetY()*(cf::GuiSys::VIRTUAL_SCREEN_SIZE_Y/GetSize().y);
680
681        // This works, but doesn't forward the mouse event to the windows.
682        // Gui->SetMousePos(NewMousePosX, NewMousePosY);
683
684        CaMouseEventT ME;
685
686        ME.Type  =CaMouseEventT::CM_MOVE_X;
687        ME.Amount=NewMousePosX-OldMousePosX;
688        if (ME.Amount!=0) Gui->ProcessDeviceEvent(ME);
689
690        ME.Type  =CaMouseEventT::CM_MOVE_Y;
691        ME.Amount=NewMousePosY-OldMousePosY;
692        if (ME.Amount!=0) Gui->ProcessDeviceEvent(ME);
693
694        m_LastMousePos=IN_OTHER_2D_GUI;
695    }
696}
697
698
699void MainCanvasT::OnMouseWheel(wxMouseEvent& ME)
700{
701    if (m_InitState!=INIT_SUCCESS) return;
702
703    CaMouseEventT MouseEvent;
704
705    MouseEvent.Type  =CaMouseEventT::CM_MOVE_Z;
706    MouseEvent.Amount=ME.GetWheelDelta();
707
708    cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent);
709}
710
711
712void MainCanvasT::OnLMouseDown(wxMouseEvent& ME)
713{
714    if (m_InitState!=INIT_SUCCESS) return;
715
716    CaMouseEventT MouseEvent;
717
718    MouseEvent.Type  =CaMouseEventT::CM_BUTTON0;
719    MouseEvent.Amount=1;
720
721    cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent);
722}
723
724
725void MainCanvasT::OnLMouseUp(wxMouseEvent& ME)
726{
727    if (m_InitState!=INIT_SUCCESS) return;
728
729    CaMouseEventT MouseEvent;
730
731    MouseEvent.Type=CaMouseEventT::CM_BUTTON0;
732    MouseEvent.Amount=0;
733
734    cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent);
735}
736
737
738void MainCanvasT::OnRMouseDown(wxMouseEvent& ME)
739{
740    if (m_InitState!=INIT_SUCCESS) return;
741
742    CaMouseEventT MouseEvent;
743
744    MouseEvent.Type  =CaMouseEventT::CM_BUTTON1;
745    MouseEvent.Amount=1;
746
747    cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent);
748}
749
750
751void MainCanvasT::OnRMouseUp(wxMouseEvent& ME)
752{
753    if (m_InitState!=INIT_SUCCESS) return;
754
755    CaMouseEventT MouseEvent;
756
757    MouseEvent.Type  =CaMouseEventT::CM_BUTTON1;
758    MouseEvent.Amount=0;
759
760    cf::GuiSys::GuiMan->ProcessDeviceEvent(MouseEvent);
761}
762
763
764struct KeyCodePairT
765{
766    wxKeyCode              wxKC;
767    CaKeyboardEventT::KeyT CaKC;
768};
769
770
771static const KeyCodePairT KeyCodes[]=
772{
773    { WXK_BACK,             CaKeyboardEventT::CK_BACKSPACE },
774    { WXK_TAB,              CaKeyboardEventT::CK_TAB },
775    { WXK_RETURN,           CaKeyboardEventT::CK_RETURN },
776    { WXK_ESCAPE,           CaKeyboardEventT::CK_ESCAPE },
777    { WXK_SPACE,            CaKeyboardEventT::CK_SPACE },
778    { (wxKeyCode)48,        CaKeyboardEventT::CK_0 },
779    { (wxKeyCode)49,        CaKeyboardEventT::CK_1 },
780    { (wxKeyCode)50,        CaKeyboardEventT::CK_2 },
781    { (wxKeyCode)51,        CaKeyboardEventT::CK_3 },
782    { (wxKeyCode)52,        CaKeyboardEventT::CK_4 },
783    { (wxKeyCode)53,        CaKeyboardEventT::CK_5 },
784    { (wxKeyCode)54,        CaKeyboardEventT::CK_6 },
785    { (wxKeyCode)55,        CaKeyboardEventT::CK_7 },
786    { (wxKeyCode)56,        CaKeyboardEventT::CK_8 },
787    { (wxKeyCode)57,        CaKeyboardEventT::CK_9 },
788    { (wxKeyCode)65,        CaKeyboardEventT::CK_A },
789    { (wxKeyCode)66,        CaKeyboardEventT::CK_B },
790    { (wxKeyCode)67,        CaKeyboardEventT::CK_C },
791    { (wxKeyCode)68,        CaKeyboardEventT::CK_D },
792    { (wxKeyCode)69,        CaKeyboardEventT::CK_E },
793    { (wxKeyCode)70,        CaKeyboardEventT::CK_F },
794    { (wxKeyCode)71,        CaKeyboardEventT::CK_G },
795    { (wxKeyCode)72,        CaKeyboardEventT::CK_H },
796    { (wxKeyCode)73,        CaKeyboardEventT::CK_I },
797    { (wxKeyCode)74,        CaKeyboardEventT::CK_J },
798    { (wxKeyCode)75,        CaKeyboardEventT::CK_K },
799    { (wxKeyCode)76,        CaKeyboardEventT::CK_L },
800    { (wxKeyCode)77,        CaKeyboardEventT::CK_M },
801    { (wxKeyCode)78,        CaKeyboardEventT::CK_N },
802    { (wxKeyCode)79,        CaKeyboardEventT::CK_O },
803    { (wxKeyCode)80,        CaKeyboardEventT::CK_P },
804    { (wxKeyCode)81,        CaKeyboardEventT::CK_Q },
805    { (wxKeyCode)82,        CaKeyboardEventT::CK_R },
806    { (wxKeyCode)83,        CaKeyboardEventT::CK_S },
807    { (wxKeyCode)84,        CaKeyboardEventT::CK_T },
808    { (wxKeyCode)85,        CaKeyboardEventT::CK_U },
809    { (wxKeyCode)86,        CaKeyboardEventT::CK_V },
810    { (wxKeyCode)87,        CaKeyboardEventT::CK_W },
811    { (wxKeyCode)88,        CaKeyboardEventT::CK_X },
812    { (wxKeyCode)89,        CaKeyboardEventT::CK_Y },
813    { (wxKeyCode)90,        CaKeyboardEventT::CK_Z },
814    { WXK_DELETE,           CaKeyboardEventT::CK_DELETE },
815    //{ WXK_START,            CaKeyboardEventT:: },
816    //{ WXK_LBUTTON,          CaKeyboardEventT:: },
817    //{ WXK_RBUTTON,          CaKeyboardEventT:: },
818    //{ WXK_CANCEL,           CaKeyboardEventT:: },
819    //{ WXK_MBUTTON,          CaKeyboardEventT:: },
820    //{ WXK_CLEAR,            CaKeyboardEventT:: },
821    { WXK_SHIFT,            CaKeyboardEventT::CK_LSHIFT },
822    { WXK_ALT,              CaKeyboardEventT::CK_LMENU },
823    { WXK_CONTROL,          CaKeyboardEventT::CK_LCONTROL },
824    //{ WXK_MENU,             CaKeyboardEventT:: },
825    { WXK_PAUSE,            CaKeyboardEventT::CK_PAUSE },
826    { WXK_CAPITAL,          CaKeyboardEventT::CK_CAPITAL },
827    { WXK_END,              CaKeyboardEventT::CK_END },
828    { WXK_HOME,             CaKeyboardEventT::CK_HOME },
829    { WXK_LEFT,             CaKeyboardEventT::CK_LEFT },
830    { WXK_UP,               CaKeyboardEventT::CK_UP },
831    { WXK_RIGHT,            CaKeyboardEventT::CK_RIGHT },
832    { WXK_DOWN,             CaKeyboardEventT::CK_DOWN },
833    //{ WXK_SELECT,           CaKeyboardEventT:: },
834    //{ WXK_PRINT,            CaKeyboardEventT:: },
835    //{ WXK_EXECUTE,          CaKeyboardEventT:: },
836    //{ WXK_SNAPSHOT,         CaKeyboardEventT:: },
837    { WXK_INSERT,           CaKeyboardEventT::CK_INSERT },
838    //{ WXK_HELP,             CaKeyboardEventT:: },
839    { WXK_NUMPAD0,          CaKeyboardEventT::CK_NUMPAD0 },
840    { WXK_NUMPAD1,          CaKeyboardEventT::CK_NUMPAD1 },
841    { WXK_NUMPAD2,          CaKeyboardEventT::CK_NUMPAD2 },
842    { WXK_NUMPAD3,          CaKeyboardEventT::CK_NUMPAD3 },
843    { WXK_NUMPAD4,          CaKeyboardEventT::CK_NUMPAD4 },
844    { WXK_NUMPAD5,          CaKeyboardEventT::CK_NUMPAD5 },
845    { WXK_NUMPAD6,          CaKeyboardEventT::CK_NUMPAD6 },
846    { WXK_NUMPAD7,          CaKeyboardEventT::CK_NUMPAD7 },
847    { WXK_NUMPAD8,          CaKeyboardEventT::CK_NUMPAD8 },
848    { WXK_NUMPAD9,          CaKeyboardEventT::CK_NUMPAD9 },
849    //{ WXK_MULTIPLY,         CaKeyboardEventT:: },
850    //{ WXK_ADD,              CaKeyboardEventT:: },
851    { WXK_SEPARATOR,        CaKeyboardEventT::CK_COMMA },
852    { WXK_SUBTRACT,         CaKeyboardEventT::CK_MINUS },
853    { WXK_DECIMAL,          CaKeyboardEventT::CK_PERIOD },
854    { WXK_DIVIDE,           CaKeyboardEventT::CK_SLASH },
855    { WXK_F1,               CaKeyboardEventT::CK_F1 },
856    { WXK_F2,               CaKeyboardEventT::CK_F2 },
857    { WXK_F3,               CaKeyboardEventT::CK_F3 },
858    { WXK_F4,               CaKeyboardEventT::CK_F4 },
859    { WXK_F5,               CaKeyboardEventT::CK_F5 },
860    { WXK_F6,               CaKeyboardEventT::CK_F6 },
861    { WXK_F7,               CaKeyboardEventT::CK_F7 },
862    { WXK_F8,               CaKeyboardEventT::CK_F8 },
863    { WXK_F9,               CaKeyboardEventT::CK_F9 },
864    { WXK_F10,              CaKeyboardEventT::CK_F10 },
865    { WXK_F11,              CaKeyboardEventT::CK_F11 },
866    { WXK_F12,              CaKeyboardEventT::CK_F12 },
867    { WXK_F13,              CaKeyboardEventT::CK_F13 },
868    { WXK_F14,              CaKeyboardEventT::CK_F14 },
869    { WXK_F15,              CaKeyboardEventT::CK_F15 },
870    { WXK_NUMLOCK,          CaKeyboardEventT::CK_NUMLOCK },
871    { WXK_SCROLL,           CaKeyboardEventT::CK_SCROLL },
872    { WXK_PAGEUP,           CaKeyboardEventT::CK_PGUP },
873    { WXK_PAGEDOWN,         CaKeyboardEventT::CK_PGDN },
874    //{ WXK_NUMPAD_SPACE,     CaKeyboardEventT:: },
875    //{ WXK_NUMPAD_TAB,       CaKeyboardEventT:: },
876    { WXK_NUMPAD_ENTER,     CaKeyboardEventT::CK_NUMPADENTER },
877    //{ WXK_NUMPAD_F1,        CaKeyboardEventT:: },
878    //{ WXK_NUMPAD_F2,        CaKeyboardEventT:: },
879    //{ WXK_NUMPAD_F3,        CaKeyboardEventT:: },
880    //{ WXK_NUMPAD_F4,        CaKeyboardEventT:: },
881    //{ WXK_NUMPAD_HOME,      CaKeyboardEventT:: },
882    //{ WXK_NUMPAD_LEFT,      CaKeyboardEventT:: },
883    //{ WXK_NUMPAD_UP,        CaKeyboardEventT:: },
884    //{ WXK_NUMPAD_RIGHT,     CaKeyboardEventT:: },
885    //{ WXK_NUMPAD_DOWN,      CaKeyboardEventT:: },
886    //{ WXK_NUMPAD_PAGEUP,    CaKeyboardEventT:: },
887    //{ WXK_NUMPAD_PAGEDOWN,  CaKeyboardEventT:: },
888    //{ WXK_NUMPAD_END,       CaKeyboardEventT:: },
889    //{ WXK_NUMPAD_BEGIN,     CaKeyboardEventT:: },
890    //{ WXK_NUMPAD_INSERT,    CaKeyboardEventT:: },
891    //{ WXK_NUMPAD_DELETE,    CaKeyboardEventT:: },
892    //{ WXK_NUMPAD_EQUAL,     CaKeyboardEventT:: },
893    { WXK_NUMPAD_MULTIPLY,  CaKeyboardEventT::CK_MULTIPLY },
894    { WXK_NUMPAD_ADD,       CaKeyboardEventT::CK_ADD },
895    { WXK_NUMPAD_SEPARATOR, CaKeyboardEventT::CK_NUMPADCOMMA },
896    { WXK_NUMPAD_SUBTRACT,  CaKeyboardEventT::CK_SUBTRACT },
897    { WXK_NUMPAD_DECIMAL,   CaKeyboardEventT::CK_DECIMAL },
898    { WXK_NUMPAD_DIVIDE,    CaKeyboardEventT::CK_DIVIDE },
899    { WXK_WINDOWS_LEFT,     CaKeyboardEventT::CK_LWIN },
900    { WXK_WINDOWS_RIGHT,    CaKeyboardEventT::CK_RWIN },
901    //{ WXK_WINDOWS_MENU ,    CaKeyboardEventT:: },
902    //{ WXK_COMMAND,          CaKeyboardEventT:: },
903    //{ (wxKeyCode)0, (CaKeyboardEventT::KeyT)0 }
904};
905
906
907void MainCanvasT::OnKeyDown(wxKeyEvent& KE)
908{
909    if (m_InitState!=INIT_SUCCESS) return;
910
911    switch (KE.GetKeyCode())
912    {
913        case WXK_F1:
914        {
915            // Activate the in-game console GUI (it's "F1" now, not CK_GRAVE ("^", accent grave) any longer).
916            extern ConVarT    Options_ServerGameName;
917            cf::GuiSys::GuiI* ConsoleGui=cf::GuiSys::GuiMan->Find("Games/"+Options_ServerGameName.GetValueString()+"/GUIs/Console.cgui");
918
919            // ConsoleGui should be the same as in Initialize(), but could be NULL on file not found, parse error, etc.
920            if (ConsoleGui!=NULL && !ConsoleGui->GetIsActive())
921            {
922                ConsoleGui->Activate();
923                cf::GuiSys::GuiMan->BringToFront(ConsoleGui);
924
925                static bool InitialHelpMsgPrinted=false;
926
927                if (!InitialHelpMsgPrinted)
928                {
929                    Console->Print("\n");
930                    Console->Print("Welcome to the Cafu console!\n");
931                    Console->Print("Enter   help()   to obtain more information.\n");
932                    Console->Print("\n");
933                    InitialHelpMsgPrinted=true;
934                }
935
936                // Handled the key.
937                return;
938            }
939
940            // Let the active GUI handle the key below (e.g. for closing the console again).
941            break;
942        }
943
944        case WXK_F5:
945        {
946            TakeScreenshot();
947            return;
948        }
949
950        case WXK_F11:
951        {
952            if (!wxGetApp().IsCustomVideoMode())
953            {
954                // Switching full-screen mode with F11 only makes sense if we didn't set a custom video mode (screen resolution).
955                // See AppCafuT::OnInit() for more details.
956                m_Parent->ShowFullScreen(!m_Parent->IsFullScreen());
957            }
958            return;
959        }
960    }
961
962    // Look for the pressed keys keycode in the table and translate it to a CaKeyCode if found.
963    for (int KeyCodeNr=0; KeyCodes[KeyCodeNr].wxKC!=0; KeyCodeNr++)
964    {
965        if (KeyCodes[KeyCodeNr].wxKC==KE.GetKeyCode())
966        {
967            CaKeyboardEventT KeyboardEvent;
968
969            KeyboardEvent.Type=CaKeyboardEventT::CKE_KEYDOWN;
970            KeyboardEvent.Key =KeyCodes[KeyCodeNr].CaKC;
971
972            cf::GuiSys::GuiMan->ProcessDeviceEvent(KeyboardEvent);
973            // We should probably return here if the GuiMan handled the key, break (only when unhandled) for calling KE.Skip() otherwise.
974            break;
975        }
976    }
977
978    KE.Skip();
979}
980
981
982void MainCanvasT::OnKeyUp(wxKeyEvent& KE)
983{
984    if (m_InitState!=INIT_SUCCESS) return;
985
986    // Look for the released keys keycode in the table and translate it to a CaKeyCode if found.
987    for (int KeyCodeNr=0; KeyCodes[KeyCodeNr].wxKC!=0; KeyCodeNr++)
988    {
989        if (KeyCodes[KeyCodeNr].wxKC==KE.GetKeyCode())
990        {
991            CaKeyboardEventT KeyboardEvent;
992
993            KeyboardEvent.Type=CaKeyboardEventT::CKE_KEYUP;
994            KeyboardEvent.Key =KeyCodes[KeyCodeNr].CaKC;
995
996            cf::GuiSys::GuiMan->ProcessDeviceEvent(KeyboardEvent);
997            return;
998        }
999    }
1000
1001    KE.Skip();
1002}
1003
1004
1005void MainCanvasT::OnKeyChar(wxKeyEvent& KE)
1006{
1007    if (m_InitState!=INIT_SUCCESS) return;
1008
1009    CaKeyboardEventT KeyboardEvent;
1010
1011    KeyboardEvent.Type=CaKeyboardEventT::CKE_CHAR;
1012    KeyboardEvent.Key =KE.GetKeyCode();
1013
1014    cf::GuiSys::GuiMan->ProcessDeviceEvent(KeyboardEvent);
1015}
Note: See TracBrowser for help on using the browser.