/** * Copyright (c) 2023 Jonathan Metzgar * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include #include #include #include #include #include #include "object.h" #define SDL_MAIN_HANDLED #include #include void LogSDLError() { fprintf(stderr, "Error: %s\n", SDL_GetError()); } /////// Game Engine ////// typedef struct GameEngine { Object_t self; SDL_Window *window; int frameCount; double previousTime; double currentTime; double deltaTime; int width; int height; bool finished; } GameEngine; typedef GameEngine *GameEnginePtr; GameEnginePtr CreateGameEngine() { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { LogSDLError(); return NULL; } const int windowWidth = 1280; const int windowHeight = 720; // Tell SDL2 to make a window with an OpenGL context. SDL_WindowFlags windowFlags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window *window = SDL_CreateWindow("Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowWidth, windowHeight, windowFlags); if (window == NULL) { LogSDLError(); return NULL; } // Tell SDL2 what to have in the OpenGL context. SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); // SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GLContext glContext = SDL_GL_CreateContext(window); if (glContext == NULL) { LogSDLError(); return NULL; } // Enable vsync on the display. SDL_GL_SetSwapInterval(1); SDL_GL_MakeCurrent(window, glContext); GameEnginePtr engine = (GameEnginePtr)allocObject(sizeof(GameEngine)); engine->window = window; engine->width = windowWidth; engine->height = windowHeight; engine->currentTime = SDL_GetTicks64() / 1000.0; return engine; } void EngineStartFrame(GameEnginePtr engine) { engine->previousTime = engine->currentTime; engine->currentTime = SDL_GetTicks64() / 1000.0; engine->deltaTime = engine->currentTime - engine->previousTime; engine->frameCount++; } void EnginePollEvents(GameEnginePtr engine) { // Process all events for the current frame. SDL_Event e; while (SDL_PollEvent(&e)) { if (e.type == SDL_KEYDOWN) { if (e.key.keysym.sym == SDLK_ESCAPE) engine->finished = true; } else if (e.type == SDL_WINDOWEVENT) { if (e.window.event == SDL_WINDOWEVENT_RESIZED) { engine->width = e.window.data1; engine->height = e.window.data2; } } else if (e.type == SDL_QUIT) { engine->finished = true; } else { // This is an unhandled event. } } } void EngineTick(GameEnginePtr engine) { } void EngineDraw(GameEnginePtr engine) { // Set up OpenGL drawing commands. glViewport(0, 0, engine->width, engine->height); double s = sin(engine->currentTime) * 0.5 + 0.5; glClearColor(0.1, s, 0.3, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBegin(GL_TRIANGLES); glVertex2f(-0.5f, -0.5f); glVertex2f(0.5f, -0.5f); glVertex2f(0.0f, 0.5f); glEnd(); } void EngineEndFrame(GameEnginePtr engine) { SDL_GL_SwapWindow(engine->window); } int main(int argc, char **argv) { GameEnginePtr engine = CreateGameEngine(); if (engine == NULL) { return -1; } // Start the game loop. while (!engine->finished) { EngineStartFrame(engine); EnginePollEvents(engine); // Leave the loop if we're done. if (engine->finished) break; EngineTick(engine); EngineDraw(engine); EngineEndFrame(engine); } releaseObject((ObjectPtr)engine); return 0; }