// Initialize the engine and all systems associated with it(if there is any) void InitSystem(HINSTANCE hInstance, char *title, int width, int height) { state = {}; LoadFileDirectory(&state); char DLLFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game.dll", DLLFullPath, sizeof(DLLFullPath)); char tempDLLFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game_temp.dll", tempDLLFullPath, sizeof(tempDLLFullPath)); char PDBFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game.pdb", PDBFullPath, sizeof(PDBFullPath)); char tempPDBFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game_temp.pdb", tempPDBFullPath, sizeof(tempPDBFullPath)); Window = {}; Window.WindowCallback = WindowCallBack; InitWindow(hInstance, &Window, title, width, height); InitInputManager(&Keys); Game = {}; Dimensions.Width = (float)Window.Width; Dimensions.Height = (float)Window.Height; Game = LoadGameCode(DLLFullPath, tempDLLFullPath); CopyFile(PDBFullPath, tempPDBFullPath, FALSE); Game.Game_Init(Dimensions); }
int main(void) { uint64 PerfCountFrequency = SDL_GetPerformanceFrequency(); SDL_Event Event; SDL_Window *Window; SDL_Renderer *Renderer; if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) != 0) { fprintf(stderr, "Could not initialize SDL: %s\n", SDL_GetError()); return -1; } atexit(SDL_Quit); int WindowWidth = 1300; int WindowHeight = 870; int BytesPerPixel = 4; Window = SDL_CreateWindow("Echelon", 0, 0, WindowWidth, WindowHeight, SDL_WINDOW_RESIZABLE); if(Window) { Renderer = SDL_CreateRenderer(Window, -1, 0); if(Renderer) { GlobalRunning = true; window_dimensions Dimensions = SDLGetWindowDimensions(Window); SDLCreateNewTexture(&GlobalBuffer, Renderer, Dimensions.Width, Dimensions.Height, BytesPerPixel); uint64 LastCounter = SDL_GetPerformanceCounter(); uint64 LastCycleCount = _rdtsc(); real64 DebugTimer = 0; real64 FPSTimer = 0; real64 UpdateTimer = 0; uint32 FPS = 0; uint32 UPS = 0; keyboard_input KeyboardInput = {}; gamepad_input GamePadInput = {}; game_code Game = LoadGameCode(); game_memory GameMemory = {}; GameMemory.IsInitialized = false; GameMemory.PlayRumble = SDLPlayRumble; GameMemory.WindowDimensions = SDLGetWindowDimensions(Window); GameMemory.PlatformDrawRenderQueue = DrawRenderQueueStub; GameMemory.PermanentStorageSize = Megabytes(100); GameMemory.PermanentStorage = mmap(0, GameMemory.PermanentStorageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); GameMemory.TransientStorageSize = Gigabytes(2); GameMemory.TransientStorage = mmap(0, GameMemory.TransientStorageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); Game.GameInit(&GameMemory); while(GlobalRunning) { // NOTE(Redab): This needs to while loop because we need // to handle events as long as they are available. while(SDL_PollEvent(&Event)) { SDLHandleEvent(&Event, &Dimensions); SDLHandleUserInput(&Event, &Game, &GameMemory, &KeyboardInput, &GamePadInput); } uint64 EndCycleCount = _rdtsc(); uint64 EndCounter = SDL_GetPerformanceCounter(); uint64 CounterElapsed = EndCounter - LastCounter; uint64 CyclesElapsed = EndCycleCount - LastCycleCount; // NOTE(Redab): CounterElapsed Contains the number of // clock cycles since last check. So we need to divide // this by the number of cycles per second which we // have in PerCountFrequency. Multiplied by 1000 to // get milliseconds. real64 SecondsPerFrame = ((real64)CounterElapsed / (real64)PerfCountFrequency); real64 MSPerFrame = SecondsPerFrame * 1000.0f; real64 KCPF = ((real64)CyclesElapsed / (1000.0f)); FPSTimer += MSPerFrame; UpdateTimer += MSPerFrame; DebugTimer += MSPerFrame; if(UpdateTimer >= (1000.0f / 60.0f)) { GameMemory.WindowDimensions = Dimensions; Game.GameUpdate(&GameMemory, &KeyboardInput, &GamePadInput, UpdateTimer / 1000.0f); UPS++; UpdateTimer = 0; } if(FPSTimer >= (1000.0f / 60.0f)) { SDLGameRender(&GlobalBuffer, &Game, &GameMemory); SDLBlitFrameToWindow(&GlobalBuffer, Renderer); FPS++; FPSTimer = 0; } if(DebugTimer >= 1000.0f) { printf("%.05fms/f, FPS: %d, UPS: %d, %.02fKc/f, Timer: %.02f\n", MSPerFrame, FPS, UPS, KCPF, DebugTimer); FPS = 0; UPS = 0; DebugTimer = 0; } LastCycleCount = EndCycleCount; LastCounter = EndCounter; } } else { printf("Failed to create SDL_Renderer: %s\n", SDL_GetError()); } } else { printf("Failed to create SDL_Window: %s\n", SDL_GetError()); } SDL_CloseAudio(); SDL_Quit(); return 0; }
inline void SDLHandleUserInput(SDL_Event *Event, game_code *GameCode, game_memory *GameMemory, keyboard_input *KeyboardInput, gamepad_input *GamePadInput) { int KeyCode = Event->key.keysym.sym; uint8 GamePadButton = Event->cbutton.button; const uint8 *KeyState = SDL_GetKeyboardState(NULL); switch(Event->type) { case SDL_KEYDOWN: if(KeyCode == SDLK_ESCAPE) { HandleButtonPress(&KeyboardInput->Escape); GlobalRunning = false; } if(KeyCode == SDLK_a) { HandleButtonPress(&KeyboardInput->A); UnloadGameCode(GameCode); *GameCode = LoadGameCode(); GameMemory->IsInitialized = false; } if(KeyState[SDL_SCANCODE_LALT] && KeyState[SDL_SCANCODE_RETURN] ) { if(!Fullscreen){ SDL_SetWindowFullscreen(SDL_GetWindowFromID(Event->window.windowID), SDL_WINDOW_FULLSCREEN); SDL_SetRelativeMouseMode((SDL_bool)SDL_ENABLE); Fullscreen = true; } else { SDL_SetWindowFullscreen(SDL_GetWindowFromID(Event->window.windowID), 0); SDL_SetRelativeMouseMode((SDL_bool)SDL_DISABLE); Fullscreen = false; } } if(KeyCode == SDLK_b) { HandleButtonPress(&KeyboardInput->B); } if(KeyCode == SDLK_f) { HandleButtonPress(&KeyboardInput->F); } if(KeyCode == SDLK_UP) { HandleButtonPress(&KeyboardInput->Up); } if(KeyCode == SDLK_DOWN) { HandleButtonPress(&KeyboardInput->Down); } if(KeyCode == SDLK_LEFT) { HandleButtonPress(&KeyboardInput->Left); } if(KeyCode == SDLK_RIGHT) { HandleButtonPress(&KeyboardInput->Right); } break; case SDL_KEYUP: if(KeyCode == SDLK_ESCAPE) { HandleButtonRelease(&KeyboardInput->Escape); } if(KeyCode == SDLK_b) { HandleButtonRelease(&KeyboardInput->B); } if(KeyCode == SDLK_f) { HandleButtonRelease(&KeyboardInput->F); } if(KeyCode == SDLK_a) { HandleButtonRelease(&KeyboardInput->A); } if(KeyCode == SDLK_UP) { HandleButtonRelease(&KeyboardInput->Up); } if(KeyCode == SDLK_DOWN) { HandleButtonRelease(&KeyboardInput->Down); } if(KeyCode == SDLK_LEFT) { HandleButtonRelease(&KeyboardInput->Left); } if(KeyCode == SDLK_RIGHT) { HandleButtonRelease(&KeyboardInput->Right); } break; case SDL_CONTROLLERBUTTONDOWN: if(GamePadButton == SDL_CONTROLLER_BUTTON_A) { HandleButtonPress(&GamePadInput->AButton); } if(GamePadButton == SDL_CONTROLLER_BUTTON_B) { HandleButtonPress(&GamePadInput->BButton); } if(GamePadButton == SDL_CONTROLLER_BUTTON_X) { HandleButtonPress(&GamePadInput->XButton); } if(GamePadButton == SDL_CONTROLLER_BUTTON_Y) { HandleButtonPress(&GamePadInput->YButton); } break; case SDL_CONTROLLERBUTTONUP: if(GamePadButton == SDL_CONTROLLER_BUTTON_A) { HandleButtonRelease(&GamePadInput->AButton); } if(GamePadButton == SDL_CONTROLLER_BUTTON_B) { HandleButtonRelease(&GamePadInput->BButton); GlobalRunning = false; } if(GamePadButton == SDL_CONTROLLER_BUTTON_X) { HandleButtonRelease(&GamePadInput->XButton); } if(GamePadButton == SDL_CONTROLLER_BUTTON_Y) { HandleButtonRelease(&GamePadInput->YButton); } break; } // NOTE(Redab): Get values from gamepad joysticks and normalize. real32 LSX = (real32)SDL_GameControllerGetAxis(GamePadHandle, SDL_CONTROLLER_AXIS_LEFTX); if(LSX < 0) { GamePadInput->LeftStick.X = LSX / 32768.0f; } else { GamePadInput->LeftStick.X = LSX / 32767.0f; } real32 LSY = (real32)SDL_GameControllerGetAxis(GamePadHandle, SDL_CONTROLLER_AXIS_LEFTY); if(LSY < 0) { GamePadInput->LeftStick.Y = LSY / 32768.0f; } else { GamePadInput->LeftStick.Y = LSY / 32767.0f; } real32 RSX = (real32)SDL_GameControllerGetAxis(GamePadHandle, SDL_CONTROLLER_AXIS_RIGHTX); if(RSX < 0) { GamePadInput->RightStick.X = RSX / 32768.0f; } else { GamePadInput->RightStick.X = RSX / 32767.0f; } real32 RSY = (real32)SDL_GameControllerGetAxis(GamePadHandle, SDL_CONTROLLER_AXIS_RIGHTY); if(RSY < 0) { GamePadInput->RightStick.Y = RSY / 32768.0f; } else { GamePadInput->RightStick.Y = RSY / 32767.0f; } }
//Our main loop which should continue running as long as we don't quite the game static void MainLoop() { char DLLFilePath[MAX_PATH]; char *onePastLastSlash; DWORD pathSize = GetModuleFileNameA(NULL, DLLFilePath, sizeof(DLLFilePath)); onePastLastSlash = DLLFilePath; for (char *scan = DLLFilePath; *scan; scan++) { if (*scan == '\\') { onePastLastSlash = scan + 1; } } char DLLFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game.dll", DLLFullPath, sizeof(DLLFullPath)); char tempDLLFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game_temp.dll", tempDLLFullPath, sizeof(tempDLLFullPath)); char PDBFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game.pdb", PDBFullPath, sizeof(PDBFullPath)); char tempPDBFullPath[MAX_PATH]; BuildFileFullPath(&state, "playground game_temp.pdb", tempPDBFullPath, sizeof(tempPDBFullPath)); Input = {}; Input.UP.Button = VK_UP; Input.DOWN.Button = VK_DOWN; Input.RIGHT.Button = VK_RIGHT; Input.LEFT.Button = VK_LEFT; LARGE_INTEGER performanceFrequency; QueryPerformanceFrequency(&performanceFrequency); TicksPerSecond = performanceFrequency.QuadPart; int monitorRefreshHZ = 60; HDC deviceContext = GetDC(Window.Window); int refreshHz = GetDeviceCaps(deviceContext, VREFRESH); ReleaseDC(Window.Window, deviceContext); if (refreshHz > 1) { monitorRefreshHZ = refreshHz; } float gameUpdateHZ = (float)(monitorRefreshHZ); float targetSecondsPerFrame = 1.0f / gameUpdateHZ; UINT desiredSchedulerTime = 1; bool sleepIsSmaller = true;//timeBeginPeriod(desiredSchedulerTime) == TIMERR_NOERROR; LARGE_INTEGER lastTick = GetTicks(); float updateTime = 0; int updates = 0; double frames = 0; double frameTime = 0; while (IsRunning) { /* start_loop = clock(); */ FILETIME newWriteTimeDLL = GetLastWriteTime(DLLFullPath); FILETIME newWriteTimePDB = GetLastWriteTime(PDBFullPath); if (CompareFileTime(&newWriteTimeDLL, &Game.LastWriteTimeDLL) != 0 && CompareFileTime(&newWriteTimeDLL, &Game.LastWriteTimePDB) != 0) { UnloadGameCode(&Game); CopyFile(PDBFullPath, tempPDBFullPath, FALSE); Game = LoadGameCode(DLLFullPath, tempDLLFullPath); Game.Game_Init(Dimensions); } LARGE_INTEGER gameTimerStart = GetTicks(); ProcessPendingMessages(&Keys); ProcessInput(&Input); //Update everything //Update the game Game.Game_Update(&Input); /*NOTE(kai): TEST ONLY*/ //Testing if A button is pressed if (IsKeyDown(&Keys, 'A')) { OutputDebugString("Key: a is pressed\n"); } //Testing if A button is released if (IsKeyUp(&Keys, 'A')) { OutputDebugString("Key: a is released\n"); } //Render everything //Clear the window ClearWindow(); //Render the game Game.Game_Render(); LARGE_INTEGER gameTimerEnd = GetTicks(); frameTime += (double)(1000.0f * GetSecondsElapsed(gameTimerStart, gameTimerEnd)); frames++; //frames += 1000.0f / (double)(1000.0f * GetSecondsElapsed(gameTimerStart, gameTimerEnd)); //PrintTimeElapsed(lastTick, gameTimerEnd); float secondsElapsedForFrame = GetSecondsElapsed(lastTick, GetTicks()); if (secondsElapsedForFrame < targetSecondsPerFrame) { if (sleepIsSmaller) { DWORD sleepTime = (DWORD)(1000.0f * (targetSecondsPerFrame - secondsElapsedForFrame)); if (sleepTime > 0) { Sleep(sleepTime); } } while (secondsElapsedForFrame < targetSecondsPerFrame) { secondsElapsedForFrame = GetSecondsElapsed(lastTick, GetTicks()); } updates++; } updateTime += GetSecondsElapsed(lastTick, GetTicks()); if (updateTime >= 1.0f) { double avgFPS = 1000.0f / ((frameTime) / frames); std::cout << "UPS: " << updates << ", average FPS: " << avgFPS << ", average work/frame: " << (frameTime) / frames << "\n"; frames = 0; frameTime = 0; updates = 0; updateTime = 0; } LARGE_INTEGER endTick = GetTicks(); //PrintTimeElapsed(lastTick, endTick); lastTick = endTick; //Render the window RenderWindow(Window.Window); /* //calc fps calcfps(); static int framecount = 0; framecount++; if (framecount == 10) { framecount = 0; std::cout << "frame per second is : " << (fps) << std::endl; } //QueryPerformanceCounter(&t_current_loop); end_loop = clock(); //float frameticks = (t_current_loop.QuadPart - t_previous_loop.QuadPart) / ((frequency_loop.QuadPart) / 1000.0); float frameticks = ((float)(end_loop - start_loop) / CLOCKS_PER_SEC) * 1000.0f; //print the current fps // std::cout << 1000/frameticks << std::endl; if (1000.0f / max_fps > frameticks){ Sleep(1000.0f / max_fps - frameticks); } */ } //Release resources (if there is any) and destory the window Release(); }
static int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { AllocConsole(); //create a console freopen( "conin$","r",stdin ); freopen( "conout$","w",stdout ); freopen( "conout$","w",stderr ); printf( "Program Started, console initialized\n" ); const char WindowName[] = "Wet Clay"; int64 mSecsPerFrame = 16; //60FPS System system = { }; system.windowHeight = 680; system.windowWidth = 1080; system.ReadWholeFile = &ReadWholeFile; system.GetMostRecentMatchingFile = &GetMostRecentMatchingFile; system.TrackFileUpdates = &TrackFileUpdates; system.DidFileUpdate = &DidFileUpdate; system.WriteFile = &WriteFile; system.HasFocus = &HasFocus; //Center position of window uint16 fullScreenWidth = GetSystemMetrics( SM_CXSCREEN ); uint16 fullScreenHeight = GetSystemMetrics( SM_CYSCREEN ); uint16 windowPosX = ( fullScreenWidth / 2 ) - (system.windowWidth / 2 ); uint16 windowPosY = ( fullScreenHeight / 2 ) - (system.windowHeight / 2 ); WNDCLASSEX wc = { }; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_OWNDC; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = WindowName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if( !RegisterClassEx( &wc ) ) { printf( "Failed to register class\n" ); return -1; } EnableCrashingOnCrashes(); // at this point WM_CREATE message is sent/received // the WM_CREATE branch inside WinProc function will execute here HWND hwnd = CreateWindowEx( 0, WindowName, "Wet Clay App", WS_BORDER, windowPosX, windowPosY, system.windowWidth, system.windowHeight, NULL, NULL, hInstance, NULL ); RECT windowRect = { }; GetClientRect( hwnd, &windowRect ); SetWindowLong( hwnd, GWL_STYLE, 0 ); ShowWindow ( hwnd, SW_SHOWNORMAL ); UpdateWindow( hwnd ); LARGE_INTEGER timerResolution; BOOL canSupportHiResTimer = QueryPerformanceFrequency( &timerResolution ); assert( canSupportHiResTimer ); size_t systemsMemorySize = MEGABYTES( 8 ); Stack gameSlab; gameSlab.size = SIZEOF_GLOBAL_HEAP + systemsMemorySize; gameSlab.start = VirtualAlloc( NULL, gameSlab.size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE ); gameSlab.current = gameSlab.start; assert( gameSlab.start != NULL ); Stack systemsMemory = AllocateNewStackFromStack( &gameSlab, systemsMemorySize ); InitWin32WorkerThread( &systemsMemory ); GLRendererGlobalState glRendererStorage = { }; globalGLRendererStorageRef = &glRendererStorage; GLRenderDriver glDriver = Win32InitGLRenderer( hwnd, &system, globalGLRendererStorageRef ); Win32Sound win32Sound = Win32InitSound( hwnd, 60, &systemsMemory ); void* imguistate = InitImGui_LimeStone( &systemsMemory, (RenderDriver*)&glDriver, system.windowWidth, system.windowHeight ); printf( "Remaining System Memory: %Id\n", SPACE_IN_STACK( (&gameSlab) ) ); LoadGameCode( &gameapp ); assert( gameapp.GameInit != NULL ); fileTracking = { }; fileTracking.writeHead = &fileTracking.stringBuffer[0]; int dllTrackingIndex = TrackFileUpdates( GAME_CODE_FILEPATH ); assert( dllTrackingIndex != -1 ); void* gameMemoryPtr = gameapp.GameInit( &gameSlab, (RenderDriver*)&glDriver, &win32Sound.driver, &system ); appIsRunning = true; InputState inputSnapshot1 = { }; InputState inputSnapshot2 = { }; inputSnapshot1.prevState = &inputSnapshot2; inputSnapshot2.prevState = &inputSnapshot1; InputState* currentSnapshotStorage = &inputSnapshot1; MSG Msg; while( appIsRunning ) { if( DidFileUpdate( dllTrackingIndex ) ) { LoadGameCode( &gameapp ); } static LARGE_INTEGER startTime; LARGE_INTEGER elapsedTime; LARGE_INTEGER lastTime = startTime; QueryPerformanceCounter( &startTime ); elapsedTime.QuadPart = startTime.QuadPart - lastTime.QuadPart; elapsedTime.QuadPart *= 1000; elapsedTime.QuadPart /= timerResolution.QuadPart; //GAME LOOP if( gameapp.UpdateAndRender != NULL && gameapp.MixSound != NULL ) { currentSnapshotStorage = currentSnapshotStorage->prevState; QueryInput( system.windowWidth, system.windowHeight, windowPosX, windowPosY, currentSnapshotStorage ); keypressHistoryIndex = 0; UpdateImgui( currentSnapshotStorage, imguistate, system.windowWidth, system.windowHeight ); appIsRunning = gameapp.UpdateAndRender( gameMemoryPtr, (float)elapsedTime.QuadPart, currentSnapshotStorage, &win32Sound.driver, (RenderDriver*)&glDriver, &system, imguistate ); PushAudioToSoundCard( &gameapp, &win32Sound ); ImGui::Render(); BOOL swapBufferSuccess = SwapBuffers( deviceContext ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); } else { printf( "Game code was not loaded...\n" ); } //Windows Message pump is here, so if there is a system level Close or //Destory command, the UpdateAndRender method does not reset the appIsRunning //flag to true while( PeekMessage( &Msg, NULL, 0, 0, PM_REMOVE ) ) { TranslateMessage( &Msg ); DispatchMessage( &Msg ); } //End loop timer query LARGE_INTEGER endTime, computeTime; { QueryPerformanceCounter( &endTime ); computeTime.QuadPart = endTime.QuadPart - startTime.QuadPart; computeTime.QuadPart *= 1000; computeTime.QuadPart /= timerResolution.QuadPart; } if( computeTime.QuadPart <= mSecsPerFrame ) { Sleep(mSecsPerFrame - computeTime.QuadPart ); } else { printf("Didn't sleep, compute was %ld\n", computeTime.QuadPart ); } }; FreeConsole(); return Msg.wParam; }