diff --git a/src/dawn/scene/Scene.cpp b/src/dawn/scene/Scene.cpp index 46bf1fa6..86908e22 100644 --- a/src/dawn/scene/Scene.cpp +++ b/src/dawn/scene/Scene.cpp @@ -36,8 +36,8 @@ void Scene::update() { // TODO: Cleanup old scene items // TODO: Tick scene items(?) - this->eventSceneUpdate.invoke(); - if(!this->game->timeManager.isPaused) this->eventSceneUnpausedUpdate.invoke(); + this->eventSceneUpdate.invoke(game->timeManager.delta); + if(!this->game->timeManager.isPaused) this->eventSceneUnpausedUpdate.invoke(game->timeManager.delta); } SceneItem * Scene::createSceneItem() { diff --git a/src/dawn/scene/Scene.hpp b/src/dawn/scene/Scene.hpp index 155d9d6a..3d8913aa 100644 --- a/src/dawn/scene/Scene.hpp +++ b/src/dawn/scene/Scene.hpp @@ -21,7 +21,7 @@ namespace Dawn { template T * _sceneForwardGetComponent(SceneItem *item); - class Scene : public StateOwner { + class Scene { private: sceneitemid_t nextId; std::map items; @@ -30,8 +30,8 @@ namespace Dawn { public: DawnGame *game; ScenePhysicsManager *physics; - Event<> eventSceneUpdate; - Event<> eventSceneUnpausedUpdate; + StateEvent eventSceneUpdate; + StateEvent eventSceneUnpausedUpdate; /** * Construct a new Scene instance. diff --git a/src/dawn/scene/components/display/AnimationController.cpp b/src/dawn/scene/components/display/AnimationController.cpp index f2ede5c5..3bf2a860 100644 --- a/src/dawn/scene/components/display/AnimationController.cpp +++ b/src/dawn/scene/components/display/AnimationController.cpp @@ -22,10 +22,10 @@ void AnimationController::addAnimation(Animation *animation) { void AnimationController::onStart() { SceneItemComponent::onStart(); - useEventLegacy([&]{ + useEvent([&](float_t delta){ auto it = this->animations.begin(); while(it != this->animations.end()) { - (*it)->tick(this->getGame()->timeManager.delta); + (*it)->tick(delta); ++it; } }, getScene()->eventSceneUnpausedUpdate); diff --git a/src/dawn/scene/components/example/ExampleSpin.cpp b/src/dawn/scene/components/example/ExampleSpin.cpp index 6beba387..3c0ad2f3 100644 --- a/src/dawn/scene/components/example/ExampleSpin.cpp +++ b/src/dawn/scene/components/example/ExampleSpin.cpp @@ -28,11 +28,11 @@ ExampleSpin::ExampleSpin(SceneItem *item) : } void ExampleSpin::onStart() { - useEventLegacy([&]{ + useEvent([&](float_t delta){ auto quat = this->transform->getLocalRotation(); - quat = glm::rotate(quat, getGame()->timeManager.delta, glm::vec3(0, 1, 0)); - quat = glm::rotate(quat, getGame()->timeManager.delta / 2.0f, glm::vec3(1, 0, 0)); - quat = glm::rotate(quat, getGame()->timeManager.delta / 4.0f, glm::vec3(0, 0, 1)); + quat = glm::rotate(quat, delta, glm::vec3(0, 1, 0)); + quat = glm::rotate(quat, delta / 2.0f, glm::vec3(1, 0, 0)); + quat = glm::rotate(quat, delta / 4.0f, glm::vec3(0, 0, 1)); this->transform->setLocalRotation(quat); }, getScene()->eventSceneUnpausedUpdate); } \ No newline at end of file diff --git a/src/dawn/scene/components/scene/SubSceneController.cpp b/src/dawn/scene/components/scene/SubSceneController.cpp index 70c3f6b4..0dec6e68 100644 --- a/src/dawn/scene/components/scene/SubSceneController.cpp +++ b/src/dawn/scene/components/scene/SubSceneController.cpp @@ -14,13 +14,13 @@ SubSceneController::SubSceneController(SceneItem *i) : SceneItemComponent(i) { void SubSceneController::onStart() { auto myScene = this->getScene(); - useEventLegacy([&]{ + useEvent([&](float_t d){ if(!this->onlyUpdateUnpaused) return; if(this->subScene == nullptr) return; this->subScene->update(); }, myScene->eventSceneUnpausedUpdate); - useEventLegacy([&]{ + useEvent([&](float_t d){ if(this->onlyUpdateUnpaused) return; if(this->subScene == nullptr) return; this->subScene->update(); diff --git a/src/dawn/scene/components/ui/UICanvas.cpp b/src/dawn/scene/components/ui/UICanvas.cpp index 0e831b30..fe591f36 100644 --- a/src/dawn/scene/components/ui/UICanvas.cpp +++ b/src/dawn/scene/components/ui/UICanvas.cpp @@ -67,7 +67,7 @@ void UICanvas::onStart() { }, this->currentMenu); // Scene Update - useEventLegacy([&]{ + useEvent([&](float_t delta){ if(this->currentMenu == nullptr) return; this->currentMenu->onTick(); }, getScene()->eventSceneUpdate); diff --git a/src/dawnopenal/scene/components/audio/AudioSource.cpp b/src/dawnopenal/scene/components/audio/AudioSource.cpp index 38bfec43..1915ca01 100644 --- a/src/dawnopenal/scene/components/audio/AudioSource.cpp +++ b/src/dawnopenal/scene/components/audio/AudioSource.cpp @@ -1,268 +1,257 @@ -// Copyright (c) 2023 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#include "AudioSource.hpp" -#include "game/DawnGame.hpp" - -using namespace Dawn; - -AudioSource::AudioSource(SceneItem *i) : SceneItemComponent(i) { - -} - - -void AudioSource::fillBuffer(ALuint buffer) { - // Read in some data - uint8_t *temporaryBuffer = (uint8_t*)memoryAllocate(AUDIO_SOURCE_BUFFER_SIZE); - /* - OK A big writeup here because things get complicated and I am sure that I - will need to revist this. - - First thing to note is that OpenAL requires that the data buffered is a - multiple of the frame size. Frame Size from what I can tell is; - dataSize * channelCount - e.g. sizeof(int16_t) * stereo = 4 - - EDIT 23017 - Something about how OpenAL handles things means the following - statement is probably false, I'm leaving it here but DON'T READ IT PLEASE - - Additionally, we need to handle cases where the buffer "wraps around". So - let's say we are at 8179/8192 bytes, we go to read "128 bytes", we need to - first read the remaining 13 bytes and then the remaining (128-13)bytes. - - After that we need to do our frame size division, and make sure we only - advance our buffer by that many bytes, so that the next buffer can pick up - exactly where this buffer left off. - */ - - // Try to read the entire buffer - this->data->loader.setPosition(this->data->bufferStart + bufferPosition); - size_t readLength = this->data->loader.read( - temporaryBuffer, AUDIO_SOURCE_BUFFER_SIZE - ); - - size_t bufferLength = (readLength / this->data->frameSize) * this->data->frameSize; - - // Did we run out of data? - size_t newPosition; - if(readLength < AUDIO_SOURCE_BUFFER_SIZE) { - newPosition = 0; - } else { - newPosition = bufferPosition + bufferLength; - } - - // Determine format, may do this somewhere else for efficiency sake. - ALenum format; - switch(this->data->channelCount) { - case 2: - format = AL_FORMAT_STEREO16; - break; - case 1: - format = AL_FORMAT_MONO16; - break; - default: - assertUnreachable(); - } - - // Buffer data - alBufferData( - buffer, format, temporaryBuffer, bufferLength, (ALsizei)this->data->sampleRate - ); - - // Cleanup - memoryFree(temporaryBuffer); - bufferPosition = newPosition; -} - -void AudioSource::detatchBuffers() { - ALint buffersQueued; - ALuint buffersUnqueued[AUDIO_SOURCE_BUFFER_COUNT]; - alGetSourcei(this->source, AL_BUFFERS_QUEUED, &buffersQueued); - if(buffersQueued > 0) { - alSourceUnqueueBuffers(this->source, buffersQueued, buffersUnqueued); - } -} - -void AudioSource::attachBuffers() { - alSourceQueueBuffers(this->source, AUDIO_SOURCE_BUFFER_COUNT, this->buffers); -} - -void AudioSource::onStart() { - // Create source and buffers - alGenSources((ALuint)1, &this->source); - alGenBuffers((ALuint)AUDIO_SOURCE_BUFFER_COUNT, this->buffers); - - // Set up the source positions - glm::vec3 position = this->transform->getWorldPosition(); - alSource3f(this->source, AL_POSITION, position.x, position.y, position.z); - alSource3f(this->source, AL_VELOCITY, 0, 0, 0); - - // In future these will probably be tied to the layer - alSourcef(this->source, AL_PITCH, 1); - alSourcef(this->source, AL_GAIN, 1); - - // Mark as ready. - this->ready = true; - - // Listen for events - this->transform->eventTransformUpdated.addListener(this, &AudioSource::onTransformUpdate); - this->getScene()->eventSceneUpdate.addListener(this, &AudioSource::onSceneUpdate); -} - -AudioSourceState AudioSource::getRealState() { - return this->internalState; -} - -void AudioSource::rewind() { - this->bufferPosition = 0; -} - -AudioAsset * AudioSource::getAudioData() { - return this->data; -} - -void AudioSource::setAudioData(AudioAsset *data) { - this->data = data; - this->rewind(); -} - -enum AudioSourcePlayMode AudioSource::getPlayMode() { - return this->playMode; -} - -void AudioSource::setPlayMode(enum AudioSourcePlayMode playMode) { - this->playMode = playMode; -} - -void AudioSource::stop() { - this->state = AUDIO_SOURCE_STATE_STOPPED; -} - -void AudioSource::play() { - this->state = AUDIO_SOURCE_STATE_PLAYING; -} - -void AudioSource::pause() { - this->state = AUDIO_SOURCE_STATE_PAUSED; -} - -void AudioSource::onDispose() { - assertTrue(this->ready); - this->ready = false; - - this->getScene()->eventSceneUpdate.removeListener(this, &AudioSource::onSceneUpdate); - this->transform->eventTransformUpdated.removeListener(this, &AudioSource::onTransformUpdate); - - alDeleteSources((ALuint)1, &this->source); - alDeleteBuffers((ALuint)AUDIO_SOURCE_BUFFER_COUNT, this->buffers); -} - -void AudioSource::onSceneUpdate() { - ALuint bufferId; - ALint buffersProcessed; - - assertTrue(this->ready); - - // What is the user trying to do? - if(this->state == AUDIO_SOURCE_STATE_PLAYING) { - assertTrue(this->data != nullptr); - assertTrue(this->data->loaded); - - // Handle the special game-paused music-paused state. - if(this->playMode == AUDIO_PLAY_MODE_UNPAUSED && this->getGame()->timeManager.isPaused) { - if(this->internalState == AUDIO_SOURCE_STATE_PLAYING) { - // Functionally, this is the same as pausing - alSourcePause(this->source); - this->internalState = AUDIO_SOURCE_STATE_PAUSED; - } - return;// Do nothing else, at all. - } - - // They are trying to play. We need to check if we are already playing - if(this->internalState == AUDIO_SOURCE_STATE_PLAYING) { - // We are good to continue buffering audio data. - buffersProcessed = 0; - alGetSourcei(this->source, AL_BUFFERS_PROCESSED, &buffersProcessed); - - while(buffersProcessed > 0) { - alSourceUnqueueBuffers(this->source, 1, &bufferId); - this->fillBuffer(bufferId); - alSourceQueueBuffers(this->source, 1, &bufferId); - buffersProcessed--; - - // Now, before we go to the next buffer let's see if we need to - // update the internal state - if(this->bufferPosition != 0) continue; - - // We reached the end of the buffer whilst filling. What to do now? - if(this->loop) { - // We're looping so it's fine - this->eventLooped.invoke(); - continue; - } - - // We are NOT looping, so we need to finish here. - this->state = AUDIO_SOURCE_STATE_STOPPED; - this->eventFinished.invoke(); - this->bufferPosition = 0; - buffersProcessed = 0; - } - } else if(this->internalState == AUDIO_SOURCE_STATE_STOPPED) { - // Not yet playing. First thing we need to do is fill the buffers. - for(int32_t i = 0; i < AUDIO_SOURCE_BUFFER_COUNT; i++) { - this->fillBuffer(this->buffers[i]); - } - - // Now attach the buffers - this->attachBuffers(); - - // And begin playing - alSourcePlay(this->source); - this->internalState = AUDIO_SOURCE_STATE_PLAYING; - this->eventPlaying.invoke(); - - } else if(AUDIO_SOURCE_STATE_PAUSED) { - // Request to resume - alSourcePlay(this->source); - this->eventResumed.invoke(); - this->internalState = AUDIO_SOURCE_STATE_PLAYING; - - } else { - assertUnreachable(); - } - } else if(this->state == AUDIO_SOURCE_STATE_PAUSED) { - assertTrue(this->data != nullptr); - assertTrue(this->data->loaded); - - // Pause has been requested. - if(this->internalState != AUDIO_SOURCE_STATE_PLAYING) return; - - // We are playing something, pause it - alSourcePause(this->source); - this->internalState = AUDIO_SOURCE_STATE_PAUSED; - this->eventPaused.invoke(); - - } else if(this->state == AUDIO_SOURCE_STATE_STOPPED) { - if(this->internalState == AUDIO_SOURCE_STATE_STOPPED) return; - - assertTrue(this->data != nullptr); - assertTrue(this->data->loaded); - - // Release the buffers - alSourceStop(this->source); - this->detatchBuffers(); - this->internalState = AUDIO_SOURCE_STATE_STOPPED; - this->eventStopped.invoke(); - this->bufferPosition = 0; - - } else { - assertUnreachable(); - } -} - -void AudioSource::onTransformUpdate() { - glm::vec3 position = this->transform->getWorldPosition(); - alSource3f(this->source, AL_POSITION, position.x, position.y, position.z); +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "AudioSource.hpp" +#include "game/DawnGame.hpp" + +using namespace Dawn; + +AudioSource::AudioSource(SceneItem *i) : + SceneItemComponent(i), + playMode(AUDIO_PLAY_MODE_UNPAUSED) +{ + +} + + +void AudioSource::fillBuffer(ALuint buffer) { + // Read in some data + uint8_t *temporaryBuffer = (uint8_t*)memoryAllocate(AUDIO_SOURCE_BUFFER_SIZE); + /* + OK A big writeup here because things get complicated and I am sure that I + will need to revist this. + + First thing to note is that OpenAL requires that the data buffered is a + multiple of the frame size. Frame Size from what I can tell is; + dataSize * channelCount + e.g. sizeof(int16_t) * stereo = 4 + + EDIT 23017 - Something about how OpenAL handles things means the following + statement is probably false, I'm leaving it here but DON'T READ IT PLEASE + + Additionally, we need to handle cases where the buffer "wraps around". So + let's say we are at 8179/8192 bytes, we go to read "128 bytes", we need to + first read the remaining 13 bytes and then the remaining (128-13)bytes. + + After that we need to do our frame size division, and make sure we only + advance our buffer by that many bytes, so that the next buffer can pick up + exactly where this buffer left off. + */ + + // Try to read the entire buffer + this->data->loader.setPosition(this->data->bufferStart + bufferPosition); + size_t readLength = this->data->loader.read( + temporaryBuffer, AUDIO_SOURCE_BUFFER_SIZE + ); + + size_t bufferLength = (readLength / this->data->frameSize) * this->data->frameSize; + + // Did we run out of data? + size_t newPosition; + if(readLength < AUDIO_SOURCE_BUFFER_SIZE) { + newPosition = 0; + } else { + newPosition = bufferPosition + bufferLength; + } + + // Determine format, may do this somewhere else for efficiency sake. + ALenum format; + switch(this->data->channelCount) { + case 2: + format = AL_FORMAT_STEREO16; + break; + case 1: + format = AL_FORMAT_MONO16; + break; + default: + assertUnreachable(); + } + + // Buffer data + alBufferData( + buffer, format, temporaryBuffer, bufferLength, (ALsizei)this->data->sampleRate + ); + + // Cleanup + memoryFree(temporaryBuffer); + bufferPosition = newPosition; +} + +void AudioSource::detatchBuffers() { + ALint buffersQueued; + ALuint buffersUnqueued[AUDIO_SOURCE_BUFFER_COUNT]; + alGetSourcei(this->source, AL_BUFFERS_QUEUED, &buffersQueued); + if(buffersQueued > 0) { + alSourceUnqueueBuffers(this->source, buffersQueued, buffersUnqueued); + } +} + +void AudioSource::attachBuffers() { + alSourceQueueBuffers(this->source, AUDIO_SOURCE_BUFFER_COUNT, this->buffers); +} + +void AudioSource::onStart() { + // Create source and buffers + alGenSources((ALuint)1, &this->source); + alGenBuffers((ALuint)AUDIO_SOURCE_BUFFER_COUNT, this->buffers); + + // Set up the source positions + glm::vec3 position = this->transform->getWorldPosition(); + alSource3f(this->source, AL_POSITION, position.x, position.y, position.z); + alSource3f(this->source, AL_VELOCITY, 0, 0, 0); + + // In future these will probably be tied to the layer + alSourcef(this->source, AL_PITCH, 1); + alSourcef(this->source, AL_GAIN, 1); + + // Mark as ready. + this->ready = true; + + // Listen for events + useEventLegacy([&]{ + glm::vec3 position = this->transform->getWorldPosition(); + alSource3f(this->source, AL_POSITION, position.x, position.y, position.z); + }, this->transform->eventTransformUpdated); + + useEvent([&](float_t delta){ + ALuint bufferId; + ALint buffersProcessed; + + assertTrue(this->ready); + + // What is the user trying to do? + if(this->state == AUDIO_SOURCE_STATE_PLAYING) { + assertTrue(this->data != nullptr); + assertTrue(this->data->loaded); + + // Handle the special game-paused music-paused state. + if(this->playMode == AUDIO_PLAY_MODE_UNPAUSED && this->getGame()->timeManager.isPaused) { + if(this->internalState == AUDIO_SOURCE_STATE_PLAYING) { + // Functionally, this is the same as pausing + alSourcePause(this->source); + this->internalState = AUDIO_SOURCE_STATE_PAUSED; + } + return;// Do nothing else, at all. + } + + // They are trying to play. We need to check if we are already playing + if(this->internalState == AUDIO_SOURCE_STATE_PLAYING) { + // We are good to continue buffering audio data. + buffersProcessed = 0; + alGetSourcei(this->source, AL_BUFFERS_PROCESSED, &buffersProcessed); + + while(buffersProcessed > 0) { + alSourceUnqueueBuffers(this->source, 1, &bufferId); + this->fillBuffer(bufferId); + alSourceQueueBuffers(this->source, 1, &bufferId); + buffersProcessed--; + + // Now, before we go to the next buffer let's see if we need to + // update the internal state + if(this->bufferPosition != 0) continue; + + // We reached the end of the buffer whilst filling. What to do now? + if(this->loop) { + // We're looping so it's fine + this->eventLooped.invoke(); + continue; + } + + // We are NOT looping, so we need to finish here. + this->state = AUDIO_SOURCE_STATE_STOPPED; + this->eventFinished.invoke(); + this->bufferPosition = 0; + buffersProcessed = 0; + } + } else if(this->internalState == AUDIO_SOURCE_STATE_STOPPED) { + // Not yet playing. First thing we need to do is fill the buffers. + for(int32_t i = 0; i < AUDIO_SOURCE_BUFFER_COUNT; i++) { + this->fillBuffer(this->buffers[i]); + } + + // Now attach the buffers + this->attachBuffers(); + + // And begin playing + alSourcePlay(this->source); + this->internalState = AUDIO_SOURCE_STATE_PLAYING; + this->eventPlaying.invoke(); + + } else if(AUDIO_SOURCE_STATE_PAUSED) { + // Request to resume + alSourcePlay(this->source); + this->eventResumed.invoke(); + this->internalState = AUDIO_SOURCE_STATE_PLAYING; + + } else { + assertUnreachable(); + } + } else if(this->state == AUDIO_SOURCE_STATE_PAUSED) { + assertTrue(this->data != nullptr); + assertTrue(this->data->loaded); + + // Pause has been requested. + if(this->internalState != AUDIO_SOURCE_STATE_PLAYING) return; + + // We are playing something, pause it + alSourcePause(this->source); + this->internalState = AUDIO_SOURCE_STATE_PAUSED; + this->eventPaused.invoke(); + + } else if(this->state == AUDIO_SOURCE_STATE_STOPPED) { + if(this->internalState == AUDIO_SOURCE_STATE_STOPPED) return; + + assertTrue(this->data != nullptr); + assertTrue(this->data->loaded); + + // Release the buffers + alSourceStop(this->source); + this->detatchBuffers(); + this->internalState = AUDIO_SOURCE_STATE_STOPPED; + this->eventStopped.invoke(); + this->bufferPosition = 0; + + } else { + assertUnreachable(); + } + }, getScene()->eventSceneUpdate); +} + +AudioSourceState AudioSource::getRealState() { + return this->internalState; +} + +void AudioSource::rewind() { + this->bufferPosition = 0; +} + +AudioAsset * AudioSource::getAudioData() { + return this->data; +} + +void AudioSource::setAudioData(AudioAsset *data) { + this->data = data; + this->rewind(); +} + +void AudioSource::stop() { + this->state = AUDIO_SOURCE_STATE_STOPPED; +} + +void AudioSource::play() { + this->state = AUDIO_SOURCE_STATE_PLAYING; +} + +void AudioSource::pause() { + this->state = AUDIO_SOURCE_STATE_PAUSED; +} + +void AudioSource::onDispose() { + assertTrue(this->ready); + this->ready = false; + + alDeleteSources((ALuint)1, &this->source); + alDeleteBuffers((ALuint)AUDIO_SOURCE_BUFFER_COUNT, this->buffers); } \ No newline at end of file diff --git a/src/dawnopenal/scene/components/audio/AudioSource.hpp b/src/dawnopenal/scene/components/audio/AudioSource.hpp index 830044fc..6fc97ce4 100644 --- a/src/dawnopenal/scene/components/audio/AudioSource.hpp +++ b/src/dawnopenal/scene/components/audio/AudioSource.hpp @@ -1,133 +1,118 @@ -// Copyright (c) 2023 Dominic Masters -// -// This software is released under the MIT License. -// https://opensource.org/licenses/MIT - -#pragma once -#include "dawnopenal.hpp" -#include "scene/SceneItemComponent.hpp" -#include "asset/assets/AudioAsset.hpp" - -#define AUDIO_SOURCE_BUFFER_COUNT 3 -#define AUDIO_SOURCE_BUFFER_SIZE 8192 - -namespace Dawn { - enum AudioSourceState { - AUDIO_SOURCE_STATE_STOPPED, - AUDIO_SOURCE_STATE_PLAYING, - AUDIO_SOURCE_STATE_PAUSED - }; - - enum AudioSourcePlayMode { - AUDIO_PLAY_MODE_ALWAYS, - AUDIO_PLAY_MODE_UNPAUSED - }; - - class AudioSource : public SceneItemComponent { - private: - ALuint source; - bool_t ready = false; - ALuint buffers[AUDIO_SOURCE_BUFFER_COUNT]; - size_t bufferPosition = 0; - - // Settings - enum AudioSourceState internalState = AUDIO_SOURCE_STATE_STOPPED; - int32_t layer = 0; - AudioAsset *data = nullptr; - AudioSourcePlayMode playMode = AUDIO_PLAY_MODE_UNPAUSED; - - /** - * Internally update the audio's state based on the current settings. - */ - void updateAudioSource(); - - void fillBuffer(ALuint buffer); - void detatchBuffers(); - void attachBuffers(); - - // Events - void onSceneUpdate(); - void onTransformUpdate(); - - public: - bool_t loop = false; - enum AudioSourceState state = AUDIO_SOURCE_STATE_STOPPED; - - Event<> eventPlaying; - Event<> eventResumed; - Event<> eventPaused; - Event<> eventStopped; - Event<> eventFinished; - Event<> eventLooped; - - /** - * Creates an Audio Source item component. - * - * @param item SceneItem that this audio source is attached to. - */ - AudioSource(SceneItem *item); - - /** - * Returns the real current state of the sound. Refer to getPlayingState - * but basically the actually requested play state and what the hardware - * and internal logic is doing may result in the actual state being a - * little different. You cannot control this, but you can read it. - * - * @return The actual current playing state. - */ - AudioSourceState getRealState(); - - /** - * Rewinds the audio source. At the time of writing this comment (23017) I - * am unsure of the behaviour when rewinding whilst the source is active. - */ - void rewind(); - - /** - * Returns the current audio data for this source. - * - * @return Audio data that is atached to this source. - */ - AudioAsset * getAudioData(); - - /** - * Sets the audio data for this source. Currently switching between the - * audio data during playback may have undefined behavior. - * - * @param data Data to set. - */ - void setAudioData(AudioAsset *data); - - /** - * Returns the play mode for the source. - * - * @return Current play mode. - */ - enum AudioSourcePlayMode getPlayMode(); - - /** - * Sets the play mode for the source. - * - * @param mode Play Mode to use. - */ - void setPlayMode(enum AudioSourcePlayMode mode); - - /** - * Stop playing this audio source. Shorthand for setting the state. - */ - void stop(); - - /** - * Play this audio source. Shorthand for state setting. - */ - void play(); - - /** - * Pause this audio source. Shorthand for state setting. - */ - void pause(); - - void onStart() override; - void onDispose() override; - }; +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "dawnopenal.hpp" +#include "scene/SceneItemComponent.hpp" +#include "asset/assets/AudioAsset.hpp" + +#define AUDIO_SOURCE_BUFFER_COUNT 3 +#define AUDIO_SOURCE_BUFFER_SIZE 8192 + +namespace Dawn { + enum AudioSourceState { + AUDIO_SOURCE_STATE_STOPPED, + AUDIO_SOURCE_STATE_PLAYING, + AUDIO_SOURCE_STATE_PAUSED + }; + + enum AudioSourcePlayMode { + AUDIO_PLAY_MODE_ALWAYS, + AUDIO_PLAY_MODE_UNPAUSED + }; + + class AudioSource : public SceneItemComponent { + private: + ALuint source; + bool_t ready = false; + ALuint buffers[AUDIO_SOURCE_BUFFER_COUNT]; + size_t bufferPosition = 0; + + // Settings + enum AudioSourceState internalState = AUDIO_SOURCE_STATE_STOPPED; + int32_t layer = 0; + AudioAsset *data = nullptr; + + /** + * Internally update the audio's state based on the current settings. + */ + void updateAudioSource(); + + void fillBuffer(ALuint buffer); + void detatchBuffers(); + void attachBuffers(); + + // Events + void onSceneUpdate(); + + public: + bool_t loop = false; + enum AudioSourceState state = AUDIO_SOURCE_STATE_STOPPED; + AudioSourcePlayMode playMode; + + Event<> eventPlaying; + Event<> eventResumed; + Event<> eventPaused; + Event<> eventStopped; + Event<> eventFinished; + Event<> eventLooped; + + /** + * Creates an Audio Source item component. + * + * @param item SceneItem that this audio source is attached to. + */ + AudioSource(SceneItem *item); + + /** + * Returns the real current state of the sound. Refer to getPlayingState + * but basically the actually requested play state and what the hardware + * and internal logic is doing may result in the actual state being a + * little different. You cannot control this, but you can read it. + * + * @return The actual current playing state. + */ + AudioSourceState getRealState(); + + /** + * Rewinds the audio source. At the time of writing this comment (23017) I + * am unsure of the behaviour when rewinding whilst the source is active. + */ + void rewind(); + + /** + * Returns the current audio data for this source. + * + * @return Audio data that is atached to this source. + */ + AudioAsset * getAudioData(); + + /** + * Sets the audio data for this source. Currently switching between the + * audio data during playback may have undefined behavior. + * + * @param data Data to set. + */ + void setAudioData(AudioAsset *data); + + /** + * Stop playing this audio source. Shorthand for setting the state. + */ + void stop(); + + /** + * Play this audio source. Shorthand for state setting. + */ + void play(); + + /** + * Pause this audio source. Shorthand for state setting. + */ + void pause(); + + void onStart() override; + void onDispose() override; + }; } \ No newline at end of file diff --git a/src/dawntictactoe/components/TicTacToeGame.cpp b/src/dawntictactoe/components/TicTacToeGame.cpp index f673ddf5..b326377f 100644 --- a/src/dawntictactoe/components/TicTacToeGame.cpp +++ b/src/dawntictactoe/components/TicTacToeGame.cpp @@ -14,73 +14,67 @@ TicTacToeGame::TicTacToeGame(SceneItem *item) : SceneItemComponent(item) {} void TicTacToeGame::onStart() { // Map tiles by tile number = tile - auto tiles = getScene()->findComponents(); - auto itTiles = tiles.begin(); - while(itTiles != tiles.end()) { + auto ts = getScene()->findComponents(); + auto itTiles = ts.begin(); + while(itTiles != ts.end()) { this->tiles[(*itTiles)->tile] = *itTiles; ++itTiles; } // Listen - getScene()->eventSceneUpdate.addListener(this, &TicTacToeGame::onSceneUpdate); -} + useEvent([&](float_t delta) { + // Get mouse in screen space. + auto mouse = getGame()->inputManager.getAxis2D(INPUT_BIND_MOUSE_X, INPUT_BIND_MOUSE_Y); + mouse *= 2.0f; + mouse -= glm::vec2(1, 1); -void TicTacToeGame::onDispose() { - getScene()->eventSceneUpdate.removeListener(this, &TicTacToeGame::onSceneUpdate); -} + Camera *camera = getScene()->findComponent(); + if(camera == nullptr) return; -void TicTacToeGame::onSceneUpdate() { - // Get mouse in screen space. - auto mouse = getGame()->inputManager.getAxis2D(INPUT_BIND_MOUSE_X, INPUT_BIND_MOUSE_Y); - mouse *= 2.0f; - mouse -= glm::vec2(1, 1); - - Camera *camera = getScene()->findComponent(); - if(camera == nullptr) return; - - struct Ray3D ray; - ray.origin = camera->transform->getWorldPosition(); - ray.direction = camera->getRayDirectionFromScreenSpace(mouse); - - - // Find the hovered tile (if any) - TicTacToeTile *hovered = nullptr; - auto results = getPhysics()->raycast3DAll(ray); - auto itResult = results.begin(); - while(itResult != results.end()) { - auto result = *itResult; - auto tile = result.collider->item->getComponent(); - if(tile == nullptr) { - ++itResult; - continue; - } + struct Ray3D ray; + ray.origin = camera->transform->getWorldPosition(); + ray.direction = camera->getRayDirectionFromScreenSpace(mouse); - hovered = tile; - break; - } - // Now update the state of each tile, also get the state while we are at it - std::map tileMap; - - auto itTiles = tiles.begin(); - while(itTiles != tiles.end()) { - auto t = itTiles->second; - - if(t == hovered) { - if(t->tileState == TIC_TAC_TOE_EMPTY && getGame()->inputManager.isPressed(INPUT_BIND_MOUSE_CLICK)) { - t->tileState = nextMove; - nextMove = nextMove == TIC_TAC_TOE_NOUGHT ? TIC_TAC_TOE_CROSS : TIC_TAC_TOE_NOUGHT; - } else if(t->tileState == TIC_TAC_TOE_EMPTY) { - t->hovered = true; + // Find the hovered tile (if any) + TicTacToeTile *hovered = nullptr; + auto results = getPhysics()->raycast3DAll(ray); + auto itResult = results.begin(); + while(itResult != results.end()) { + auto result = *itResult; + auto tile = result.collider->item->getComponent(); + if(tile == nullptr) { + ++itResult; + continue; } - } else { - t->hovered = false; + + hovered = tile; + break; } - tileMap[itTiles->second->tile] = itTiles->second->tileState; - ++itTiles; - } - // Determine winner - std::vector winningCombo; - auto winner = ticTacToeDetermineWinner(tileMap, &winningCombo); + // Now update the state of each tile, also get the state while we are at it + std::map tileMap; + + auto itTiles = tiles.begin(); + while(itTiles != tiles.end()) { + auto t = itTiles->second; + + if(t == hovered) { + if(t->tileState == TIC_TAC_TOE_EMPTY && getGame()->inputManager.isPressed(INPUT_BIND_MOUSE_CLICK)) { + t->tileState = nextMove; + nextMove = nextMove == TIC_TAC_TOE_NOUGHT ? TIC_TAC_TOE_CROSS : TIC_TAC_TOE_NOUGHT; + } else if(t->tileState == TIC_TAC_TOE_EMPTY) { + t->hovered = true; + } + } else { + t->hovered = false; + } + tileMap[itTiles->second->tile] = itTiles->second->tileState; + ++itTiles; + } + + // Determine winner + std::vector winningCombo; + auto winner = ticTacToeDetermineWinner(tileMap, &winningCombo); + }, getScene()->eventSceneUpdate); } \ No newline at end of file diff --git a/src/dawntictactoe/components/TicTacToeGame.hpp b/src/dawntictactoe/components/TicTacToeGame.hpp index 819ea95e..29983eeb 100644 --- a/src/dawntictactoe/components/TicTacToeGame.hpp +++ b/src/dawntictactoe/components/TicTacToeGame.hpp @@ -17,8 +17,5 @@ namespace Dawn { TicTacToeGame(SceneItem *item); void onStart() override; - void onDispose() override; - - void onSceneUpdate(); }; } \ No newline at end of file