Dengine/src/dengine.c
Doyle Thai 332888f3b5 Switch mem allocation to custom memory block
No longer repeatedly invoke malloc for retrieving memory. Some operations still
leak memory (notably audio streaming). We introduce notion of Transient and
Permanent storage. Transient memory is used for data in memory that is
temporarily allocated (i.e. graphics for that frame's output), and permanent as
more like game state storage.

We now give out pointers from a pre-allocated block of memory, so we have no
allocation cost and benefits of reduced memory leaks. The transient block gets
reset every frame. Any memory allocated within is cleared at no cost simply by
rearranging the base pointer of the memory block.
2016-09-24 14:43:59 +10:00

257 lines
6.6 KiB
C

#include "Dengine/AssetManager.h"
#include "Dengine/Common.h"
#include "Dengine/Debug.h"
#include "Dengine/Math.h"
#include "Dengine/OpenGL.h"
#include "Dengine/WorldTraveller.h"
INTERNAL inline void processKey(KeyState *state, int action)
{
if (action == GLFW_PRESS || action == GLFW_RELEASE)
{
state->newHalfTransitionCount++;
}
}
INTERNAL void keyCallback(GLFWwindow *window, int key, int scancode, int action,
int mode)
{
GameState *game = CAST(GameState *)(glfwGetWindowUserPointer(window));
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
{
glfwSetWindowShouldClose(window, GL_TRUE);
}
switch (key)
{
case GLFW_KEY_UP:
processKey(&game->input.keys[keycode_up], action);
break;
case GLFW_KEY_DOWN:
processKey(&game->input.keys[keycode_down], action);
break;
case GLFW_KEY_LEFT:
processKey(&game->input.keys[keycode_left], action);
break;
case GLFW_KEY_RIGHT:
processKey(&game->input.keys[keycode_right], action);
break;
case GLFW_KEY_LEFT_SHIFT:
processKey(&game->input.keys[keycode_leftShift], action);
break;
case GLFW_KEY_ENTER:
processKey(&game->input.keys[keycode_enter], action);
break;
case GLFW_KEY_BACKSPACE:
processKey(&game->input.keys[keycode_backspace], action);
break;
case GLFW_KEY_TAB:
processKey(&game->input.keys[keycode_tab], action);
break;
default:
if (key >= ' ' && key <= '~')
{
i32 offset = 0;
if (key >= 'A' && key <= 'Z')
{
if (!game->input.keys[keycode_leftShift].endedDown)
offset = 'a' - 'A';
}
i32 glfwCodeToPlatformCode = (key - ' ') + offset;
processKey(&game->input.keys[glfwCodeToPlatformCode], action);
}
break;
}
}
INTERNAL void mouseCallback(GLFWwindow *window, double xPos, double yPos)
{
GameState *game = CAST(GameState *)(glfwGetWindowUserPointer(window));
// NOTE(doyle): x(0), y(0) of mouse starts from the top left of window
v2 windowSize = game->renderer.size;
f32 flipYPos = windowSize.h - CAST(f32) yPos;
game->input.mouseP = V2(CAST(f32) xPos, flipYPos);
}
INTERNAL void mouseButtonCallback(GLFWwindow *window, int button, int action,
int mods)
{
GameState *game = CAST(GameState *)(glfwGetWindowUserPointer(window));
switch(button)
{
case GLFW_MOUSE_BUTTON_LEFT:
processKey(&game->input.keys[keycode_mouseLeft], action);
break;
default:
break;
}
}
INTERNAL void scrollCallback(GLFWwindow *window, double xOffset, double yOffset)
{
}
i32 main(void)
{
/*
**************************
* INIT APPLICATION WINDOW
**************************
*/
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
i32 windowWidth = 1600;
i32 windowHeight = 900;
GLFWwindow *window =
glfwCreateWindow(windowWidth, windowHeight, "Dengine", NULL, NULL);
if (!window)
{
printf("glfwCreateWindow() failed: Failed to create window\n");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
/*
**************************
* INITIALISE OPENGL STATE
**************************
*/
/* Make GLEW use more modern technies for OGL on core profile*/
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK)
{
printf("glewInit() failed: Failed to initialise GLEW\n");
return -1;
}
// NOTE(doyle): glewInit() bug that sets the gl error flag after init
// regardless of success. Catch it once by calling glGetError
glGetError();
i32 frameBufferWidth, frameBufferHeight;
glfwGetFramebufferSize(window, &frameBufferWidth, &frameBufferHeight);
glViewport(0, 0, frameBufferWidth, frameBufferHeight);
glfwSetKeyCallback(window, keyCallback);
glfwSetCursorPosCallback(window, mouseCallback);
glfwSetMouseButtonCallback(window, mouseButtonCallback);
glfwSetScrollCallback(window, scrollCallback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glEnable(GL_BLEND);
glEnable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glCullFace(GL_BACK);
/*
*******************
* INITIALISE GAME
*******************
*/
Memory memory = {0};
size_t persistentSize = MEGABYTES(128);
size_t transientSize = MEGABYTES(128);
memory.persistentSize = persistentSize;
memory.persistent = PLATFORM_MEM_ALLOC_(NULL, persistentSize, u8);
memory.transientSize = transientSize;
memory.transient = PLATFORM_MEM_ALLOC_(NULL, transientSize, u8);
GameState worldTraveller = {0};
worldTraveller_gameInit(&worldTraveller,
V2i(frameBufferWidth, frameBufferHeight), &memory);
#ifdef DENGINE_DEBUG
debug_init(&worldTraveller.arena_, V2i(windowWidth, windowHeight),
worldTraveller.assetManager.font);
#endif
glfwSetWindowUserPointer(window, CAST(void *)(&worldTraveller));
/*
*******************
* GAME LOOP
*******************
*/
f32 startTime = CAST(f32)(glfwGetTime());
f32 secondsElapsed = 0.0f; // Time between current frame and last frame
#if 0
// TODO(doyle): Get actual monitor refresh rate
i32 monitorRefreshHz = 60;
f32 targetSecondsPerFrame = 1.0f / CAST(f32)(monitorRefreshHz);
#else
// TODO(doyle): http://gafferongames.com/game-physics/fix-your-timestep/
// NOTE(doyle): Prevent glfwSwapBuffer until a vertical retrace has
// occurred, i.e. limit framerate to monitor refresh rate
glfwSwapInterval(1);
#endif
while (!glfwWindowShouldClose(window))
{
/* Check and call events */
glfwPollEvents();
/* Rendering commands here*/
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
worldTraveller_gameUpdateAndRender(&worldTraveller, secondsElapsed);
GL_CHECK_ERROR();
/* Swap the buffers */
glfwSwapBuffers(window);
f32 endTime = CAST(f32)glfwGetTime();
secondsElapsed = endTime - startTime;
#if 0
// TODO(doyle): Busy waiting, should sleep
while (secondsElapsed < targetSecondsPerFrame)
{
endTime = CAST(f32)(glfwGetTime());
secondsElapsed = endTime - startTime;
}
#endif
LOCAL_PERSIST f32 titleUpdateFrequencyInSeconds = 0.5f;
titleUpdateFrequencyInSeconds -= secondsElapsed;
if (titleUpdateFrequencyInSeconds <= 0)
{
f32 msPerFrame = secondsElapsed * 1000.0f;
f32 framesPerSecond = 1.0f / secondsElapsed;
i32 entityCount =
worldTraveller.world[worldTraveller.currWorldIndex]
.freeEntityIndex;
char textBuffer[256];
snprintf(textBuffer, ARRAY_COUNT(textBuffer),
"Dengine | %f ms/f | %f fps | Entity Count: %d",
msPerFrame, framesPerSecond, entityCount);
glfwSetWindowTitle(window, textBuffer);
titleUpdateFrequencyInSeconds = 0.5f;
}
startTime = endTime;
}
glfwTerminate();
return 0;
}