Kelsidavis-WoWee/src/rendering/sky_system.cpp
Kelsi c623fcef51 Add property-based mount animation discovery and procedural lean
Mount Animation System:
- Property-based jump animation discovery using sequence metadata
- Chain linkage scoring (nextAnimation/aliasNext) for accurate detection
- Correct loop detection: flags & 0x01 == 0 means looping
- Avoids brake/stop animations via blendTime penalties
- Works on any mount model without hardcoded animation IDs

Mount Physics:
- Physics-based jump height: vz = sqrt(2 * g * h)
- Configurable MOUNT_JUMP_HEIGHT constant (1.0m default)
- Procedural lean into turns for ground mounts
- Smooth roll based on turn rate (±14° max, 6x/sec blend)

Audio Improvements:
- State-machine driven mount sounds (jump, land, rear-up)
- Semantic sound methods (no animation ID dependencies)
- Debug logging for missing sound files

Bug Fixes:
- Fixed mount animation sequencing (JumpStart → JumpLoop → JumpEnd)
- Fixed animation loop flag interpretation (0x20 vs 0x21)
- Rider bone attachment working correctly during all mount actions
2026-02-10 19:30:45 -08:00

189 lines
5 KiB
C++

#include "rendering/sky_system.hpp"
#include "rendering/skybox.hpp"
#include "rendering/celestial.hpp"
#include "rendering/starfield.hpp"
#include "rendering/clouds.hpp"
#include "rendering/lens_flare.hpp"
#include "rendering/camera.hpp"
#include "core/logger.hpp"
namespace wowee {
namespace rendering {
SkySystem::SkySystem() = default;
SkySystem::~SkySystem() {
shutdown();
}
bool SkySystem::initialize() {
if (initialized_) {
LOG_WARNING("SkySystem already initialized");
return true;
}
LOG_INFO("Initializing sky system");
// Initialize skybox (authoritative)
skybox_ = std::make_unique<Skybox>();
if (!skybox_->initialize()) {
LOG_ERROR("Failed to initialize skybox");
return false;
}
// Initialize celestial bodies (sun + 2 moons)
celestial_ = std::make_unique<Celestial>();
if (!celestial_->initialize()) {
LOG_ERROR("Failed to initialize celestial bodies");
return false;
}
// Initialize procedural stars (FALLBACK only)
starField_ = std::make_unique<StarField>();
if (!starField_->initialize()) {
LOG_ERROR("Failed to initialize star field");
return false;
}
// Default: disabled (skybox is authoritative)
starField_->setEnabled(false);
// Initialize clouds
clouds_ = std::make_unique<Clouds>();
if (!clouds_->initialize()) {
LOG_ERROR("Failed to initialize clouds");
return false;
}
// Initialize lens flare
lensFlare_ = std::make_unique<LensFlare>();
if (!lensFlare_->initialize()) {
LOG_ERROR("Failed to initialize lens flare");
return false;
}
initialized_ = true;
LOG_INFO("Sky system initialized successfully");
return true;
}
void SkySystem::shutdown() {
if (!initialized_) {
return;
}
LOG_INFO("Shutting down sky system");
// Shutdown components that have explicit shutdown methods
if (starField_) starField_->shutdown();
if (celestial_) celestial_->shutdown();
if (skybox_) skybox_->shutdown();
// Reset all (destructors handle cleanup for clouds/lensFlare)
lensFlare_.reset();
clouds_.reset();
starField_.reset();
celestial_.reset();
skybox_.reset();
initialized_ = false;
}
void SkySystem::update(float deltaTime) {
if (!initialized_) {
return;
}
// Update time-based systems
if (skybox_) skybox_->update(deltaTime);
if (celestial_) celestial_->update(deltaTime);
if (starField_) starField_->update(deltaTime);
}
void SkySystem::render(const Camera& camera, const SkyParams& params) {
if (!initialized_) {
return;
}
// Render skybox first (authoritative, includes baked stars)
if (skybox_) {
skybox_->render(camera, params.timeOfDay);
}
// Decide whether to render procedural stars
bool renderProceduralStars = false;
if (debugSkyMode_) {
// Debug mode: always show procedural stars
renderProceduralStars = true;
} else if (proceduralStarsEnabled_) {
// Fallback mode: show only if skybox doesn't have stars
renderProceduralStars = !params.skyboxHasStars;
}
// Render procedural stars (FALLBACK or DEBUG only)
if (renderProceduralStars && starField_) {
starField_->setEnabled(true);
starField_->render(camera, params.timeOfDay, params.cloudDensity, params.fogDensity);
} else if (starField_) {
starField_->setEnabled(false);
}
// Render celestial bodies (sun + White Lady + Blue Child)
// Pass gameTime for deterministic moon phases
if (celestial_) {
celestial_->render(camera, params.timeOfDay, &params.directionalDir, &params.sunColor, params.gameTime);
}
// Render clouds
if (clouds_) {
clouds_->render(camera, params.timeOfDay);
}
// Render lens flare (sun glow effect)
if (lensFlare_) {
glm::vec3 sunPos = getSunPosition(params);
lensFlare_->render(camera, sunPos, params.timeOfDay);
}
}
glm::vec3 SkySystem::getSunPosition(const SkyParams& params) const {
// TESTING: X-up test
glm::vec3 dir = glm::vec3(1.0f, 0.0f, 0.0f); // X-up
glm::vec3 pos = dir * 800.0f;
static int counter = 0;
if (counter++ % 100 == 0) {
LOG_INFO("Flare TEST X-UP dir=(", dir.x, ",", dir.y, ",", dir.z, ") pos=(", pos.x, ",", pos.y, ",", pos.z, ")");
}
return pos;
}
void SkySystem::setMoonPhaseCycling(bool enabled) {
if (celestial_) {
celestial_->setMoonPhaseCycling(enabled);
}
}
void SkySystem::setWhiteLadyPhase(float phase) {
if (celestial_) {
celestial_->setMoonPhase(phase); // White Lady is primary moon
}
}
void SkySystem::setBlueChildPhase(float phase) {
if (celestial_) {
celestial_->setBlueChildPhase(phase);
}
}
float SkySystem::getWhiteLadyPhase() const {
return celestial_ ? celestial_->getMoonPhase() : 0.5f;
}
float SkySystem::getBlueChildPhase() const {
// TODO: Second moon support
return 0.25f; // Placeholder phase
}
} // namespace rendering
} // namespace wowee