/**
* Copyright (c) 2023 Jonathan Metzgar
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "object.h"
#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
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;
}
Comments