Working on music cross fade and settings
This commit is contained in:
1 parent
84f9fa37a8
commit
b8d79c7b7b
13 files changed
+779
-332
No files matched your search
+129
-7
@@ -43,6 +43,12 @@ bool AudioPlayerAsync::init() {
|
||||
void AudioPlayerAsync::shutdown() {
|
||||
if (!initialized) return;
|
||||
|
||||
if (currentMusic_) {
|
||||
Mix_HaltMusic();
|
||||
Mix_FreeMusic(currentMusic_);
|
||||
currentMusic_ = nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(soundCacheMutex);
|
||||
for (auto& pair : soundCache) {
|
||||
@@ -62,6 +68,7 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in
|
||||
std::cerr << "AudioPlayerAsync not initialized" << std::endl;
|
||||
return;
|
||||
}
|
||||
if (!soundEnabled_) return;
|
||||
|
||||
auto task = [this, filePath, loops, channel]() {
|
||||
Mix_Chunk* sound = nullptr;
|
||||
@@ -99,20 +106,20 @@ void AudioPlayerAsync::playSoundAsync(const std::string& filePath, int loops, in
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
|
||||
std::cout << "AudioPlayerAsync::playMusicAsync called step 1" << std::endl;
|
||||
if (!initialized) return;
|
||||
std::cout << "AudioPlayerAsync::playMusicAsync called step 1" << std::endl;
|
||||
|
||||
if (filePath == currentTrack)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentTrack = filePath;
|
||||
if (!musicEnabled_) return;
|
||||
|
||||
auto task = [filePath, loops]() {
|
||||
std::cout << "AudioPlayerAsync::playMusicAsync called step 3" << std::endl;
|
||||
|
||||
auto task = [this, filePath, loops]() {
|
||||
Mix_HaltMusic();
|
||||
if (currentMusic_) {
|
||||
Mix_FreeMusic(currentMusic_);
|
||||
currentMusic_ = nullptr;
|
||||
}
|
||||
Mix_Music* music = Mix_LoadMUS(filePath.c_str());
|
||||
if (!music) {
|
||||
std::cerr << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
|
||||
@@ -121,7 +128,9 @@ void AudioPlayerAsync::playMusicAsync(const std::string& filePath, int loops) {
|
||||
if (Mix_PlayMusic(music, loops) == -1) {
|
||||
std::cerr << "Mix_PlayMusic failed: " << Mix_GetError() << std::endl;
|
||||
Mix_FreeMusic(music);
|
||||
return;
|
||||
}
|
||||
currentMusic_ = music;
|
||||
};
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
@@ -178,6 +187,7 @@ void AudioPlayerAsync::resumeMusicAsync() {
|
||||
void AudioPlayerAsync::setMusicVolume(int volume) {
|
||||
if (!initialized) return;
|
||||
volume = std::max(0, std::min(128, volume));
|
||||
musicVolume_ = volume;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
Mix_VolumeMusic(volume);
|
||||
#else
|
||||
@@ -192,6 +202,7 @@ void AudioPlayerAsync::setMusicVolume(int volume) {
|
||||
void AudioPlayerAsync::setSoundVolume(int volume) {
|
||||
if (!initialized) return;
|
||||
volume = std::max(0, std::min(128, volume));
|
||||
soundVolume_ = volume;
|
||||
#ifdef __EMSCRIPTEN__
|
||||
Mix_Volume(-1, volume);
|
||||
#else
|
||||
@@ -203,6 +214,117 @@ void AudioPlayerAsync::setSoundVolume(int volume) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::crossFadeMusicAsync(const std::string& filePath, int fadeDurationMs, int loops) {
|
||||
if (!initialized) return;
|
||||
if (filePath == currentTrack) return;
|
||||
currentTrack = filePath;
|
||||
if (!musicEnabled_) return;
|
||||
|
||||
auto task = [this, filePath, fadeDurationMs, loops]() {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
Mix_HaltMusic();
|
||||
if (currentMusic_) { Mix_FreeMusic(currentMusic_); currentMusic_ = nullptr; }
|
||||
#else
|
||||
if (Mix_PlayingMusic()) {
|
||||
Mix_FadeOutMusic(fadeDurationMs / 2);
|
||||
SDL_Delay(fadeDurationMs / 2 + 20);
|
||||
}
|
||||
Mix_HaltMusic();
|
||||
if (currentMusic_) { Mix_FreeMusic(currentMusic_); currentMusic_ = nullptr; }
|
||||
#endif
|
||||
Mix_Music* music = Mix_LoadMUS(filePath.c_str());
|
||||
if (!music) {
|
||||
std::cerr << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
|
||||
return;
|
||||
}
|
||||
if (Mix_FadeInMusic(music, loops, fadeDurationMs / 2) == -1) {
|
||||
std::cerr << "Mix_FadeInMusic failed: " << Mix_GetError() << std::endl;
|
||||
Mix_FreeMusic(music);
|
||||
return;
|
||||
}
|
||||
currentMusic_ = music;
|
||||
};
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
task();
|
||||
#else
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push(std::move(task));
|
||||
cv.notify_one();
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::crossFadeMusicFromPositionAsync(const std::string& filePath, int fadeDurationMs, int loops) {
|
||||
if (!initialized) return;
|
||||
if (filePath == currentTrack) return;
|
||||
currentTrack = filePath;
|
||||
if (!musicEnabled_) return;
|
||||
|
||||
auto task = [this, filePath, fadeDurationMs, loops]() {
|
||||
double position = 0.0;
|
||||
if (currentMusic_ && Mix_PlayingMusic()) {
|
||||
double pos = Mix_GetMusicPosition(currentMusic_);
|
||||
if (pos >= 0.0) position = pos;
|
||||
}
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if (Mix_PlayingMusic()) {
|
||||
Mix_FadeOutMusic(fadeDurationMs / 2);
|
||||
SDL_Delay(fadeDurationMs / 2 + 20);
|
||||
}
|
||||
#endif
|
||||
Mix_HaltMusic();
|
||||
if (currentMusic_) { Mix_FreeMusic(currentMusic_); currentMusic_ = nullptr; }
|
||||
|
||||
Mix_Music* music = Mix_LoadMUS(filePath.c_str());
|
||||
if (!music) {
|
||||
std::cerr << "Failed to load music " << filePath << ": " << Mix_GetError() << std::endl;
|
||||
return;
|
||||
}
|
||||
if (Mix_FadeInMusicPos(music, loops, fadeDurationMs / 2, position) == -1) {
|
||||
std::cerr << "Mix_FadeInMusicPos failed: " << Mix_GetError() << std::endl;
|
||||
Mix_FreeMusic(music);
|
||||
return;
|
||||
}
|
||||
currentMusic_ = music;
|
||||
};
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
task();
|
||||
#else
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push(std::move(task));
|
||||
cv.notify_one();
|
||||
#endif
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::setMusicEnabled(bool enabled) {
|
||||
if (musicEnabled_ == enabled) return;
|
||||
musicEnabled_ = enabled;
|
||||
if (!initialized) return;
|
||||
|
||||
if (!enabled) {
|
||||
// Halt playback but keep currentTrack so we can restart it on re-enable.
|
||||
#ifdef __EMSCRIPTEN__
|
||||
Mix_HaltMusic();
|
||||
#else
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
taskQueue.push([]() { Mix_HaltMusic(); });
|
||||
cv.notify_one();
|
||||
#endif
|
||||
} else {
|
||||
// Restart the last requested track, if any.
|
||||
if (!currentTrack.empty()) {
|
||||
const std::string track = currentTrack;
|
||||
currentTrack = ""; // clear so crossFadeMusicAsync doesn't bail early
|
||||
crossFadeMusicAsync(track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayerAsync::setSoundEnabled(bool enabled) {
|
||||
soundEnabled_ = enabled;
|
||||
}
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
void AudioPlayerAsync::workerThread() {
|
||||
while (true) {
|
||||
|
||||
@@ -24,11 +24,20 @@ public:
|
||||
|
||||
void playSoundAsync(const std::string& filePath, int loops = 0, int channel = -1);
|
||||
void playMusicAsync(const std::string& filePath, int loops = -1);
|
||||
void crossFadeMusicAsync(const std::string& filePath, int fadeDurationMs = 1000, int loops = -1);
|
||||
void crossFadeMusicFromPositionAsync(const std::string& filePath, int fadeDurationMs = 1000, int loops = -1);
|
||||
void stopMusicAsync();
|
||||
void pauseMusicAsync();
|
||||
void resumeMusicAsync();
|
||||
void setMusicVolume(int volume); // 0..128
|
||||
void setSoundVolume(int volume); // 0..128
|
||||
int getMusicVolume() const { return musicVolume_; }
|
||||
int getSoundVolume() const { return soundVolume_; }
|
||||
|
||||
void setMusicEnabled(bool enabled);
|
||||
void setSoundEnabled(bool enabled);
|
||||
bool isMusicEnabled() const { return musicEnabled_; }
|
||||
bool isSoundEnabled() const { return soundEnabled_; }
|
||||
|
||||
void exit() { stop = true; }
|
||||
|
||||
@@ -47,6 +56,12 @@ private:
|
||||
std::mutex soundCacheMutex;
|
||||
|
||||
std::string currentTrack;
|
||||
Mix_Music* currentMusic_ = nullptr;
|
||||
|
||||
int musicVolume_ = 128;
|
||||
int soundVolume_ = 128;
|
||||
bool musicEnabled_ = true;
|
||||
bool soundEnabled_ = true;
|
||||
|
||||
bool initialized = false;
|
||||
};
|
||||
+6
-4
@@ -327,7 +327,7 @@ namespace ZL
|
||||
menuManager.startGameFunc = [this]() {
|
||||
gameState.currentLocationName = "location_dorm";
|
||||
currentLocation()->scriptEngine.callLocationEnterCallback();
|
||||
this->audioPlayer->playMusicAsync("audio/obshaga.ogg");
|
||||
this->audioPlayer->crossFadeMusicAsync("audio/obshaga.ogg");
|
||||
};
|
||||
|
||||
menuManager.onSaveGame = [this](int slot) { saveGame(slot); };
|
||||
@@ -343,7 +343,9 @@ namespace ZL
|
||||
std::cout << "Audio initialization failed" << std::endl;
|
||||
}
|
||||
|
||||
audioPlayer->playMusicAsync("audio/main menu final.ogg");
|
||||
menuManager.loadSettings();
|
||||
|
||||
audioPlayer->crossFadeMusicAsync("audio/main menu final.ogg");
|
||||
|
||||
loadingCompleted = true;
|
||||
|
||||
@@ -453,10 +455,10 @@ namespace ZL
|
||||
}
|
||||
|
||||
gameState.locations["uni_interior"]->requestDarklandsPlayBattleMusic = [this]() {
|
||||
audioPlayer->playMusicAsync("audio/bishkek fight.ogg");
|
||||
audioPlayer->crossFadeMusicFromPositionAsync("audio/bishkek fight.ogg");
|
||||
};
|
||||
gameState.locations["uni_interior"]->requestDarklandsPlayNormalMusic = [this]() {
|
||||
audioPlayer->playMusicAsync("audio/bishkek fight calm.ogg");
|
||||
audioPlayer->crossFadeMusicFromPositionAsync("audio/bishkek fight calm.ogg");
|
||||
};
|
||||
LocationSetup uniExteriorParams = uniInteriorParams;
|
||||
uniExteriorParams.gameObjectsJsonPath = "resources/config2/gameobjects_uni_exterior_x.json";
|
||||
|
||||
+107
-9
@@ -1,7 +1,9 @@
|
||||
#include "MenuManager.h"
|
||||
#include "Game.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include "utils/Utils.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
@@ -252,7 +254,7 @@ namespace ZL {
|
||||
void MenuManager::showMainMenu() {
|
||||
if (onResetGame) onResetGame();
|
||||
|
||||
audioPlayer_.playMusicAsync("audio/main menu final.ogg");
|
||||
audioPlayer_.crossFadeMusicAsync("audio/main menu final.ogg");
|
||||
|
||||
uiState_ = GameUiState::MainMenu;
|
||||
uiManager.clearMenuStack();
|
||||
@@ -294,9 +296,72 @@ namespace ZL {
|
||||
void MenuManager::showSettingsScreen() {
|
||||
uiManager.pushMenuFromSavedRoot(settingsScreenRoot);
|
||||
uiManager.setTextButtonCallback("settingsBackButton", [this](const std::string&) {
|
||||
saveSettings();
|
||||
uiManager.popMenu();
|
||||
uiManager.updateAllLayouts();
|
||||
});
|
||||
|
||||
// Set initial slider positions before registering callbacks so
|
||||
// setSliderValue does not fire an unregistered callback.
|
||||
const float musicFrac = audioPlayer_.getMusicVolume() / 128.0f;
|
||||
const float soundFrac = audioPlayer_.getSoundVolume() / 128.0f;
|
||||
uiManager.setSliderValue("musicVolumeSlider", musicFrac);
|
||||
uiManager.setSliderValue("soundVolumeSlider", soundFrac);
|
||||
uiManager.setText("musicVolumeText",
|
||||
"Music volume: " + std::to_string(audioPlayer_.getMusicVolume()));
|
||||
uiManager.setText("soundVolumeText",
|
||||
"Sound volume: " + std::to_string(audioPlayer_.getSoundVolume()));
|
||||
|
||||
uiManager.setSliderCallback("musicVolumeSlider",
|
||||
[this](const std::string&, float value) {
|
||||
const int vol = static_cast<int>(value * 128.0f + 0.5f);
|
||||
audioPlayer_.setMusicVolume(vol);
|
||||
uiManager.setText("musicVolumeText",
|
||||
"Music volume: " + std::to_string(vol));
|
||||
});
|
||||
uiManager.setSliderCallback("soundVolumeSlider",
|
||||
[this](const std::string&, float value) {
|
||||
const int vol = static_cast<int>(value * 128.0f + 0.5f);
|
||||
audioPlayer_.setSoundVolume(vol);
|
||||
uiManager.setText("soundVolumeText",
|
||||
"Sound volume: " + std::to_string(vol));
|
||||
});
|
||||
|
||||
// Music on/off toggle — helper to sync button text and color from current state
|
||||
auto refreshMusicToggle = [this]() {
|
||||
const bool on = audioPlayer_.isMusicEnabled();
|
||||
uiManager.setTextButtonText("musicToggleButton", on ? "Music: ON" : "Music: OFF");
|
||||
uiManager.setTextButtonColor("musicToggleButton", on
|
||||
? std::array<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||
: std::array<float, 4>{ 0.9f, 0.2f, 0.2f, 1.0f });
|
||||
};
|
||||
auto refreshSoundToggle = [this]() {
|
||||
const bool on = audioPlayer_.isSoundEnabled();
|
||||
uiManager.setTextButtonText("soundToggleButton", on ? "Sound: ON" : "Sound: OFF");
|
||||
uiManager.setTextButtonColor("soundToggleButton", on
|
||||
? std::array<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||
: std::array<float, 4>{ 0.9f, 0.2f, 0.2f, 1.0f });
|
||||
};
|
||||
|
||||
refreshMusicToggle();
|
||||
refreshSoundToggle();
|
||||
|
||||
uiManager.setTextButtonCallback("musicToggleButton", [this](const std::string&) {
|
||||
audioPlayer_.setMusicEnabled(!audioPlayer_.isMusicEnabled());
|
||||
const bool on = audioPlayer_.isMusicEnabled();
|
||||
uiManager.setTextButtonText("musicToggleButton", on ? "Music: ON" : "Music: OFF");
|
||||
uiManager.setTextButtonColor("musicToggleButton", on
|
||||
? std::array<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||
: std::array<float, 4>{ 0.9f, 0.2f, 0.2f, 1.0f });
|
||||
});
|
||||
uiManager.setTextButtonCallback("soundToggleButton", [this](const std::string&) {
|
||||
audioPlayer_.setSoundEnabled(!audioPlayer_.isSoundEnabled());
|
||||
const bool on = audioPlayer_.isSoundEnabled();
|
||||
uiManager.setTextButtonText("soundToggleButton", on ? "Sound: ON" : "Sound: OFF");
|
||||
uiManager.setTextButtonColor("soundToggleButton", on
|
||||
? std::array<float, 4>{ 0.2f, 0.9f, 0.2f, 1.0f }
|
||||
: std::array<float, 4>{ 0.9f, 0.2f, 0.2f, 1.0f });
|
||||
});
|
||||
}
|
||||
|
||||
void MenuManager::showLoadGameScreen()
|
||||
@@ -906,29 +971,29 @@ namespace ZL {
|
||||
setupGameplayHudCallbacks();
|
||||
if (gameState_.isDarklands)
|
||||
{
|
||||
audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg");
|
||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek fight calm.ogg");
|
||||
}
|
||||
else if (gameState_.isNight)
|
||||
{
|
||||
audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg");
|
||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek night.ogg");
|
||||
}
|
||||
else
|
||||
{
|
||||
audioPlayer_.playMusicAsync("audio/bishkek univer day.ogg");
|
||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek univer day.ogg");
|
||||
}
|
||||
} else if (locationName == "uni_interior") {
|
||||
applyUniIntHud();
|
||||
if (gameState_.isDarklands)
|
||||
{
|
||||
audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg");
|
||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek fight calm.ogg");
|
||||
}
|
||||
else if (gameState_.isNight)
|
||||
{
|
||||
audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg");
|
||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek night.ogg");
|
||||
}
|
||||
else
|
||||
{
|
||||
audioPlayer_.playMusicAsync("audio/bishkek univer day.ogg");
|
||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek univer day.ogg");
|
||||
}
|
||||
} else {
|
||||
// Returning to dorm: reuse step5ab, suppress already-completed hints
|
||||
@@ -938,11 +1003,11 @@ namespace ZL {
|
||||
|
||||
if (gameState_.isNight)
|
||||
{
|
||||
audioPlayer_.playMusicAsync("audio/bishkek fight calm.ogg");
|
||||
audioPlayer_.crossFadeMusicAsync("audio/bishkek night.ogg");
|
||||
}
|
||||
else
|
||||
{
|
||||
audioPlayer_.playMusicAsync("audio/obshaga.ogg");
|
||||
audioPlayer_.crossFadeMusicAsync("audio/obshaga.ogg");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1452,4 +1517,37 @@ namespace ZL {
|
||||
updateToasts(deltaMs);
|
||||
}
|
||||
|
||||
void MenuManager::saveSettings() {
|
||||
nlohmann::json root;
|
||||
root["musicVolume"] = audioPlayer_.getMusicVolume();
|
||||
root["soundVolume"] = audioPlayer_.getSoundVolume();
|
||||
root["musicEnabled"] = audioPlayer_.isMusicEnabled();
|
||||
root["soundEnabled"] = audioPlayer_.isSoundEnabled();
|
||||
|
||||
std::ofstream file("settings.json");
|
||||
if (file.is_open()) {
|
||||
file << root.dump(2);
|
||||
} else {
|
||||
std::cerr << "[settings] Could not open settings.json for writing" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::loadSettings() {
|
||||
const std::string content = ZL::readTextFile("settings.json");
|
||||
if (content.empty()) return;
|
||||
try {
|
||||
const nlohmann::json root = nlohmann::json::parse(content);
|
||||
if (root.contains("musicVolume"))
|
||||
audioPlayer_.setMusicVolume(root["musicVolume"].get<int>());
|
||||
if (root.contains("soundVolume"))
|
||||
audioPlayer_.setSoundVolume(root["soundVolume"].get<int>());
|
||||
if (root.contains("musicEnabled"))
|
||||
audioPlayer_.setMusicEnabled(root["musicEnabled"].get<bool>());
|
||||
if (root.contains("soundEnabled"))
|
||||
audioPlayer_.setSoundEnabled(root["soundEnabled"].get<bool>());
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[settings] Failed to parse settings.json: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZL
|
||||
@@ -93,6 +93,9 @@ namespace ZL {
|
||||
void onCutsceneFinished();
|
||||
bool cutsceneHudActive_ = false;
|
||||
|
||||
void saveSettings();
|
||||
void loadSettings();
|
||||
|
||||
// Toast notification system
|
||||
void showToast(const std::string& iconPath, const std::string& text);
|
||||
void update(float deltaMs);
|
||||
|
||||
+17
-13
@@ -327,16 +327,16 @@ namespace ZL {
|
||||
knobMesh.data.PositionData.clear();
|
||||
knobMesh.data.TexCoordData.clear();
|
||||
|
||||
float kw = vertical ? rect.w * 4.0f : rect.w * 0.5f;
|
||||
float kh = vertical ? rect.w * 4.0f : rect.h * 0.5f;
|
||||
|
||||
float cx = rect.x + rect.w * 0.5f;
|
||||
float cy = rect.y + (vertical ? (value * rect.h) : (rect.h * 0.5f));
|
||||
|
||||
float x0 = cx - kw * 0.5f;
|
||||
float y0 = cy - kh * 0.5f;
|
||||
float x1 = cx + kw * 0.5f;
|
||||
float y1 = cy + kh * 0.5f;
|
||||
// Knob is built centered at the middle of the track rect.
|
||||
// draw() applies a per-frame translation so the knob tracks the current value
|
||||
// without needing to rebuild the VBO on every value change.
|
||||
const float kSize = vertical ? rect.w * 2.0f : rect.h;
|
||||
const float cx = rect.x + rect.w * 0.5f;
|
||||
const float cy = rect.y + rect.h * 0.5f;
|
||||
const float x0 = cx - kSize * 0.5f;
|
||||
const float y0 = cy - kSize * 0.5f;
|
||||
const float x1 = cx + kSize * 0.5f;
|
||||
const float y1 = cy + kSize * 0.5f;
|
||||
|
||||
knobMesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||
knobMesh.data.TexCoordData.push_back({ 0, 0 });
|
||||
@@ -366,8 +366,15 @@ namespace ZL {
|
||||
renderer.DrawVertexRenderStruct(trackMesh);
|
||||
}
|
||||
if (texKnob) {
|
||||
// The knob mesh is built centered at the middle of the track rect.
|
||||
// Translate it so its center lands on the value position along the track.
|
||||
const float offsetX = vertical ? 0.0f : (value - 0.5f) * rect.w;
|
||||
const float offsetY = vertical ? (value - 0.5f) * rect.h : 0.0f;
|
||||
renderer.PushMatrix();
|
||||
renderer.TranslateMatrix({ offsetX, offsetY, 0.0f });
|
||||
glBindTexture(GL_TEXTURE_2D, texKnob->getTexID());
|
||||
renderer.DrawVertexRenderStruct(knobMesh);
|
||||
renderer.PopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1301,7 +1308,6 @@ namespace ZL {
|
||||
if (fabs(s->value - value) < 1e-6f) return true;
|
||||
s->value = value;
|
||||
s->buildTrackMesh();
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
return true;
|
||||
}
|
||||
@@ -1796,7 +1802,6 @@ namespace ZL {
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
s->value = t;
|
||||
s->buildTrackMesh();
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
}
|
||||
}
|
||||
@@ -1843,7 +1848,6 @@ namespace ZL {
|
||||
if (t > 1.0f) t = 1.0f;
|
||||
s->value = t;
|
||||
s->buildTrackMesh();
|
||||
s->buildKnobMesh();
|
||||
if (s->onValueChanged) s->onValueChanged(s->name, s->value);
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user