2026-02-02 12:24:50 -08:00
|
|
|
#include "rendering/celestial.hpp"
|
|
|
|
|
#include "rendering/shader.hpp"
|
|
|
|
|
#include "rendering/camera.hpp"
|
|
|
|
|
#include "core/logger.hpp"
|
|
|
|
|
#include <GL/glew.h>
|
|
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
namespace wowee {
|
|
|
|
|
namespace rendering {
|
|
|
|
|
|
|
|
|
|
Celestial::Celestial() = default;
|
|
|
|
|
|
|
|
|
|
Celestial::~Celestial() {
|
|
|
|
|
shutdown();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Celestial::initialize() {
|
|
|
|
|
LOG_INFO("Initializing celestial renderer");
|
|
|
|
|
|
|
|
|
|
// Create celestial shader
|
|
|
|
|
celestialShader = std::make_unique<Shader>();
|
|
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
// Vertex shader - billboard facing camera (sky dome locked)
|
2026-02-02 12:24:50 -08:00
|
|
|
const char* vertexShaderSource = R"(
|
|
|
|
|
#version 330 core
|
|
|
|
|
layout (location = 0) in vec3 aPos;
|
|
|
|
|
layout (location = 1) in vec2 aTexCoord;
|
|
|
|
|
|
|
|
|
|
uniform mat4 model;
|
|
|
|
|
uniform mat4 view;
|
|
|
|
|
uniform mat4 projection;
|
|
|
|
|
|
|
|
|
|
out vec2 TexCoord;
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
TexCoord = aTexCoord;
|
|
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
// Sky object: remove translation, keep rotation (skybox technique)
|
|
|
|
|
mat4 viewNoTranslation = mat4(mat3(view));
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
gl_Position = projection * viewNoTranslation * model * vec4(aPos, 1.0);
|
2026-02-02 12:24:50 -08:00
|
|
|
}
|
|
|
|
|
)";
|
|
|
|
|
|
|
|
|
|
// Fragment shader - disc with glow and moon phase support
|
|
|
|
|
const char* fragmentShaderSource = R"(
|
|
|
|
|
#version 330 core
|
|
|
|
|
in vec2 TexCoord;
|
|
|
|
|
|
|
|
|
|
uniform vec3 celestialColor;
|
|
|
|
|
uniform float intensity;
|
|
|
|
|
uniform float moonPhase; // 0.0 = new moon, 0.5 = full moon, 1.0 = new moon
|
|
|
|
|
|
|
|
|
|
out vec4 FragColor;
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
// Create circular disc
|
|
|
|
|
vec2 center = vec2(0.5, 0.5);
|
|
|
|
|
float dist = distance(TexCoord, center);
|
|
|
|
|
|
|
|
|
|
// Core disc
|
|
|
|
|
float disc = smoothstep(0.5, 0.4, dist);
|
|
|
|
|
|
|
|
|
|
// Glow around disc
|
|
|
|
|
float glow = smoothstep(0.7, 0.0, dist) * 0.3;
|
|
|
|
|
|
|
|
|
|
float alpha = (disc + glow) * intensity;
|
|
|
|
|
|
|
|
|
|
// Apply moon phase shadow (only for moon, indicated by low intensity)
|
|
|
|
|
if (intensity < 0.5) { // Moon has lower intensity than sun
|
|
|
|
|
// Calculate phase position (-1 to 1, where 0 is center)
|
|
|
|
|
float phasePos = (moonPhase - 0.5) * 2.0;
|
|
|
|
|
|
|
|
|
|
// Distance from phase terminator line
|
|
|
|
|
float x = (TexCoord.x - 0.5) * 2.0; // -1 to 1
|
|
|
|
|
|
|
|
|
|
// Create shadow using smoothstep
|
|
|
|
|
float shadow = 1.0;
|
|
|
|
|
|
|
|
|
|
if (moonPhase < 0.5) {
|
|
|
|
|
// Waning (right to left shadow)
|
|
|
|
|
shadow = smoothstep(phasePos - 0.1, phasePos + 0.1, x);
|
|
|
|
|
} else {
|
|
|
|
|
// Waxing (left to right shadow)
|
|
|
|
|
shadow = smoothstep(phasePos - 0.1, phasePos + 0.1, -x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply elliptical terminator for 3D effect
|
|
|
|
|
float y = (TexCoord.y - 0.5) * 2.0;
|
|
|
|
|
float ellipse = sqrt(1.0 - y * y);
|
|
|
|
|
float terminatorX = phasePos / ellipse;
|
|
|
|
|
|
|
|
|
|
if (moonPhase < 0.5) {
|
|
|
|
|
shadow = smoothstep(terminatorX - 0.15, terminatorX + 0.15, x);
|
|
|
|
|
} else {
|
|
|
|
|
shadow = smoothstep(terminatorX - 0.15, terminatorX + 0.15, -x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Darken shadowed area (not completely black, slight glow remains)
|
|
|
|
|
alpha *= mix(0.05, 1.0, shadow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FragColor = vec4(celestialColor, alpha);
|
|
|
|
|
}
|
|
|
|
|
)";
|
|
|
|
|
|
|
|
|
|
if (!celestialShader->loadFromSource(vertexShaderSource, fragmentShaderSource)) {
|
|
|
|
|
LOG_ERROR("Failed to create celestial shader");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create billboard quad
|
|
|
|
|
createCelestialQuad();
|
|
|
|
|
|
|
|
|
|
LOG_INFO("Celestial renderer initialized");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Celestial::shutdown() {
|
|
|
|
|
destroyCelestialQuad();
|
|
|
|
|
celestialShader.reset();
|
|
|
|
|
}
|
|
|
|
|
|
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 Celestial::render(const Camera& camera, float timeOfDay,
|
|
|
|
|
const glm::vec3* sunDir, const glm::vec3* sunColor, float gameTime) {
|
2026-02-02 12:24:50 -08:00
|
|
|
if (!renderingEnabled || vao == 0 || !celestialShader) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
// Update moon phases from game time if available (deterministic)
|
|
|
|
|
if (gameTime >= 0.0f) {
|
|
|
|
|
updatePhasesFromGameTime(gameTime);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
// Enable additive blending for celestial glow (brighter against sky)
|
2026-02-02 12:24:50 -08:00
|
|
|
glEnable(GL_BLEND);
|
2026-02-10 19:30:45 -08:00
|
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Additive blending for brightness
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
// Disable depth testing entirely - celestial bodies render "on" the sky
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
2026-02-02 12:24:50 -08:00
|
|
|
glDepthMask(GL_FALSE);
|
|
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
// Disable culling - billboards can face either way
|
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
|
|
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
|
|
|
// Render sun and moons (pass lighting parameters)
|
|
|
|
|
renderSun(camera, timeOfDay, sunDir, sunColor);
|
|
|
|
|
renderMoon(camera, timeOfDay); // White Lady (primary moon)
|
|
|
|
|
|
|
|
|
|
if (dualMoonMode_) {
|
|
|
|
|
renderBlueChild(camera, timeOfDay); // Blue Child (secondary moon)
|
|
|
|
|
}
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Restore state
|
2026-02-10 19:30:45 -08:00
|
|
|
glEnable(GL_DEPTH_TEST);
|
2026-02-02 12:24:50 -08:00
|
|
|
glDepthMask(GL_TRUE);
|
|
|
|
|
glDisable(GL_BLEND);
|
2026-02-10 19:30:45 -08:00
|
|
|
glEnable(GL_CULL_FACE);
|
2026-02-02 12:24:50 -08:00
|
|
|
}
|
|
|
|
|
|
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 Celestial::renderSun(const Camera& camera, float timeOfDay,
|
|
|
|
|
const glm::vec3* sunDir, const glm::vec3* sunColor) {
|
2026-02-02 12:24:50 -08:00
|
|
|
// Sun visible from 5:00 to 19:00
|
|
|
|
|
if (timeOfDay < 5.0f || timeOfDay >= 19.0f) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
celestialShader->use();
|
|
|
|
|
|
2026-02-11 19:28:15 -08:00
|
|
|
glm::vec3 dir = sunDir ? glm::normalize(*sunDir) : glm::vec3(0.0f, 0.0f, 1.0f);
|
2026-02-10 19:30:45 -08:00
|
|
|
|
|
|
|
|
// Place sun on sky sphere at fixed distance
|
|
|
|
|
const float sunDistance = 800.0f;
|
|
|
|
|
glm::vec3 sunPos = dir * sunDistance;
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Create model matrix
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
|
model = glm::translate(model, sunPos);
|
2026-02-10 19:30:45 -08:00
|
|
|
model = glm::scale(model, glm::vec3(500.0f, 500.0f, 1.0f)); // Large and visible
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Set uniforms
|
|
|
|
|
glm::mat4 view = camera.getViewMatrix();
|
|
|
|
|
glm::mat4 projection = camera.getProjectionMatrix();
|
|
|
|
|
|
|
|
|
|
celestialShader->setUniform("model", model);
|
|
|
|
|
celestialShader->setUniform("view", view);
|
|
|
|
|
celestialShader->setUniform("projection", projection);
|
|
|
|
|
|
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
|
|
|
// Sun color and intensity (use lighting color if provided)
|
|
|
|
|
glm::vec3 color = sunColor ? *sunColor : getSunColor(timeOfDay);
|
2026-02-02 12:24:50 -08:00
|
|
|
float intensity = getSunIntensity(timeOfDay);
|
|
|
|
|
|
|
|
|
|
celestialShader->setUniform("celestialColor", color);
|
|
|
|
|
celestialShader->setUniform("intensity", intensity);
|
|
|
|
|
celestialShader->setUniform("moonPhase", 0.5f); // Sun doesn't use this, but shader expects it
|
|
|
|
|
|
|
|
|
|
// Render quad
|
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Celestial::renderMoon(const Camera& camera, float timeOfDay) {
|
|
|
|
|
// Moon visible from 19:00 to 5:00 (night)
|
|
|
|
|
if (timeOfDay >= 5.0f && timeOfDay < 19.0f) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
celestialShader->use();
|
|
|
|
|
|
|
|
|
|
// Get moon position
|
|
|
|
|
glm::vec3 moonPos = getMoonPosition(timeOfDay);
|
|
|
|
|
|
|
|
|
|
// Create model matrix
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
|
model = glm::translate(model, moonPos);
|
|
|
|
|
model = glm::scale(model, glm::vec3(40.0f, 40.0f, 1.0f)); // 40 unit diameter (smaller than sun)
|
|
|
|
|
|
|
|
|
|
// Set uniforms
|
|
|
|
|
glm::mat4 view = camera.getViewMatrix();
|
|
|
|
|
glm::mat4 projection = camera.getProjectionMatrix();
|
|
|
|
|
|
|
|
|
|
celestialShader->setUniform("model", model);
|
|
|
|
|
celestialShader->setUniform("view", view);
|
|
|
|
|
celestialShader->setUniform("projection", projection);
|
|
|
|
|
|
|
|
|
|
// Moon color (pale blue-white) and intensity
|
|
|
|
|
glm::vec3 color = glm::vec3(0.8f, 0.85f, 1.0f);
|
|
|
|
|
|
|
|
|
|
// Fade in/out at transitions
|
|
|
|
|
float intensity = 1.0f;
|
|
|
|
|
if (timeOfDay >= 19.0f && timeOfDay < 21.0f) {
|
|
|
|
|
// Fade in (19:00-21:00)
|
|
|
|
|
intensity = (timeOfDay - 19.0f) / 2.0f;
|
|
|
|
|
}
|
|
|
|
|
else if (timeOfDay >= 3.0f && timeOfDay < 5.0f) {
|
|
|
|
|
// Fade out (3:00-5:00)
|
|
|
|
|
intensity = 1.0f - (timeOfDay - 3.0f) / 2.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
celestialShader->setUniform("celestialColor", color);
|
|
|
|
|
celestialShader->setUniform("intensity", intensity);
|
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
|
|
|
celestialShader->setUniform("moonPhase", whiteLadyPhase_);
|
|
|
|
|
|
|
|
|
|
// Render quad
|
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Celestial::renderBlueChild(const Camera& camera, float timeOfDay) {
|
|
|
|
|
// Blue Child visible from 19:00 to 5:00 (night, same as White Lady)
|
|
|
|
|
if (timeOfDay >= 5.0f && timeOfDay < 19.0f) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
celestialShader->use();
|
|
|
|
|
|
|
|
|
|
// Get moon position (offset slightly from White Lady)
|
|
|
|
|
glm::vec3 moonPos = getMoonPosition(timeOfDay);
|
|
|
|
|
// Offset Blue Child to the right and slightly lower
|
|
|
|
|
moonPos.x += 80.0f; // Right offset
|
|
|
|
|
moonPos.z -= 40.0f; // Slightly lower
|
|
|
|
|
|
|
|
|
|
// Create model matrix (smaller than White Lady)
|
|
|
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
|
model = glm::translate(model, moonPos);
|
|
|
|
|
model = glm::scale(model, glm::vec3(30.0f, 30.0f, 1.0f)); // 30 unit diameter (smaller)
|
|
|
|
|
|
|
|
|
|
// Set uniforms
|
|
|
|
|
glm::mat4 view = camera.getViewMatrix();
|
|
|
|
|
glm::mat4 projection = camera.getProjectionMatrix();
|
|
|
|
|
|
|
|
|
|
celestialShader->setUniform("model", model);
|
|
|
|
|
celestialShader->setUniform("view", view);
|
|
|
|
|
celestialShader->setUniform("projection", projection);
|
|
|
|
|
|
|
|
|
|
// Blue Child color (pale blue tint)
|
|
|
|
|
glm::vec3 color = glm::vec3(0.7f, 0.8f, 1.0f);
|
|
|
|
|
|
|
|
|
|
// Fade in/out at transitions (same as White Lady)
|
|
|
|
|
float intensity = 1.0f;
|
|
|
|
|
if (timeOfDay >= 19.0f && timeOfDay < 21.0f) {
|
|
|
|
|
// Fade in (19:00-21:00)
|
|
|
|
|
intensity = (timeOfDay - 19.0f) / 2.0f;
|
|
|
|
|
}
|
|
|
|
|
else if (timeOfDay >= 3.0f && timeOfDay < 5.0f) {
|
|
|
|
|
// Fade out (3:00-5:00)
|
|
|
|
|
intensity = 1.0f - (timeOfDay - 3.0f) / 2.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Blue Child is dimmer than White Lady
|
|
|
|
|
intensity *= 0.7f;
|
|
|
|
|
|
|
|
|
|
celestialShader->setUniform("celestialColor", color);
|
|
|
|
|
celestialShader->setUniform("intensity", intensity);
|
|
|
|
|
celestialShader->setUniform("moonPhase", blueChildPhase_);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
// Render quad
|
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glm::vec3 Celestial::getSunPosition(float timeOfDay) const {
|
|
|
|
|
// Sun rises at 6:00, peaks at 12:00, sets at 18:00
|
|
|
|
|
float angle = calculateCelestialAngle(timeOfDay, 6.0f, 18.0f);
|
|
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
const float radius = 800.0f; // Horizontal distance
|
|
|
|
|
const float height = 600.0f; // Maximum height at zenith
|
2026-02-02 12:24:50 -08:00
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
// Arc across sky (angle 0→π maps to sunrise→noon→sunset)
|
|
|
|
|
// Z is vertical (matches skybox: Altitude = aPos.z)
|
|
|
|
|
// At angle=0: x=radius, z=0 (east horizon)
|
|
|
|
|
// At angle=π/2: x=0, z=height (zenith, directly overhead)
|
|
|
|
|
// At angle=π: x=-radius, z=0 (west horizon)
|
|
|
|
|
float x = radius * std::cos(angle); // Horizontal position (E→W)
|
|
|
|
|
float y = 0.0f; // Y is north-south (keep at 0)
|
|
|
|
|
float z = height * std::sin(angle); // Vertical position (Z is UP, matches skybox)
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
return glm::vec3(x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glm::vec3 Celestial::getMoonPosition(float timeOfDay) const {
|
|
|
|
|
// Moon rises at 18:00, peaks at 0:00 (24:00), sets at 6:00
|
|
|
|
|
// Adjust time for moon (opposite to sun)
|
|
|
|
|
float moonTime = timeOfDay + 12.0f;
|
|
|
|
|
if (moonTime >= 24.0f) moonTime -= 24.0f;
|
|
|
|
|
|
|
|
|
|
float angle = calculateCelestialAngle(moonTime, 6.0f, 18.0f);
|
|
|
|
|
|
|
|
|
|
const float radius = 800.0f;
|
|
|
|
|
const float height = 600.0f;
|
|
|
|
|
|
2026-02-10 19:30:45 -08:00
|
|
|
// Same arc formula as sun (Z is vertical, matches skybox)
|
|
|
|
|
float x = radius * std::cos(angle);
|
2026-02-02 12:24:50 -08:00
|
|
|
float y = 0.0f;
|
2026-02-10 19:30:45 -08:00
|
|
|
float z = height * std::sin(angle);
|
2026-02-02 12:24:50 -08:00
|
|
|
|
|
|
|
|
return glm::vec3(x, y, z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glm::vec3 Celestial::getSunColor(float timeOfDay) const {
|
|
|
|
|
// Sunrise/sunset: orange/red
|
|
|
|
|
// Midday: bright yellow-white
|
|
|
|
|
|
|
|
|
|
if (timeOfDay >= 5.0f && timeOfDay < 7.0f) {
|
|
|
|
|
// Sunrise: orange
|
|
|
|
|
return glm::vec3(1.0f, 0.6f, 0.2f);
|
|
|
|
|
}
|
|
|
|
|
else if (timeOfDay >= 7.0f && timeOfDay < 9.0f) {
|
|
|
|
|
// Morning: blend to yellow
|
|
|
|
|
float t = (timeOfDay - 7.0f) / 2.0f;
|
|
|
|
|
glm::vec3 orange = glm::vec3(1.0f, 0.6f, 0.2f);
|
|
|
|
|
glm::vec3 yellow = glm::vec3(1.0f, 1.0f, 0.9f);
|
|
|
|
|
return glm::mix(orange, yellow, t);
|
|
|
|
|
}
|
|
|
|
|
else if (timeOfDay >= 9.0f && timeOfDay < 16.0f) {
|
|
|
|
|
// Day: bright yellow-white
|
|
|
|
|
return glm::vec3(1.0f, 1.0f, 0.9f);
|
|
|
|
|
}
|
|
|
|
|
else if (timeOfDay >= 16.0f && timeOfDay < 18.0f) {
|
|
|
|
|
// Evening: blend to orange
|
|
|
|
|
float t = (timeOfDay - 16.0f) / 2.0f;
|
|
|
|
|
glm::vec3 yellow = glm::vec3(1.0f, 1.0f, 0.9f);
|
|
|
|
|
glm::vec3 orange = glm::vec3(1.0f, 0.5f, 0.1f);
|
|
|
|
|
return glm::mix(yellow, orange, t);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Sunset: deep orange/red
|
|
|
|
|
return glm::vec3(1.0f, 0.4f, 0.1f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float Celestial::getSunIntensity(float timeOfDay) const {
|
|
|
|
|
// Fade in at sunrise (5:00-6:00)
|
|
|
|
|
if (timeOfDay >= 5.0f && timeOfDay < 6.0f) {
|
|
|
|
|
return (timeOfDay - 5.0f); // 0 to 1
|
|
|
|
|
}
|
|
|
|
|
// Full intensity during day (6:00-18:00)
|
|
|
|
|
else if (timeOfDay >= 6.0f && timeOfDay < 18.0f) {
|
|
|
|
|
return 1.0f;
|
|
|
|
|
}
|
|
|
|
|
// Fade out at sunset (18:00-19:00)
|
|
|
|
|
else if (timeOfDay >= 18.0f && timeOfDay < 19.0f) {
|
|
|
|
|
return 1.0f - (timeOfDay - 18.0f); // 1 to 0
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return 0.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float Celestial::calculateCelestialAngle(float timeOfDay, float riseTime, float setTime) const {
|
|
|
|
|
// Map time to angle (0 to PI)
|
|
|
|
|
// riseTime: 0 radians (horizon east)
|
|
|
|
|
// (riseTime + setTime) / 2: PI/2 radians (zenith)
|
|
|
|
|
// setTime: PI radians (horizon west)
|
|
|
|
|
|
|
|
|
|
float duration = setTime - riseTime;
|
|
|
|
|
float elapsed = timeOfDay - riseTime;
|
|
|
|
|
|
|
|
|
|
// Normalize to 0-1
|
|
|
|
|
float t = elapsed / duration;
|
|
|
|
|
|
|
|
|
|
// Map to 0 to PI (arc from east to west)
|
|
|
|
|
return t * M_PI;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Celestial::createCelestialQuad() {
|
|
|
|
|
// Simple quad centered at origin
|
|
|
|
|
float vertices[] = {
|
|
|
|
|
// Position // TexCoord
|
|
|
|
|
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // Top-left
|
|
|
|
|
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // Top-right
|
|
|
|
|
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // Bottom-right
|
|
|
|
|
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f // Bottom-left
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
uint32_t indices[] = {
|
|
|
|
|
0, 1, 2, // First triangle
|
|
|
|
|
0, 2, 3 // Second triangle
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Create OpenGL buffers
|
|
|
|
|
glGenVertexArrays(1, &vao);
|
|
|
|
|
glGenBuffers(1, &vbo);
|
|
|
|
|
glGenBuffers(1, &ebo);
|
|
|
|
|
|
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
|
|
|
|
|
|
// Upload vertex data
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
|
|
// Upload index data
|
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
|
|
// Set vertex attributes
|
|
|
|
|
// Position
|
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
|
|
|
|
|
glEnableVertexAttribArray(0);
|
|
|
|
|
|
|
|
|
|
// Texture coordinates
|
|
|
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
|
|
|
|
|
glEnableVertexAttribArray(1);
|
|
|
|
|
|
|
|
|
|
glBindVertexArray(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Celestial::destroyCelestialQuad() {
|
|
|
|
|
if (vao != 0) {
|
|
|
|
|
glDeleteVertexArrays(1, &vao);
|
|
|
|
|
vao = 0;
|
|
|
|
|
}
|
|
|
|
|
if (vbo != 0) {
|
|
|
|
|
glDeleteBuffers(1, &vbo);
|
|
|
|
|
vbo = 0;
|
|
|
|
|
}
|
|
|
|
|
if (ebo != 0) {
|
|
|
|
|
glDeleteBuffers(1, &ebo);
|
|
|
|
|
ebo = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Celestial::update(float deltaTime) {
|
|
|
|
|
if (!moonPhaseCycling) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update moon phase timer
|
|
|
|
|
moonPhaseTimer += deltaTime;
|
|
|
|
|
|
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
|
|
|
// White Lady completes full cycle in MOON_CYCLE_DURATION seconds
|
|
|
|
|
whiteLadyPhase_ = std::fmod(moonPhaseTimer / MOON_CYCLE_DURATION, 1.0f);
|
|
|
|
|
|
|
|
|
|
// Blue Child has a different cycle rate (slightly faster, 3.5 minutes)
|
|
|
|
|
constexpr float BLUE_CHILD_CYCLE = 210.0f;
|
|
|
|
|
blueChildPhase_ = std::fmod(moonPhaseTimer / BLUE_CHILD_CYCLE, 1.0f);
|
2026-02-02 12:24:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Celestial::setMoonPhase(float phase) {
|
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
|
|
|
// Set White Lady phase (primary moon)
|
|
|
|
|
whiteLadyPhase_ = glm::clamp(phase, 0.0f, 1.0f);
|
|
|
|
|
|
|
|
|
|
// Update timer to match White Lady phase
|
|
|
|
|
moonPhaseTimer = whiteLadyPhase_ * MOON_CYCLE_DURATION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Celestial::setBlueChildPhase(float phase) {
|
|
|
|
|
// Set Blue Child phase (secondary moon)
|
|
|
|
|
blueChildPhase_ = glm::clamp(phase, 0.0f, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float Celestial::computePhaseFromGameTime(float gameTime, float cycleDays) const {
|
|
|
|
|
// WoW game time: 1 game day = 24 real minutes = 1440 seconds
|
|
|
|
|
constexpr float SECONDS_PER_GAME_DAY = 1440.0f;
|
|
|
|
|
|
|
|
|
|
// Convert game time to game days
|
|
|
|
|
float gameDays = gameTime / SECONDS_PER_GAME_DAY;
|
|
|
|
|
|
|
|
|
|
// Compute phase as fraction of lunar cycle (0.0-1.0)
|
|
|
|
|
float phase = std::fmod(gameDays / cycleDays, 1.0f);
|
|
|
|
|
|
|
|
|
|
// Ensure positive (fmod can return negative for negative input)
|
|
|
|
|
if (phase < 0.0f) {
|
|
|
|
|
phase += 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return phase;
|
|
|
|
|
}
|
2026-02-02 12:24:50 -08:00
|
|
|
|
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 Celestial::updatePhasesFromGameTime(float gameTime) {
|
|
|
|
|
// Compute deterministic phases from server game time
|
|
|
|
|
whiteLadyPhase_ = computePhaseFromGameTime(gameTime, WHITE_LADY_CYCLE_DAYS);
|
|
|
|
|
blueChildPhase_ = computePhaseFromGameTime(gameTime, BLUE_CHILD_CYCLE_DAYS);
|
2026-02-02 12:24:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace rendering
|
|
|
|
|
} // namespace wowee
|