/** * Copyright (c) 2022 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "DawnGLFWHost.hpp" #include "game/DawnGame.hpp" #include "dawnopengl.hpp" #include "display/BackBufferRenderTarget.hpp" #include #include #include "AudioFile.h" using namespace Dawn; // Static declaration of the host, needed due to GLFW events being C-like DawnHost *DAWN_HOST = nullptr; // Host DawnHost::DawnHost() { this->data = new DawnHostData(); this->data->window = nullptr; } int32_t DawnHost::init(DawnGame *game) { AudioFile audioFile; audioFile.load ("C:\\sample.wav"); audioFile.printSummary(); ALCdevice* device = alcOpenDevice(nullptr); if(!device) assertUnreachable(); ALCcontext *context; context = alcCreateContext(device, NULL); if(!alcMakeContextCurrent(context)) assertUnreachable(); ALfloat listenerOri[] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f }; alListener3f(AL_POSITION, 0, 0, 1.0f); alListener3f(AL_VELOCITY, 0, 0, 0); alListenerfv(AL_ORIENTATION, listenerOri); ALuint source; alGenSources((ALuint)1, &source); alSourcef(source, AL_PITCH, 1); alSourcef(source, AL_GAIN, 1); alSource3f(source, AL_POSITION, 0, 0, 0); alSource3f(source, AL_VELOCITY, 0, 0, 0); alSourcei(source, AL_LOOPING, AL_FALSE); ALuint buffer; alGenBuffers((ALuint)1, &buffer); ALenum format; switch(audioFile.getBitDepth()) { case 16: switch(audioFile.getNumChannels()) { case 2: format = AL_FORMAT_STEREO16; break; case 1: format = AL_FORMAT_MONO16; break; default: assertUnreachable(); } break; case 8: switch(audioFile.getNumChannels()) { case 2: format = AL_FORMAT_STEREO8; break; case 1: format = AL_FORMAT_MONO8; break; default: assertUnreachable(); } break; default: assertUnreachable(); } std::vector data; for (int i = 0; i < audioFile.getNumSamplesPerChannel(); i++) { for(int y = 0; y < audioFile.getNumChannels(); y++) { double sample = audioFile.samples[y][i]; sample = mathClamp(sample, -1., 1.); auto q = static_cast (sample * 32767.); uint8_t bytes[2]; bytes[0] = (q >> 8) & 0xFF; bytes[1] = q & 0xFF; data.push_back(bytes[1]); data.push_back(bytes[0]); } } alBufferData(buffer, format, &data[0], data.size(), audioFile.getSampleRate()); ALint source_state; alSourcei(source, AL_BUFFER, buffer); alSourcePlay(source); alGetSourcei(source, AL_SOURCE_STATE, &source_state); while (source_state == AL_PLAYING) { alGetSourcei(source, AL_SOURCE_STATE, &source_state); } return DAWN_HOST_INIT_RESULT_SUCCESS; // Update values this->game = game; DAWN_HOST = this; // Init GLFW if(!glfwInit()) return DAWN_GLFW_INIT_RESULT_INIT_FAILED; glfwSetErrorCallback(&glfwOnError); // Setup window hints glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, false); // Create Window this->data->window = glfwCreateWindow( DAWN_GLFW_WINDOW_WIDTH_DEFAULT, DAWN_GLFW_WINDOW_HEIGHT_DEFAULT, "Dawn", NULL, NULL ); if(this->data->window == nullptr) { glfwTerminate(); return DAWN_GLFW_INIT_RESULT_WINDOW_CREATE_FAILED; } // Load GLAD glfwMakeContextCurrent(this->data->window); glfwSwapInterval(0); gladLoadGLLoader((GLADloadproc)glfwGetProcAddress); // Override the defaults game->renderManager.backBuffer.setSize( DAWN_GLFW_WINDOW_WIDTH_DEFAULT, DAWN_GLFW_WINDOW_HEIGHT_DEFAULT ); // Default keybinds game->inputManager.bind(INPUT_BIND_ACCEPT, GLFW_KEY_ENTER); game->inputManager.bind(INPUT_BIND_ACCEPT, GLFW_KEY_E); game->inputManager.bind(INPUT_BIND_ACCEPT, GLFW_KEY_SPACE); // game->inputManager.bind(INPUT_BIND_NEGATIVE_X, GLFW_KEY_A); // game->inputManager.bind(INPUT_BIND_POSITIVE_X, GLFW_KEY_D); // game->inputManager.bind(INPUT_BIND_NEGATIVE_Y, GLFW_KEY_S); // game->inputManager.bind(INPUT_BIND_POSITIVE_Y, GLFW_KEY_W); // Initialize the game auto result = game->init(); if(result != DAWN_GAME_INIT_RESULT_SUCCESS) return result; // Hard-Load the first scene assets. if(game->scene != nullptr) { auto assets = game->scene->getRequiredAssets(); game->assetManager.queueLoad(assets); game->assetManager.syncLoad(); game->scene->stage(); } // Set up event listeners glfwSetFramebufferSizeCallback(this->data->window, &glfwOnResize); glfwSetKeyCallback(this->data->window, &glfwOnKey); return DAWN_HOST_INIT_RESULT_SUCCESS; } int32_t DawnHost::start(DawnGame *game) { double_t time, newTime; float_t fDelta; int32_t updateResult; // Main Render Loop time = 0.0f; while(!glfwWindowShouldClose(this->data->window)) { // Determine the delta. newTime = glfwGetTime(); fDelta = (float_t)(newTime - time); time = newTime; // Perform update updateResult = this->update(game, fDelta); // Did the update complete successfully? if(updateResult == DAWN_HOST_UPDATE_RESULT_EXIT) { break; } else if(updateResult != DAWN_HOST_UPDATE_RESULT_SUCCESS) { return DAWN_GLFW_START_RESULT_UPDATE_FAILED; } // Tick the engine. glfwSwapBuffers(this->data->window); // Update events glfwPollEvents(); } return DAWN_HOST_START_RESULT_EXIT_SUCCESS; } int32_t DawnHost::update(DawnGame *game, float_t delta) { // Tick game. auto ret = game->update(delta); switch(ret) { case DAWN_GAME_UPDATE_RESULT_SUCCESS: return DAWN_HOST_UPDATE_RESULT_SUCCESS; case DAWN_GAME_UPDATE_RESULT_EXIT: return DAWN_HOST_UPDATE_RESULT_EXIT; default: return ret; } } void DawnHost::unload(DawnGame *game) { glfwDestroyWindow(this->data->window); this->data->window = nullptr; glfwTerminate(); } DawnHost::~DawnHost() { delete this->data; DAWN_HOST = nullptr; } // GLFW Callbacks void glfwOnError(int error, const char* description) { fputs(description, stderr); } void glfwOnResize(GLFWwindow *window, int32_t w, int32_t h) { if(DAWN_HOST == nullptr) return; // TODO: I may throttle this, it calls for every frame the window's resized. DAWN_HOST->game->renderManager.backBuffer.setSize((float_t)w, (float_t)h); } void glfwOnKey( GLFWwindow *window, int32_t key, int32_t scancode, int32_t action, int32_t mods ) { if(DAWN_HOST == nullptr) return; // Determine Value float_t value; if(action == GLFW_PRESS) { value = 1.0f; } else if(action == GLFW_RELEASE) { value = 0.0f; } else { return; } // Determine the input axis DAWN_HOST->game->inputManager.rawInputValues[key] = value; }