Implement WoW-accurate DBC-driven sky system with lore-faithful celestial bodies
Add SkySystem coordinator that follows WoW's actual architecture where skyboxes
are authoritative and procedural elements serve as fallbacks. Integrate lighting
system across all renderers (terrain, WMO, M2, character) with unified parameters.
Sky System:
- SkySystem coordinator manages skybox, celestial bodies, stars, clouds, lens flare
- Skybox is authoritative (baked stars from M2 models, procedural fallback only)
- skyboxHasStars flag gates procedural star rendering (prevents double-star bug)
Celestial Bodies (Lore-Accurate):
- Two moons: White Lady (30-day cycle, pale white) + Blue Child (27-day cycle, pale blue)
- Deterministic moon phases from server gameTime (not deltaTime toys)
- Sun positioning driven by LightingManager directionalDir (DBC-sourced)
- Camera-locked sky dome (translation ignored, rotation applied)
Lighting Integration:
- Apply LightingManager params to WMO, M2, character renderers
- Unified lighting: directional light, diffuse color, ambient color, fog
- Star occlusion by cloud density (70% weight) and fog density (30% weight)
Documentation:
- Add comprehensive SKY_SYSTEM.md technical guide
- Update MEMORY.md with sky system architecture and anti-patterns
- Update README.md with WoW-accurate descriptions
Critical design decisions:
- NO latitude-based star rotation (Azeroth not modeled as spherical planet)
- NO always-on procedural stars (skybox authority prevents zone identity loss)
- NO universal dual-moon setup (map-specific celestial configurations)
2026-02-10 14:36:17 -08:00
|
|
|
#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, ¶ms.directionalDir, ¶ms.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 {
|
2026-02-11 19:28:15 -08:00
|
|
|
glm::vec3 dir = glm::normalize(params.directionalDir);
|
|
|
|
|
if (glm::length(dir) < 0.0001f) {
|
|
|
|
|
dir = glm::vec3(0.0f, 0.0f, 1.0f);
|
2026-02-10 19:30:45 -08:00
|
|
|
}
|
2026-02-11 19:28:15 -08:00
|
|
|
glm::vec3 pos = dir * 800.0f;
|
2026-02-10 19:30:45 -08:00
|
|
|
return pos;
|
Implement WoW-accurate DBC-driven sky system with lore-faithful celestial bodies
Add SkySystem coordinator that follows WoW's actual architecture where skyboxes
are authoritative and procedural elements serve as fallbacks. Integrate lighting
system across all renderers (terrain, WMO, M2, character) with unified parameters.
Sky System:
- SkySystem coordinator manages skybox, celestial bodies, stars, clouds, lens flare
- Skybox is authoritative (baked stars from M2 models, procedural fallback only)
- skyboxHasStars flag gates procedural star rendering (prevents double-star bug)
Celestial Bodies (Lore-Accurate):
- Two moons: White Lady (30-day cycle, pale white) + Blue Child (27-day cycle, pale blue)
- Deterministic moon phases from server gameTime (not deltaTime toys)
- Sun positioning driven by LightingManager directionalDir (DBC-sourced)
- Camera-locked sky dome (translation ignored, rotation applied)
Lighting Integration:
- Apply LightingManager params to WMO, M2, character renderers
- Unified lighting: directional light, diffuse color, ambient color, fog
- Star occlusion by cloud density (70% weight) and fog density (30% weight)
Documentation:
- Add comprehensive SKY_SYSTEM.md technical guide
- Update MEMORY.md with sky system architecture and anti-patterns
- Update README.md with WoW-accurate descriptions
Critical design decisions:
- NO latitude-based star rotation (Azeroth not modeled as spherical planet)
- NO always-on procedural stars (skybox authority prevents zone identity loss)
- NO universal dual-moon setup (map-specific celestial configurations)
2026-02-10 14:36:17 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|