| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- //========================================================================
- // GameCode.cpp : Defines the Application layer for GameCode4
- //
- // Part of the GameCode4 Application
- //
- // GameCode4 is the sample application that encapsulates much of the source code
- // discussed in "Game Coding Complete - 4th Edition" by Mike McShaffry and David
- // "Rez" Graham, published by Charles River Media.
- // ISBN-10: 1133776574 | ISBN-13: 978-1133776574
- //
- // If this source code has found it's way to you, and you think it has helped you
- // in any way, do the authors a favor and buy a new copy of the book - there are
- // detailed explanations in it that compliment this code well. Buy a copy at Amazon.com
- // by clicking here:
- // http://www.amazon.com/gp/product/1133776574/ref=olp_product_details?ie=UTF8&me=&seller=
- //
- // There's a companion web site at http://www.mcshaffry.com/GameCode/
- //
- // The source code is managed and maintained through Google Code:
- // http://code.google.com/p/gamecode4/
- //
- // (c) Copyright 2012 Michael L. McShaffry and David Graham
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU Lesser GPL v3
- // as published by the Free Software Foundation.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
- // http://www.gnu.org/licenses/lgpl-3.0.txt for more details.
- //
- // You should have received a copy of the GNU Lesser GPL v3
- // along with this program; if not, write to the Free Software
- // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- //
- //========================================================================
- #include "../Unix/GameCodeStd.h"
- #include "../Mainloop/Initialization.h"
- #include "../Actors/BaseScriptComponent.h"
- #include "../ResourceCache/ResCache.h"
- #include "../Graphics3D/OpenGLRenderer.h"
- #include "../ResourceCache/XmlResource.h"
- #include "../LUAScripting/ScriptExports.h"
- #include "../LUAScripting/ScriptProcess.h"
- #include "../LUAScripting/LuaStateManager.h"
- #include "../EventManager/EventManagerImpl.h"
- #include "../Debugging/Logger.h"
- #include "../Utilities/Dialogs.h"
- #include <imgui.h>
- #include <imgui_impl_sdl3.h>
- #include <imgui_impl_opengl3.h>
- #include <GL/glew.h>
- GameCodeApp *g_pApp = NULL;
- const char* SCRIPT_PREINIT_FILE = "Scripts/PreInit.lua";
- const unsigned int SCREEN_REFRESH_RATE(1000/60);
- //========================================================================
- //
- // GameCodeApp implementation
- //
- //========================================================================
- GameCodeApp::GameCodeApp()
- {
- g_pApp = this;
- m_pGame = NULL;
- m_bIsRunning = false;
- m_bQuitRequested = false;
- m_bQuitting = false;
- }
- //===================================================================
- // Win32 Specific Stuff
- //
- // InitInstance - this checks system resources, creates your window, and launches the game
- //
- // preprocessor macro setting in the C++ options of the project provides the base macro
- // C preprocessor string concatenation takes care of the rest.
- //
- // GameCodeApp::InitInstance - Chapter 5, page 136
- //
- //===================================================================
- bool GameCodeApp::InitInstance(int screenWidth, int screenHeight)
- {
- // Check for adequate machine resources.
- bool resourceCheck = false;
- while (!resourceCheck)
- {
- const size_t physicalRAM = 512 * MEGABYTE;
- const size_t virtualRAM = 1024 * MEGABYTE;
- const size_t diskSpace = 10 * MEGABYTE;
- if (!CheckStorage(diskSpace))
- return false;
- const unsigned int minCpuSpeed = 1300; // 1.3Ghz
- unsigned int thisCPU = ReadCPUSpeed();
- if (thisCPU < minCpuSpeed)
- {
- GCC_ERROR("GetCPUSpeed reports CPU is too slow for this game.");
- return false;
- }
- resourceCheck = true;
- }
- IResourceFile *zipFile = (m_bIsEditorRunning || m_Options.m_useDevelopmentDirectories) ?
- GCC_NEW DevelopmentResourceZipFile(L"Assets.zip", DevelopmentResourceZipFile::Editor) :
- GCC_NEW ResourceZipFile(L"Assets.zip");
- m_ResCache = GCC_NEW ResCache(50, zipFile);
- if (!m_ResCache->Init())
- {
- GCC_ERROR("Failed to initialize resource cache! Are your paths set up correctly?");
- return false;
- }
- // extern std::shared_ptr<IResourceLoader> CreateWAVResourceLoader();
- // extern std::shared_ptr<IResourceLoader> CreateOGGResourceLoader();
- extern std::shared_ptr<IResourceLoader> CreateDDSResourceLoader();
- extern std::shared_ptr<IResourceLoader> CreateJPGResourceLoader();
- extern std::shared_ptr<IResourceLoader> CreateXmlResourceLoader();
- extern std::shared_ptr<IResourceLoader> CreateGlbMeshResourceLoader();
- extern std::shared_ptr<IResourceLoader> CreateScriptResourceLoader();
- // Note - register these in order from least specific to most specific! They get pushed onto a list.
- // RegisterLoader is discussed in Chapter 5, page 142
- // m_ResCache->RegisterLoader(CreateWAVResourceLoader());
- // m_ResCache->RegisterLoader(CreateOGGResourceLoader());
- // m_ResCache->RegisterLoader(CreateDDSResourceLoader());
- m_ResCache->RegisterLoader(CreateJPGResourceLoader());
- m_ResCache->RegisterLoader(CreateXmlResourceLoader());
- m_ResCache->RegisterLoader(CreateGlbMeshResourceLoader());
- m_ResCache->RegisterLoader(CreateScriptResourceLoader());
-
- if(!LoadStrings("English"))
- {
- GCC_ERROR("Failed to load strings");
- return false;
- }
- // Rez up the Lua State manager now, and run the initial script - discussed in Chapter 5, page 144.
- if (!LuaStateManager::Create())
- {
- GCC_ERROR("Failed to initialize Lua");
- return false;
- }
- // Load the preinit file. This is within braces to create a scope and destroy the resource once it's loaded. We
- // don't need to do anything with it, we just need to load it.
- {
- Resource resource(SCRIPT_PREINIT_FILE);
- std::shared_ptr<ResHandle> pResourceHandle = m_ResCache->GetHandle(&resource); // this actually loads the script from the zip file
- }
- // // Register function exported from C++
- ScriptExports::Register();
- ScriptProcess::RegisterScriptClass();
- BaseScriptComponent::RegisterScriptFunctions();
- // The event manager should be created next so that subsystems can hook in as desired.
- // Discussed in Chapter 5, page 144
- m_pEventManager = GCC_NEW EventManager("GameCodeApp Event Mgr", true);
- if (!m_pEventManager)
- {
- GCC_ERROR("Failed to create EventManager.");
- return false;
- }
- SDL_InitFlags initSystems = SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMEPAD | SDL_INIT_EVENTS;
- if (!SDL_Init(initSystems)) {
- GCC_ERROR(SDL_GetError());
- return false;
- }
- static int const OPENGL_MAJOR_VERSION = 4;
- static int const OPENGL_MINOR_VERSION = 6;
-
- if (!SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, OPENGL_MAJOR_VERSION)) {
- GCC_ERROR(SDL_GetError());
- }
-
- if (!SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, OPENGL_MINOR_VERSION)) {
- GCC_ERROR(SDL_GetError());
- }
-
- if (!SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)) {
- GCC_ERROR(SDL_GetError());
- }
-
- if (!SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE)) {
- GCC_ERROR(SDL_GetError());
- }
- if (!SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG)) {
- GCC_ERROR(SDL_GetError());
- }
- m_Window = SDL_CreateWindow(
- VGetGameTitle().c_str(),
- screenWidth,
- screenHeight,
- /*settings.window_flags |*/ SDL_WINDOW_OPENGL
- );
- if (!m_Window) {
- GCC_ERROR(SDL_GetError());
- return false;
- }
- // TODO(munnellg) - Set an icon
- // SDL_SetWindowIcon(m_Window, VGetIcon());
- m_GlContext = SDL_GL_CreateContext(m_Window);
- if (!m_GlContext) {
- GCC_ERROR(SDL_GetError());
- return false;
- }
- GLuint err = glewInit();
- if (err != GLEW_OK) {
- GCC_ERROR((char*) glewGetString(err));
- return false;
- }
- ImGui::CreateContext();
- ImGuiIO& io = ImGui::GetIO(); (void)io;
- io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
- // Setup Dear ImGui style
- ImGui::StyleColorsDark();
- //ImGui::StyleColorsLight();
- // Setup scaling
- float main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
- ImGuiStyle& style = ImGui::GetStyle();
- style.ScaleAllSizes(main_scale); // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
- style.FontScaleDpi = main_scale; // Set initial font scale. (in docking branch: using io.ConfigDpiScaleFonts=true automatically overrides this for every window depending on the current monitor)
- // Setup Platform/Renderer backends
- ImGui_ImplSDL3_InitForOpenGL(m_Window, m_GlContext);
- ImGui_ImplOpenGL3_Init(nullptr);
- // TODO(munnellg) - implement this
- // initialize the directory location you can store save game files
- // _tcscpy_s(m_saveGameDirectory, GetSaveGameDirectory(GetHwnd(), VGetGameAppDirectory()));
- if (GetRendererImpl() == Renderer_OpenGL46)
- {
- m_Renderer = std::shared_ptr<IRenderer>(GCC_NEW OpenGLRenderer46());
- }
- else
- {
- GCC_FATAL("Unknown renderer");
- }
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- m_Renderer->VSetBackgroundColor(255, 20, 20, 200);
- m_Renderer->VOnRestore();
- // You usually must have an HWND to initialize your game views...
- // VCreateGameAndView - Chapter 5, page 145
- m_pGame = VCreateGameAndView();
- if (!m_pGame)
- return false;
- // now that all the major systems are initialized, preload resources
- // Preload calls are discussed in Chapter 5, page 148
- m_ResCache->Preload("*.ogg", NULL);
- m_ResCache->Preload("*.dds", NULL);
- m_ResCache->Preload("*.jpg", NULL);
- m_screenSize = Point(screenWidth, screenHeight);
- m_bIsRunning = true;
- return true;
- }
- bool GameCodeApp::VLoadGame(void)
- {
- // Read the game options and see what the current game
- // needs to be - all of the game graphics are initialized by now, too...
- return m_pGame->VLoadGame(m_Options.m_Level.c_str());
- }
- int GameCodeApp::OnSysCommand(SDL_Event const &e)
- {
- switch (e.type)
- {
- case SDL_EVENT_QUIT:
- {
- // The quit dialog confirmation would appear once for
- // every SC_CLOSE we get - which happens multiple times
- // if modal dialogs are up. This now uses the g_QuitNoPrompt
- // flag to only prompt when receiving a SC_CLOSE that isn't
- // generated by us (identified by g_QuitNoPrompt).
- // ET - 05/21/01 - Bug #1916 - Begin
- // We were receiving multiple close dialogs
- // when closing again ALT-F4 while the close
- // confirmation dialog was up.
- // Eat if already servicing a close
- if (m_bQuitRequested)
- return true;
- // Wait for the application to be restored
- // before going any further with the new
- // screen. Flash until the person selects
- // that they want to restore the game, then
- // reinit the display if fullscreen.
- // The reinit is necessary otherwise the game
- // will switch to windowed mode.
- // Quit requested
- m_bQuitRequested = true;
- // Prompt
- // TODO(munnellg): The book uses its own UI class for this. We should do the same
- // int result = MessageBox(m_Window, "Are you sure you want to quit?", "Quit?", MB_YESNO);
- // if (result == IDNO)
- // {
- // // Bail - quit aborted
-
- // // Reset quit requested flag
- // m_bQuitRequested = false;
-
- // return true;
- // }
- m_bQuitting = true;
- // Reset the quit after any other dialogs have popped up from this close
- m_bQuitRequested = false;
- }
- }
- return 0;
- }
- bool GameCodeApp::LoadStrings(std::string language)
- {
- std::string languageFile = "Strings/";
- languageFile += language;
- languageFile += ".xml";
- TiXmlElement* pRoot = XmlResourceLoader::LoadAndReturnRootXmlElement(languageFile.c_str());
- if (!pRoot)
- {
- GCC_ERROR("Strings are missing.");
- return false;
- }
- // Loop through each child element and load the component
- for (TiXmlElement* pElem = pRoot->FirstChildElement(); pElem; pElem = pElem->NextSiblingElement())
- {
- const char *pKey=pElem->Attribute("id");
- const char *pText=pElem->Attribute("value");
- const char *pHotkey=pElem->Attribute("hotkey");
- if (pKey && pText)
- {
- m_textResource[std::string(pKey)] = std::string(pText);
- // TODO(munnellg) - maybe implement? Don't see what it's doing, but can look when SDL onboarded
- // if (pHotKey)
- // {
- // m_hotkeys[std::string(wideKey)] = MapCharToKeycode(*pHotkey);
- // }
- }
- }
- return true;
- }
- //----------------------------------------------------------
- // GameCodeApp::GetString - Chapter 5, page 144
- //
- // creates a string from a string resource ID in the string table
- // stored in a special DLL, LANG.DLL, so game text strings
- // can be language independant
- //
- std::string GameCodeApp::GetString(std::string sID)
- {
- auto localizedString = m_textResource.find(sID);
- if (localizedString == m_textResource.end())
- {
- GCC_ASSERT(0 && "String not found!");
- return "";
- }
- return localizedString->second;
- }
- //----------------------------------------------------------
- // Win32 Specific Message Handlers
- //
- // WndProc - the main message handler for the window class
- //
- // OnNcCreate - this is where you can set window data before it is created
- // OnMove - called whenever the window moves; used to update members of g_App
- // OnDeviceChange - called whenever you eject the CD-ROM.
- // OnDisplayChange - called whenever the user changes the desktop settings
- // OnPowerBroadcast - called whenever a power message forces a shutdown
- // OnActivate - called whenever windows on the desktop change focus.
- //
- // Note: pUserContext added to comply with DirectX 9c - June 2005 Update
- //
- int GameCodeApp::MsgProc(SDL_Event &e)
- {
- int result = ImGui_ImplSDL3_ProcessEvent(&e);
-
- ImGuiIO& io = ImGui::GetIO();
- switch (e.type)
- {
- case SDL_EVENT_QUIT:
- // case SDL_APP_TERMINATING:
- // case SDL_APP_LOWMEMORY:
- // case SDL_APP_WILLENTERBACKGROUND:
- // case SDL_APP_DIDENTERBACKGROUND:
- // case SDL_APP_WILLENTERFOREGROUND:
- // case SDL_APP_DIDENTERFOREGROUND:
- // case SDL_DISPLAYEVENT:
- // case SDL_WINDOWEVENT:
- // case SDL_SYSWMEVENT:
- result = g_pApp->OnSysCommand(e);
- break;
- case SDL_EVENT_KEY_DOWN:
- case SDL_EVENT_KEY_UP:
- if (io.WantCaptureKeyboard)
- {
- break;
- }
- case SDL_EVENT_MOUSE_MOTION:
- case SDL_EVENT_MOUSE_BUTTON_DOWN:
- case SDL_EVENT_MOUSE_BUTTON_UP:
- if (io.WantCaptureMouse)
- {
- break;
- }
- case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
- case SDL_EVENT_JOYSTICK_BUTTON_UP:
- case SDL_EVENT_JOYSTICK_AXIS_MOTION:
- case SDL_EVENT_JOYSTICK_HAT_MOTION:
- {
- //
- // See Chapter 10, page 278 for more explanation of this code.
- //
- if (g_pApp->m_pGame)
- {
- BaseGameLogic *pGame = g_pApp->m_pGame;
- // Note the reverse order! User input is grabbed first from the view that is on top,
- // which is the last one in the list.
- for(GameViewList::reverse_iterator i=pGame->m_gameViews.rbegin(); i!=pGame->m_gameViews.rend(); ++i)
- {
- if ( (*i)->VOnMsgProc( e ) )
- {
- result = true;
- break; // WARNING! This breaks out of the for loop.
- }
- }
- }
- break;
- }
- }
- return 0;
- }
- GameCodeApp::Renderer GameCodeApp::GetRendererImpl()
- {
- return Renderer_OpenGL46;
- }
- void GameCodeApp::RunMainLoop()
- {
- Uint64 lastUpdate = SDL_GetTicks();
- Uint64 lastDraw = SDL_GetTicks();
- while (!m_bQuitting)
- {
- // process any inputs
- PumpSDLEvents();
- OnUpdateGame((float) SDL_GetTicks(), ((float) (SDL_GetTicks() - lastUpdate)) / 1000.0f);
- lastUpdate = SDL_GetTicks();
- // render the frame
- OnOpenGLFrameRender((float) SDL_GetTicks(), ((float) (SDL_GetTicks() - lastDraw)) / 1000.0f);
- lastDraw = SDL_GetTicks();
- }
- }
- void GameCodeApp::Shutdown()
- {
- // TODO(munnellg): release assets and shut down gracefully
- }
- void GameCodeApp::PumpSDLEvents()
- {
- SDL_Event e;
- while (SDL_PollEvent(&e))
- {
- MsgProc(e);
- }
- }
- //--------------------------------------------------------------------------------------
- // This callback function will be called once at the beginning of every frame. This is the
- // best location for your application to handle updates to the scene, but is not
- // intended to contain actual rendering calls, which should instead be placed in the
- // OnFrameRender callback.
- //
- // See Game Coding Complete - 4th Edition - Chapter X, page Y
- //--------------------------------------------------------------------------------------
- void GameCodeApp::OnUpdateGame(double fTime, float fElapsedTime)
- {
- // if (g_pApp->HasModalDialog())
- // {
- // // don't update the game if a modal dialog is up.
- // return;
- // }
- // if (g_pApp->m_bQuitting)
- // {
- // PostMessage(g_pApp->GetHwnd(), WM_CLOSE, 0, 0);
- // }
- if (g_pApp->m_pGame)
- {
- IEventManager::Get()->VUpdate(20); // allow event queue to process for up to 20 ms
- // if (g_pApp->m_pBaseSocketManager)
- // g_pApp->m_pBaseSocketManager->DoSelect(0); // pause 0 microseconds
- g_pApp->m_pGame->VOnUpdate(float(fTime), fElapsedTime);
- }
- }
- void GameCodeApp::OnOpenGLFrameRender(double fTime, float fElapsedTime)
- {
- static uint64_t lastDraw = 0;
- uint64_t currTick = SDL_GetTicks();
-
- if (currTick == lastDraw)
- return;
- // if (currTick - lastDraw > SCREEN_REFRESH_RATE)
- // {
- g_pApp->m_Renderer->VPreRender();
- BaseGameLogic *pGame = g_pApp->m_pGame;
- for (GameViewList::iterator i = pGame->m_gameViews.begin(),
- end = pGame->m_gameViews.end(); i!=end; ++i)
- {
- (*i)->VOnRender(fTime, fElapsedTime);
- }
- g_pApp->m_pGame->VRenderDiagnostics();
- g_pApp->m_Renderer->VPostRender();
- // record the last successful paint
- lastDraw = currTick;
- // }
- }
|