Upgraded to C++ 20 & better audio voice management
This commit is contained in:
@@ -121,7 +121,7 @@ int ApplicationMain(int argc, char* argv[])
|
||||
|
||||
// Initialize Engine & Window
|
||||
Engine::Init();
|
||||
auto& window = Engine::GetCurrentWindow();
|
||||
auto window = Engine::GetCurrentWindow();
|
||||
window->SetSize(launchSettings.resolution);
|
||||
window->SetTitle(launchSettings.windowTitle);
|
||||
|
||||
|
||||
@@ -56,6 +56,16 @@ public:
|
||||
ComponentTableReset(component.IsPlaying, false);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
{
|
||||
ImGui::Text("Loop");
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::Checkbox("##Loop", &component.Loop);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ComponentTableReset(component.Loop, false);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
{
|
||||
ImGui::Text("Volume");
|
||||
ImGui::TableNextColumn();
|
||||
@@ -85,6 +95,51 @@ public:
|
||||
|
||||
ComponentTableReset(component.Pan, 0.0f);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
{
|
||||
ImGui::Text("Spatialized");
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::Checkbox("##Spatialized", &component.Spatialized);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ComponentTableReset(component.Spatialized, false);
|
||||
}
|
||||
|
||||
if (component.Spatialized)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
{
|
||||
ImGui::Text("Min Distance");
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::DragFloat("##minDistance", &component.MinDistance, 0.001f, 0.0f);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ComponentTableReset(component.MinDistance, 1.0f);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
{
|
||||
ImGui::Text("Max Distance");
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::DragFloat("##maxDistance", &component.MaxDistance, 0.001f, 0.0f);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ComponentTableReset(component.MaxDistance, 10.0f);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
{
|
||||
ImGui::Text("Attenuation Factor");
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::DragFloat("##attenuationFactor", &component.AttenuationFactor, 0.001f, 0.0f);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ComponentTableReset(component.AttenuationFactor, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
EndComponentTable();
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
|
||||
uint32_t animIndex = model->GetCurrentAnimationIndex();
|
||||
uint32_t oldAnimIndex = animIndex;
|
||||
auto& animations = model->GetAnimations();
|
||||
auto animations = model->GetAnimations();
|
||||
if (ImGui::BeginCombo("Type", model->GetCurrentAnimation()->GetName().c_str()))
|
||||
{
|
||||
for (int n = 0; n < model->GetAnimationsCount(); n++)
|
||||
|
||||
@@ -175,7 +175,7 @@ void GizmoDrawer::DrawGizmos(Ref<Scene> scene)
|
||||
auto capsuleColliderView = scene->m_Registry.view<TransformComponent, CapsuleColliderComponent>();
|
||||
for (auto e : capsuleColliderView)
|
||||
{
|
||||
auto& [transform, capsule] = scene->m_Registry.get<TransformComponent, CapsuleColliderComponent>(e);
|
||||
auto [transform, capsule] = scene->m_Registry.get<TransformComponent, CapsuleColliderComponent>(e);
|
||||
|
||||
const auto entityId = (uint32_t)e;
|
||||
if (_CapsuleEntity.find(entityId) == _CapsuleEntity.end())
|
||||
@@ -199,7 +199,7 @@ void GizmoDrawer::DrawGizmos(Ref<Scene> scene)
|
||||
auto cylinderColliderView = scene->m_Registry.view<TransformComponent, CylinderColliderComponent>();
|
||||
for (auto e : cylinderColliderView)
|
||||
{
|
||||
auto& [transform, cylinder] = scene->m_Registry.get<TransformComponent, CylinderColliderComponent>(e);
|
||||
auto [transform, cylinder] = scene->m_Registry.get<TransformComponent, CylinderColliderComponent>(e);
|
||||
|
||||
const auto entityId = (uint32_t)e;
|
||||
if (_CylinderEntity.find(entityId) == _CylinderEntity.end())
|
||||
@@ -235,7 +235,7 @@ void GizmoDrawer::DrawGizmos(Ref<Scene> scene)
|
||||
auto meshColliderView = scene->m_Registry.view<TransformComponent, MeshColliderComponent, ModelComponent>();
|
||||
for (auto e : meshColliderView)
|
||||
{
|
||||
auto& [transform, mesh, model] = scene->m_Registry.get<TransformComponent, MeshColliderComponent, ModelComponent>(e);
|
||||
auto [transform, mesh, model] = scene->m_Registry.get<TransformComponent, MeshColliderComponent, ModelComponent>(e);
|
||||
|
||||
// Component has no mesh set.
|
||||
if (!model.ModelResource)
|
||||
|
||||
@@ -6,5 +6,5 @@ class PopupHelper
|
||||
public:
|
||||
static void OpenPopup(const std::string& id);
|
||||
static bool DefineConfirmationDialog(const std::string& id, const std::string& text);
|
||||
static bool PopupHelper::DefineTextDialog(const std::string& id, std::string& currentText);
|
||||
static bool DefineTextDialog(const std::string& id, std::string& currentText);
|
||||
};
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace Nuake {
|
||||
|
||||
if (const int result = gbuffer.ReadPixel(3, pixelPos); result > 0)
|
||||
{
|
||||
auto& ent = Entity{ (entt::entity)(result - 1), Engine::GetCurrentScene().get() };
|
||||
auto ent = Entity{ (entt::entity)(result - 1), Engine::GetCurrentScene().get() };
|
||||
if (ent.IsValid())
|
||||
{
|
||||
Selection = EditorSelection(ent);
|
||||
@@ -423,9 +423,9 @@ namespace Nuake {
|
||||
ImGui::TableNextColumn();
|
||||
{
|
||||
bool& isVisible = e.GetComponent<VisibilityComponent>().Visible;
|
||||
char* visibilityIcon = isVisible ? ICON_FA_EYE : ICON_FA_EYE_SLASH;
|
||||
std::string visibilityIcon = isVisible ? ICON_FA_EYE : ICON_FA_EYE_SLASH;
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0, 0, 0, 0 });
|
||||
if (ImGui::Button(visibilityIcon, { 40, 0 }))
|
||||
if (ImGui::Button(visibilityIcon.c_str(), {40, 0}))
|
||||
{
|
||||
isVisible = !isVisible;
|
||||
}
|
||||
@@ -1540,7 +1540,7 @@ namespace Nuake {
|
||||
_WelcomeWindow->LoadQueuedProject();
|
||||
isLoadingProjectQueue = false;
|
||||
|
||||
auto& window = Window::Get();
|
||||
auto window = Window::Get();
|
||||
window->SetDecorated(true);
|
||||
window->SetSize({ 1900, 1000 });
|
||||
window->Center();
|
||||
|
||||
@@ -16,7 +16,7 @@ private:
|
||||
Ref<Nuake::Texture> _NuakeLogo;
|
||||
|
||||
public:
|
||||
static LoadingSplash& LoadingSplash::Get()
|
||||
static LoadingSplash& Get()
|
||||
{
|
||||
static LoadingSplash instance;
|
||||
return instance;
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Nuake {
|
||||
{
|
||||
//ImGuiTextSTD("Name", p.name);
|
||||
ImGui::TableNextColumn();
|
||||
std::string current_item = NULL;
|
||||
std::string current_item = "";
|
||||
if (ImGui::BeginCombo(("TypeSelection" + std::to_string(idx)).c_str(), current_item.c_str()))
|
||||
{
|
||||
for (int n = 0; n < IM_ARRAYSIZE(items); n++)
|
||||
|
||||
@@ -37,9 +37,30 @@ namespace Nuake {
|
||||
|
||||
void AudioManager::Deinitialize()
|
||||
{
|
||||
m_WavSamples.clear();
|
||||
m_ActiveClips.clear();
|
||||
m_Soloud->deinit();
|
||||
}
|
||||
|
||||
void AudioManager::SetListenerPosition(const Vector3& position, const Vector3& direction, const Vector3& up)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(m_AudioQueueMutex);
|
||||
|
||||
if (position != m_ListenerPosition || direction != m_ListenerDirection || up != m_ListenerUp)
|
||||
{
|
||||
m_ListenerPosition = position;
|
||||
m_ListenerDirection = direction;
|
||||
m_ListenerUp = up;
|
||||
|
||||
m_Soloud->set3dListenerParameters(m_ListenerPosition.x, m_ListenerPosition.y, m_ListenerPosition.z,
|
||||
m_ListenerDirection.x, m_ListenerDirection.y, m_ListenerDirection.z,
|
||||
m_ListenerUp.x, m_ListenerUp.y, m_ListenerUp.z
|
||||
);
|
||||
|
||||
m_Soloud->update3dAudio();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioManager::PlayTTS(const std::string& text)
|
||||
{
|
||||
|
||||
@@ -60,11 +81,53 @@ namespace Nuake {
|
||||
m_AudioQueue.push(request);
|
||||
}
|
||||
|
||||
void AudioManager::UpdateVoice(const AudioRequest& request)
|
||||
{
|
||||
// Acquire mutex lock
|
||||
const std::lock_guard<std::mutex> lock(m_AudioQueueMutex);
|
||||
|
||||
if (IsVoiceActive(request.audioFile))
|
||||
{
|
||||
if (!m_Soloud->isValidVoiceHandle(m_ActiveClips[request.audioFile]))
|
||||
{
|
||||
m_ActiveClips.erase(request.audioFile);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SoLoud::handle& voice = m_ActiveClips[request.audioFile];
|
||||
m_Soloud->set3dSourcePosition(voice, request.position.x, request.position.y, request.position.z);
|
||||
m_Soloud->setLooping(voice, request.Loop);
|
||||
m_Soloud->set3dSourceMinMaxDistance(voice, request.MinDistance, request.MaxDistance);
|
||||
m_Soloud->set3dSourceAttenuation(voice, 1, request.AttenuationFactor);
|
||||
m_Soloud->setRelativePlaySpeed(voice, request.speed);
|
||||
m_Soloud->setPan(voice, request.pan);
|
||||
m_Soloud->update3dAudio();
|
||||
}
|
||||
|
||||
void AudioManager::StopVoice(const std::string& filePath)
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(m_AudioQueueMutex);
|
||||
|
||||
if (!IsVoiceActive(filePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SoLoud::handle& voice = m_ActiveClips[filePath];
|
||||
m_Soloud->stop(voice);
|
||||
}
|
||||
|
||||
bool AudioManager::IsWavLoaded(const std::string& filePath) const
|
||||
{
|
||||
return m_WavSamples.find(filePath) != m_WavSamples.end();
|
||||
}
|
||||
|
||||
bool AudioManager::IsVoiceActive(const std::string& voice) const
|
||||
{
|
||||
return m_ActiveClips.find(voice) != m_ActiveClips.end();
|
||||
}
|
||||
|
||||
void AudioManager::LoadWavAudio(const std::string& filePath)
|
||||
{
|
||||
m_WavSamples[filePath] = SoLoud::Wav();
|
||||
@@ -73,24 +136,57 @@ namespace Nuake {
|
||||
|
||||
void AudioManager::AudioThreadLoop()
|
||||
{
|
||||
|
||||
|
||||
while(m_AudioThreadRunning)
|
||||
{
|
||||
// Acquire mutex lock
|
||||
const std::lock_guard<std::mutex> lock(m_AudioQueueMutex);
|
||||
|
||||
//CleanupInactiveVoices();
|
||||
|
||||
// Check if we have audio queued
|
||||
while (!m_AudioQueue.empty())
|
||||
{
|
||||
AudioRequest& currentAudio = m_AudioQueue.front();
|
||||
AudioRequest& audioRequest = m_AudioQueue.front();
|
||||
SoLoud::Wav& audio = m_WavSamples[audioRequest.audioFile];
|
||||
|
||||
SoLoud::handle soloudHandle = m_Soloud->play(m_WavSamples[currentAudio.audioFile]);
|
||||
m_Soloud->setVolume(soloudHandle, currentAudio.volume);
|
||||
m_Soloud->setPan(soloudHandle, currentAudio.pan);
|
||||
m_Soloud->setRelativePlaySpeed(soloudHandle, currentAudio.speed);
|
||||
SoLoud::handle soloudHandle;
|
||||
if (!audioRequest.spatialized)
|
||||
{
|
||||
soloudHandle = m_Soloud->play(audio);
|
||||
m_Soloud->setVolume(soloudHandle, audioRequest.volume);
|
||||
m_Soloud->setPan(soloudHandle, audioRequest.pan);
|
||||
m_Soloud->setRelativePlaySpeed(soloudHandle, audioRequest.speed);
|
||||
m_Soloud->setLooping(soloudHandle, audioRequest.Loop);
|
||||
}
|
||||
else
|
||||
{
|
||||
const Vector3& position = audioRequest.position;
|
||||
soloudHandle = m_Soloud->play3d(audio, position.x, position.y, position.z);
|
||||
m_Soloud->set3dSourceMinMaxDistance(soloudHandle, audioRequest.MinDistance, audioRequest.MaxDistance);
|
||||
m_Soloud->set3dSourceAttenuation(soloudHandle, 1, audioRequest.AttenuationFactor);
|
||||
m_Soloud->setLooping(soloudHandle, audioRequest.Loop);
|
||||
}
|
||||
|
||||
m_ActiveClips[audioRequest.audioFile] = soloudHandle;
|
||||
|
||||
// Remove item from queue.
|
||||
m_AudioQueue.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioManager::CleanupInactiveVoices()
|
||||
{
|
||||
if (m_ActiveClips.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::erase_if(m_ActiveClips, [this](const auto& item)
|
||||
{
|
||||
return !m_Soloud->isValidVoiceHandle(item.second);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,17 @@
|
||||
#pragma once
|
||||
#include "src/Core/Core.h"
|
||||
#include <src/Core/Maths.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
|
||||
namespace SoLoud
|
||||
{
|
||||
class Soloud;
|
||||
class AudioSource;
|
||||
}
|
||||
|
||||
// Temp code
|
||||
struct AudioRequest
|
||||
{
|
||||
std::string audioFile;
|
||||
float volume = 1.0f;
|
||||
float pan = 0.0f;
|
||||
float speed = 1.0f;
|
||||
|
||||
};
|
||||
|
||||
namespace SoLoud
|
||||
{
|
||||
class Wav;
|
||||
@@ -27,6 +19,20 @@ namespace SoLoud
|
||||
|
||||
namespace Nuake
|
||||
{
|
||||
struct AudioRequest
|
||||
{
|
||||
std::string audioFile;
|
||||
float volume = 1.0f;
|
||||
float pan = 0.0f;
|
||||
float speed = 1.0f;
|
||||
bool spatialized = false;
|
||||
Vector3 position;
|
||||
float MinDistance = 1.0f;
|
||||
float MaxDistance = 25.0f;
|
||||
float AttenuationFactor = 1.0f;
|
||||
bool Loop = false;
|
||||
};
|
||||
|
||||
// This is a singleton that manages everything for audio.
|
||||
class AudioManager
|
||||
{
|
||||
@@ -41,7 +47,13 @@ namespace Nuake
|
||||
std::atomic<bool> m_AudioQueued = { false };
|
||||
std::queue<AudioRequest> m_AudioQueue;
|
||||
|
||||
Vector3 m_ListenerPosition;
|
||||
Vector3 m_ListenerDirection;
|
||||
Vector3 m_ListenerUp;
|
||||
|
||||
std::unordered_map<std::string, SoLoud::Wav> m_WavSamples;
|
||||
std::unordered_map<std::string, unsigned int> m_ActiveClips;
|
||||
|
||||
public:
|
||||
AudioManager();
|
||||
~AudioManager();
|
||||
@@ -59,12 +71,20 @@ namespace Nuake
|
||||
float GetVolume() const;
|
||||
void SetVolume(float volume);
|
||||
|
||||
void SetListenerPosition(const Vector3& position, const Vector3& direction, const Vector3& up);
|
||||
|
||||
void PlayTTS(const std::string& text);
|
||||
|
||||
void QueueWavAudio(const AudioRequest& request);
|
||||
void UpdateVoice(const AudioRequest & request);
|
||||
void StopVoice(const std::string& filePath);
|
||||
bool IsWavLoaded(const std::string& filePath) const;
|
||||
bool IsVoiceActive(const std::string & voice) const;
|
||||
void LoadWavAudio(const std::string& filePath);
|
||||
|
||||
private:
|
||||
void AudioThreadLoop();
|
||||
|
||||
void CleanupInactiveVoices();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace Nuake
|
||||
Ref<File> FileSystem::GetFile(const std::string& path)
|
||||
{
|
||||
// Note, Might be broken on other platforms.
|
||||
auto& splits = String::Split(path, '/');
|
||||
auto splits = String::Split(path, '/');
|
||||
|
||||
int currentDepth = -1;
|
||||
std::string currentDirName = ".";
|
||||
|
||||
@@ -338,7 +338,7 @@ namespace Nuake
|
||||
settings->mPredictiveContactDistance = 0.01f;
|
||||
settings->mShape = GetJoltShape(cc->Shape);
|
||||
|
||||
auto& joltPosition = JPH::Vec3(cc->Position.x, cc->Position.y, cc->Position.z);
|
||||
auto joltPosition = JPH::Vec3(cc->Position.x, cc->Position.y, cc->Position.z);
|
||||
|
||||
const Quat& bodyRotation = cc->Rotation;
|
||||
const auto& joltRotation = JPH::Quat(bodyRotation.x, bodyRotation.y, bodyRotation.z, bodyRotation.w);
|
||||
@@ -487,14 +487,18 @@ namespace Nuake
|
||||
|
||||
if(ts > minStepDuration)
|
||||
{
|
||||
#ifdef NK_DEBUG
|
||||
Logger::Log("Large step detected: " + std::to_string(ts), "physics", WARNING);
|
||||
#endif
|
||||
collisionSteps = static_cast<float>(ts) / minStepDuration;
|
||||
}
|
||||
|
||||
#ifdef NK_DEBUG
|
||||
if (collisionSteps >= maxStepCount)
|
||||
{
|
||||
Logger::Log("Very large step detected: " + std::to_string(ts), "physics", WARNING);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Prevents having too many steps and running out of jobs
|
||||
collisionSteps = std::min(collisionSteps, maxStepCount);
|
||||
@@ -502,7 +506,6 @@ namespace Nuake
|
||||
// Step the world
|
||||
try
|
||||
{
|
||||
// TODO: Potential memory leak with new keyword.
|
||||
auto joltTempAllocator = CreateRef<JPH::TempAllocatorMalloc>();
|
||||
|
||||
JPH::CharacterVirtual::ExtendedUpdateSettings joltUpdateSettings;
|
||||
@@ -563,11 +566,6 @@ namespace Nuake
|
||||
|
||||
if (!_registeredCharacters.empty())
|
||||
{
|
||||
for (auto& character : _registeredCharacters)
|
||||
{
|
||||
//character.second->RemoveFromPhysicsSystem();
|
||||
}
|
||||
|
||||
_registeredCharacters.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace Nuake {
|
||||
template<typename T>
|
||||
void Push(unsigned int count)
|
||||
{
|
||||
static_assert(false);
|
||||
}
|
||||
|
||||
template<>
|
||||
|
||||
@@ -21,12 +21,12 @@ namespace Nuake
|
||||
|
||||
_ssaoFramebuffer = CreateRef<FrameBuffer>(false, Vector2(_size));
|
||||
|
||||
auto& renderTarget = CreateRef<Texture>(_size, GL_RGBA, GL_RGBA16F, GL_FLOAT);
|
||||
auto renderTarget = CreateRef<Texture>(_size, GL_RGBA, GL_RGBA16F, GL_FLOAT);
|
||||
_ssaoFramebuffer->SetTexture(renderTarget, GL_COLOR_ATTACHMENT0);
|
||||
|
||||
_ssaoBlurFramebuffer = CreateRef<FrameBuffer>(false, Vector2(_size));
|
||||
|
||||
auto& renderTargetBlur = CreateRef<Texture>(_size, GL_RGBA, GL_RGBA16F, GL_FLOAT);
|
||||
auto renderTargetBlur = CreateRef<Texture>(_size, GL_RGBA, GL_RGBA16F, GL_FLOAT);
|
||||
_ssaoBlurFramebuffer->SetTexture(renderTargetBlur);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace Nuake
|
||||
float lineHeight = 0.0f;
|
||||
for (char const& c : str)
|
||||
{
|
||||
Char& letter = style.font->GetChar((int)c);
|
||||
Char letter = style.font->GetChar((int)c);
|
||||
|
||||
if (letter.PlaneBounds.top - letter.PlaneBounds.bottom > lineHeight)
|
||||
lineHeight = letter.PlaneBounds.top - letter.PlaneBounds.bottom;
|
||||
@@ -104,7 +104,7 @@ namespace Nuake
|
||||
float advance = 0.0f;
|
||||
for (char const& c : str)
|
||||
{
|
||||
Char& letter = style.font->GetChar((int)c);
|
||||
Char letter = style.font->GetChar((int)c);
|
||||
advance += letter.Advance * style.fontSize;
|
||||
}
|
||||
return Vector2(advance, lineHeight * style.fontSize);
|
||||
@@ -121,7 +121,7 @@ namespace Nuake
|
||||
float lineHeight = 0.0f;
|
||||
for (char const& c : str)
|
||||
{
|
||||
Char& letter = style.font->GetChar((int)c);
|
||||
Char letter = style.font->GetChar((int)c);
|
||||
|
||||
if (letter.PlaneBounds.top - letter.PlaneBounds.bottom > lineHeight)
|
||||
lineHeight = letter.PlaneBounds.top - letter.PlaneBounds.bottom;
|
||||
@@ -130,7 +130,7 @@ namespace Nuake
|
||||
float advance = 0.0f;
|
||||
for (char const& c : str)
|
||||
{
|
||||
Char& letter = style.font->GetChar((int)c);
|
||||
Char letter = style.font->GetChar((int)c);
|
||||
|
||||
Matrix4 mat = glm::translate(model, Vector3(advance, (-(letter.PlaneBounds.top ) + (lineHeight)) * style.fontSize, 0.f));
|
||||
float scaleX = letter.PlaneBounds.right - letter.PlaneBounds.left;
|
||||
|
||||
@@ -235,7 +235,7 @@ namespace Nuake
|
||||
if (!visibility.Visible || !sprite.SpriteMesh)
|
||||
continue;
|
||||
|
||||
auto& finalQuadTransform = transform.GetGlobalTransform();
|
||||
auto finalQuadTransform = transform.GetGlobalTransform();
|
||||
if (sprite.Billboard)
|
||||
{
|
||||
finalQuadTransform = glm::inverse(mView);
|
||||
@@ -358,7 +358,7 @@ namespace Nuake
|
||||
if (!visibility.Visible || !sprite.SpriteMesh)
|
||||
continue;
|
||||
|
||||
auto& finalQuadTransform = transform.GetGlobalTransform();
|
||||
auto finalQuadTransform = transform.GetGlobalTransform();
|
||||
if (sprite.Billboard)
|
||||
{
|
||||
finalQuadTransform = glm::inverse(mView);
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Nuake
|
||||
{
|
||||
m_RendererId = 0;
|
||||
int channels = 0;
|
||||
m_LocalBuffer = stbi_load_from_memory(data, len, &m_Width, &m_Height, &channels, 0);
|
||||
m_LocalBuffer = stbi_load_from_memory(data, len, &m_Width, &m_Height, &channels, 4);
|
||||
glGenTextures(1, &m_RendererId);
|
||||
glBindTexture(GL_TEXTURE_2D, m_RendererId);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
|
||||
12
Nuake/src/Resource/AudioResource.h
Normal file
12
Nuake/src/Resource/AudioResource.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "src/Resource/Resource.h"
|
||||
|
||||
namespace Nuake {
|
||||
|
||||
class AudioResource : public Resource
|
||||
{
|
||||
public:
|
||||
std::string Path;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -147,7 +147,7 @@ namespace Nuake
|
||||
|
||||
SkeletonNode rootSkeletonNode;
|
||||
ProcessAnimationNode(rootSkeletonNode, scene->mRootNode);
|
||||
model->SetSkeletonRootNode(std::move(rootSkeletonNode));
|
||||
model->SetSkeletonRootNode(rootSkeletonNode);
|
||||
model->SetAnimations(std::move(animations));
|
||||
}
|
||||
|
||||
@@ -189,10 +189,10 @@ namespace Nuake
|
||||
|
||||
Ref<SkinnedMesh> ModelLoader::ProcessSkinnedMesh(aiMesh* node, const aiScene* scene)
|
||||
{
|
||||
auto& vertices = ProcessSkinnedVertices(node);
|
||||
auto& indices = ProcessIndices(node);
|
||||
auto& material = ProcessMaterials(scene, node);
|
||||
auto& bones = std::vector<Bone>();
|
||||
auto vertices = ProcessSkinnedVertices(node);
|
||||
auto indices = ProcessIndices(node);
|
||||
auto material = ProcessMaterials(scene, node);
|
||||
auto bones = std::vector<Bone>();
|
||||
|
||||
if (node->HasBones())
|
||||
{
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
#include "AudioEmitterComponent.h"
|
||||
|
||||
namespace Nuake {
|
||||
|
||||
json AudioEmitterComponent::Serialize()
|
||||
{
|
||||
BEGIN_SERIALIZE();
|
||||
SERIALIZE_VAL(FilePath);
|
||||
SERIALIZE_VAL(IsPlaying);
|
||||
SERIALIZE_VAL(Volume);
|
||||
SERIALIZE_VAL(Pan);
|
||||
SERIALIZE_VAL(PlaybackSpeed);
|
||||
SERIALIZE_VAL(Spatialized);
|
||||
SERIALIZE_VAL(MinDistance);
|
||||
SERIALIZE_VAL(MaxDistance);
|
||||
SERIALIZE_VAL(AttenuationFactor);
|
||||
SERIALIZE_VAL(Loop);
|
||||
END_SERIALIZE();
|
||||
}
|
||||
|
||||
@@ -15,8 +22,14 @@ namespace Nuake {
|
||||
{
|
||||
DESERIALIZE_VAL(FilePath);
|
||||
DESERIALIZE_VAL(Volume);
|
||||
DESERIALIZE_VAL(IsPlaying);
|
||||
DESERIALIZE_VAL(Pan);
|
||||
DESERIALIZE_VAL(PlaybackSpeed);
|
||||
DESERIALIZE_VAL(Spatialized);
|
||||
DESERIALIZE_VAL(MinDistance);
|
||||
DESERIALIZE_VAL(MaxDistance);
|
||||
DESERIALIZE_VAL(AttenuationFactor);
|
||||
DESERIALIZE_VAL(Loop);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,19 @@ namespace Nuake {
|
||||
class AudioEmitterComponent
|
||||
{
|
||||
public:
|
||||
std::string FilePath;
|
||||
|
||||
bool IsPlaying = false;
|
||||
bool Loop = false;
|
||||
|
||||
float Volume = 1.0f;
|
||||
float Pan = 0.0f;
|
||||
float PlaybackSpeed = 1.0f;
|
||||
bool IsPlaying = false;
|
||||
std::string FilePath;
|
||||
|
||||
bool Spatialized = false;
|
||||
float MinDistance = 1.0f;
|
||||
float MaxDistance = 10.0f;
|
||||
float AttenuationFactor = 1.0f;
|
||||
|
||||
json Serialize();
|
||||
bool Deserialize(const json& j);
|
||||
|
||||
@@ -464,7 +464,7 @@ namespace Nuake
|
||||
if (!parentComponent.HasParent)
|
||||
continue;
|
||||
|
||||
auto& entity = Entity{ e, this };
|
||||
auto entity = Entity{ e, this };
|
||||
auto parentEntity = GetEntityByID(parentComponent.ParentID);
|
||||
parentEntity.AddChild(entity);
|
||||
}
|
||||
|
||||
@@ -80,11 +80,6 @@ namespace Nuake
|
||||
Ref<Environment> GetEnvironment() const;
|
||||
void SetEnvironment(Ref<Environment> env);
|
||||
|
||||
template<class T>
|
||||
Ref<T> GetResource(const UUID& uuid)
|
||||
{
|
||||
return m_Resources[uuid];
|
||||
}
|
||||
|
||||
bool Save();
|
||||
bool SaveAs(const std::string& path);
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Nuake
|
||||
{
|
||||
auto& animationTrack = animation->GetTrack(bone.Name);
|
||||
|
||||
Entity& boneEnt = m_Scene->GetEntityByID(bone.EntityHandle);
|
||||
Entity boneEnt = m_Scene->GetEntityByID(bone.EntityHandle);
|
||||
if (boneEnt.IsValid())
|
||||
{
|
||||
auto& transformComponent = boneEnt.GetComponent<TransformComponent>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "AudioSystem.h"
|
||||
|
||||
#include "Engine.h"
|
||||
#include "src/Scene/Scene.h"
|
||||
#include "src/Scene/Entities/Entity.h"
|
||||
#include "src/Scene/Components/AudioEmitterComponent.h"
|
||||
@@ -23,6 +24,19 @@ namespace Nuake
|
||||
|
||||
void AudioSystem::Update(Timestep ts)
|
||||
{
|
||||
auto& audioManager = AudioManager::Get();
|
||||
|
||||
// Update 3D listener of the audio system
|
||||
auto currentCamera = m_Scene->GetCurrentCamera();
|
||||
|
||||
Vector3 direction = currentCamera->GetDirection();
|
||||
if (Engine::IsPlayMode())
|
||||
{
|
||||
direction *= Vector3(-1, -1, -1);
|
||||
}
|
||||
|
||||
audioManager.SetListenerPosition(currentCamera->GetTranslation(), std::move(direction), currentCamera->GetUp());
|
||||
|
||||
auto view = m_Scene->m_Registry.view<TransformComponent, AudioEmitterComponent>();
|
||||
for (auto& e : view)
|
||||
{
|
||||
@@ -30,21 +44,50 @@ namespace Nuake
|
||||
|
||||
if (audioEmitterComponent.FilePath.empty())
|
||||
{
|
||||
// Doesn't have a file
|
||||
continue;
|
||||
}
|
||||
|
||||
if (audioEmitterComponent.IsPlaying)
|
||||
const bool isPlaying = audioEmitterComponent.IsPlaying;
|
||||
const std::string absoluteFilePath = FileSystem::RelativeToAbsolute(audioEmitterComponent.FilePath);
|
||||
const bool isVoiceActive = audioManager.IsVoiceActive(absoluteFilePath);
|
||||
|
||||
AudioRequest audioRequest;
|
||||
audioRequest.audioFile = absoluteFilePath;
|
||||
audioRequest.pan = audioEmitterComponent.Pan;
|
||||
audioRequest.volume = audioEmitterComponent.Volume;
|
||||
audioRequest.speed = audioEmitterComponent.PlaybackSpeed;
|
||||
audioRequest.spatialized = audioEmitterComponent.Spatialized;
|
||||
audioRequest.Loop = audioEmitterComponent.Loop;
|
||||
audioRequest.position = transformComponent.GetGlobalTransform()[3];
|
||||
audioRequest.MinDistance = audioEmitterComponent.MinDistance;
|
||||
audioRequest.MaxDistance = audioEmitterComponent.MaxDistance;
|
||||
audioRequest.AttenuationFactor = audioEmitterComponent.AttenuationFactor;
|
||||
|
||||
if (isVoiceActive)
|
||||
{
|
||||
const std::string absoluteFilePath = FileSystem::RelativeToAbsolute(audioEmitterComponent.FilePath);
|
||||
|
||||
AudioRequest audioRequest;
|
||||
audioRequest.audioFile = absoluteFilePath;
|
||||
audioRequest.pan = audioEmitterComponent.Pan;
|
||||
audioRequest.volume = audioEmitterComponent.Volume;
|
||||
audioRequest.speed = audioEmitterComponent.PlaybackSpeed;
|
||||
|
||||
AudioManager::Get().QueueWavAudio(std::move(audioRequest));
|
||||
audioEmitterComponent.IsPlaying = false;
|
||||
if (!isPlaying && audioEmitterComponent.Loop)
|
||||
{
|
||||
audioManager.StopVoice(absoluteFilePath); // Stop audio
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the active voice with new params
|
||||
audioManager.UpdateVoice(audioRequest);
|
||||
}
|
||||
}
|
||||
else if (isPlaying)
|
||||
{
|
||||
// Reset the play status to false since the audio has been fired
|
||||
if (!audioEmitterComponent.Loop)
|
||||
{
|
||||
audioManager.QueueWavAudio(std::move(audioRequest));
|
||||
audioEmitterComponent.IsPlaying = false;
|
||||
}
|
||||
else if (!isVoiceActive)
|
||||
{
|
||||
audioManager.QueueWavAudio(std::move(audioRequest));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Nuake
|
||||
|
||||
void ParticleSystem::Update(Timestep ts)
|
||||
{
|
||||
auto& view = m_Scene->m_Registry.view<TransformComponent, ParticleEmitterComponent>();
|
||||
auto view = m_Scene->m_Registry.view<TransformComponent, ParticleEmitterComponent>();
|
||||
for (auto& e : view)
|
||||
{
|
||||
auto [transformComponent, emitterComponent] = view.get<TransformComponent, ParticleEmitterComponent>(e);
|
||||
@@ -42,7 +42,7 @@ namespace Nuake
|
||||
|
||||
void ParticleSystem::FixedUpdate(Timestep ts)
|
||||
{
|
||||
auto& view = m_Scene->m_Registry.view<TransformComponent, ParticleEmitterComponent>();
|
||||
auto view = m_Scene->m_Registry.view<TransformComponent, ParticleEmitterComponent>();
|
||||
for (auto& e : view)
|
||||
{
|
||||
auto [transformComponent, emitterComponent] = view.get<TransformComponent, ParticleEmitterComponent>(e);
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Nuake {
|
||||
|
||||
Logger::Log("Initializing ScriptingSystem");
|
||||
|
||||
auto& entities = m_Scene->m_Registry.view<WrenScriptComponent>();
|
||||
auto entities = m_Scene->m_Registry.view<WrenScriptComponent>();
|
||||
for (auto& e : entities)
|
||||
{
|
||||
WrenScriptComponent& wren = entities.get<WrenScriptComponent>(e);
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Nuake
|
||||
{
|
||||
bool hasFoundModule = false;
|
||||
|
||||
auto& splits = String::Split(fileContent, ' ');
|
||||
auto splits = String::Split(fileContent, ' ');
|
||||
for (unsigned int i = 0; i < splits.size(); i++)
|
||||
{
|
||||
std::string s = splits[i];
|
||||
|
||||
@@ -22,14 +22,14 @@ int ApplicationMain(int argc, char* argv[])
|
||||
if (!FileSystem::FileExists(projectPath))
|
||||
{
|
||||
Logger::Log("game.project not found", "runtime", CRITICAL);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const std::string& projectfileContent = FileSystem::ReadFile(projectPath, true);
|
||||
if (projectfileContent.empty())
|
||||
{
|
||||
Logger::Log("game.project was empty", "runtime", CRITICAL);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
project->Deserialize(json::parse(projectfileContent));
|
||||
@@ -90,7 +90,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cdmline, int cmds
|
||||
|
||||
#else
|
||||
|
||||
void main(int argc, char* argv[])
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
return ApplicationMain(argc, argv);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ project "Nuake"
|
||||
}
|
||||
|
||||
filter "system:windows"
|
||||
cppdialect "C++17"
|
||||
cppdialect "C++20"
|
||||
staticruntime "On"
|
||||
defines {
|
||||
"NK_WIN"
|
||||
@@ -154,7 +154,7 @@ project "NuakeRuntime"
|
||||
}
|
||||
|
||||
filter "system:windows"
|
||||
cppdialect "C++17"
|
||||
cppdialect "C++20"
|
||||
staticruntime "On"
|
||||
defines {
|
||||
"NK_WIN"
|
||||
@@ -243,7 +243,7 @@ project "Editor"
|
||||
}
|
||||
|
||||
filter "system:windows"
|
||||
cppdialect "C++17"
|
||||
cppdialect "C++20"
|
||||
staticruntime "On"
|
||||
|
||||
filter "configurations:Debug"
|
||||
|
||||
Reference in New Issue
Block a user