270 lines
6.9 KiB
C++
270 lines
6.9 KiB
C++
/**
|
|
* 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 <AL/al.h>
|
|
#include <AL/alc.h>
|
|
#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<double> 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<uint8_t> 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<int16_t> (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;
|
|
} |