mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-25 00:20:16 +00:00
Enhanced sky atmosphere with DBC-driven colors, sun lighting, and zone weather
- Skybox now uses DBC sky colors (skyTop/skyMiddle/skyBand1/skyBand2) instead of hardcoded C++ color curves, with 3-band gradient and Rayleigh/Mie scattering - Clouds receive sun direction for lit edges, self-shadowing, and silver lining - Fixed sun quad box artifact with proper edge fade in celestial shader - Lens flare attenuated by fog, cloud density, and weather intensity - Replaced garish green/purple lens flare ghosts with warm natural palette - Added zone-based weather system for single-player mode with per-zone rain/snow configuration, probability-based activation, and smooth intensity transitions - Server SMSG_WEATHER remains authoritative when connected to a server
This commit is contained in:
parent
085fd09b9d
commit
6563eebb60
18 changed files with 434 additions and 252 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#include "rendering/clouds.hpp"
|
||||
#include "rendering/sky_system.hpp"
|
||||
#include "rendering/vk_context.hpp"
|
||||
#include "rendering/vk_shader.hpp"
|
||||
#include "rendering/vk_pipeline.hpp"
|
||||
|
|
@ -40,11 +41,11 @@ bool Clouds::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout) {
|
|||
VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
||||
// ------------------------------------------------------------------ push constants
|
||||
// Fragment-only push: vec4 cloudColor + float density + float windOffset = 24 bytes
|
||||
// Fragment-only push: 3 x vec4 = 48 bytes
|
||||
VkPushConstantRange pushRange{};
|
||||
pushRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
pushRange.offset = 0;
|
||||
pushRange.size = sizeof(CloudPush); // 24 bytes
|
||||
pushRange.size = sizeof(CloudPush); // 48 bytes
|
||||
|
||||
// ------------------------------------------------------------------ pipeline layout
|
||||
pipelineLayout_ = createPipelineLayout(device, {perFrameLayout}, {pushRange});
|
||||
|
|
@ -54,10 +55,9 @@ bool Clouds::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout) {
|
|||
}
|
||||
|
||||
// ------------------------------------------------------------------ vertex input
|
||||
// Vertex: vec3 pos only, stride = 12 bytes
|
||||
VkVertexInputBindingDescription binding{};
|
||||
binding.binding = 0;
|
||||
binding.stride = sizeof(glm::vec3); // 12 bytes
|
||||
binding.stride = sizeof(glm::vec3);
|
||||
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
VkVertexInputAttributeDescription posAttr{};
|
||||
|
|
@ -77,7 +77,7 @@ bool Clouds::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout) {
|
|||
.setVertexInput({binding}, {posAttr})
|
||||
.setTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
|
||||
.setRasterization(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE)
|
||||
.setDepthTest(true, false, VK_COMPARE_OP_LESS_OR_EQUAL) // test on, write off (sky layer)
|
||||
.setDepthTest(true, false, VK_COMPARE_OP_LESS_OR_EQUAL)
|
||||
.setColorBlendAttachment(PipelineBuilder::blendAlpha())
|
||||
.setMultisample(vkCtx_->getMsaaSamples())
|
||||
.setLayout(pipelineLayout_)
|
||||
|
|
@ -122,7 +122,6 @@ void Clouds::recreatePipelines() {
|
|||
VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT);
|
||||
VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
||||
// Vertex input (same as initialize)
|
||||
VkVertexInputBindingDescription binding{};
|
||||
binding.binding = 0;
|
||||
binding.stride = sizeof(glm::vec3);
|
||||
|
|
@ -182,25 +181,35 @@ void Clouds::shutdown() {
|
|||
// Render
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Clouds::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, float timeOfDay) {
|
||||
void Clouds::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const SkyParams& params) {
|
||||
if (!enabled_ || pipeline_ == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
glm::vec3 color = getCloudColor(timeOfDay);
|
||||
// Derive cloud base color from DBC horizon band, slightly brightened
|
||||
glm::vec3 cloudBaseColor = params.skyBand1Color * 1.1f;
|
||||
cloudBaseColor = glm::clamp(cloudBaseColor, glm::vec3(0.0f), glm::vec3(1.0f));
|
||||
|
||||
// Sun direction (opposite of light direction)
|
||||
glm::vec3 sunDir = -glm::normalize(params.directionalDir);
|
||||
float sunAboveHorizon = glm::clamp(sunDir.z, 0.0f, 1.0f);
|
||||
|
||||
// Sun intensity based on elevation
|
||||
float sunIntensity = sunAboveHorizon;
|
||||
|
||||
// Ambient light — brighter during day, dimmer at night
|
||||
float ambient = glm::mix(0.3f, 0.7f, sunAboveHorizon);
|
||||
|
||||
CloudPush push{};
|
||||
push.cloudColor = glm::vec4(color, 1.0f);
|
||||
push.density = density_;
|
||||
push.windOffset = windOffset_;
|
||||
push.cloudColor = glm::vec4(cloudBaseColor, 1.0f);
|
||||
push.sunDirDensity = glm::vec4(sunDir, density_);
|
||||
push.windAndLight = glm::vec4(windOffset_, sunIntensity, ambient, 0.0f);
|
||||
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_);
|
||||
|
||||
// Bind per-frame UBO (set 0 — vertex shader reads view/projection from here)
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_,
|
||||
0, 1, &perFrameSet, 0, nullptr);
|
||||
|
||||
// Push cloud params to fragment shader
|
||||
vkCmdPushConstants(cmd, pipelineLayout_,
|
||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
0, sizeof(push), &push);
|
||||
|
|
@ -223,29 +232,6 @@ void Clouds::update(float deltaTime) {
|
|||
windOffset_ += deltaTime * windSpeed_ * 0.05f; // Slow drift
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cloud colour (unchanged logic from GL version)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
glm::vec3 Clouds::getCloudColor(float timeOfDay) const {
|
||||
glm::vec3 dayColor(0.95f, 0.95f, 1.0f);
|
||||
|
||||
if (timeOfDay >= 5.0f && timeOfDay < 7.0f) {
|
||||
// Dawn — orange tint fading to day
|
||||
float t = (timeOfDay - 5.0f) / 2.0f;
|
||||
return glm::mix(glm::vec3(1.0f, 0.7f, 0.5f), dayColor, t);
|
||||
} else if (timeOfDay >= 17.0f && timeOfDay < 19.0f) {
|
||||
// Dusk — day fading to orange/pink
|
||||
float t = (timeOfDay - 17.0f) / 2.0f;
|
||||
return glm::mix(dayColor, glm::vec3(1.0f, 0.6f, 0.4f), t);
|
||||
} else if (timeOfDay >= 20.0f || timeOfDay < 5.0f) {
|
||||
// Night — dark blue-grey
|
||||
return glm::vec3(0.15f, 0.15f, 0.25f);
|
||||
}
|
||||
|
||||
return dayColor;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Density setter
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -265,7 +251,7 @@ void Clouds::generateMesh() {
|
|||
// Upper hemisphere — Z-up world: altitude goes into Z, horizontal spread in X/Y
|
||||
for (int ring = 0; ring <= RINGS; ++ring) {
|
||||
float phi = (ring / static_cast<float>(RINGS)) * (static_cast<float>(M_PI) * 0.5f);
|
||||
float altZ = RADIUS * std::cos(phi); // altitude → world Z (up)
|
||||
float altZ = RADIUS * std::cos(phi);
|
||||
float ringRadius = RADIUS * std::sin(phi);
|
||||
|
||||
for (int seg = 0; seg <= SEGMENTS; ++seg) {
|
||||
|
|
|
|||
|
|
@ -211,28 +211,27 @@ void LensFlare::generateFlareElements() {
|
|||
flareElements.push_back({0.0f, 0.3f, glm::vec3(1.0f, 0.95f, 0.8f), 0.8f});
|
||||
|
||||
// Flare ghosts along sun-to-center axis
|
||||
// These appear at various positions between sun and opposite side
|
||||
|
||||
// Bright white ghost near sun
|
||||
flareElements.push_back({0.2f, 0.08f, glm::vec3(1.0f, 1.0f, 1.0f), 0.5f});
|
||||
|
||||
// Blue-tinted ghost
|
||||
flareElements.push_back({0.4f, 0.15f, glm::vec3(0.3f, 0.5f, 1.0f), 0.4f});
|
||||
flareElements.push_back({0.4f, 0.15f, glm::vec3(0.4f, 0.55f, 0.9f), 0.35f});
|
||||
|
||||
// Small bright spot
|
||||
flareElements.push_back({0.6f, 0.05f, glm::vec3(1.0f, 0.8f, 0.6f), 0.6f});
|
||||
flareElements.push_back({0.6f, 0.05f, glm::vec3(1.0f, 0.8f, 0.6f), 0.5f});
|
||||
|
||||
// Green-tinted ghost (chromatic aberration)
|
||||
flareElements.push_back({0.8f, 0.12f, glm::vec3(0.4f, 1.0f, 0.5f), 0.3f});
|
||||
// Warm amber ghost (replaced oversaturated green)
|
||||
flareElements.push_back({0.8f, 0.10f, glm::vec3(0.9f, 0.75f, 0.5f), 0.2f});
|
||||
|
||||
// Large halo on opposite side
|
||||
flareElements.push_back({-0.5f, 0.25f, glm::vec3(1.0f, 0.7f, 0.4f), 0.2f});
|
||||
flareElements.push_back({-0.5f, 0.22f, glm::vec3(1.0f, 0.8f, 0.5f), 0.15f});
|
||||
|
||||
// Purple ghost far from sun
|
||||
flareElements.push_back({-0.8f, 0.1f, glm::vec3(0.8f, 0.4f, 1.0f), 0.25f});
|
||||
// Faint blue ghost far from sun
|
||||
flareElements.push_back({-0.8f, 0.08f, glm::vec3(0.6f, 0.5f, 0.9f), 0.15f});
|
||||
|
||||
// Small red ghost
|
||||
flareElements.push_back({-1.2f, 0.06f, glm::vec3(1.0f, 0.3f, 0.3f), 0.3f});
|
||||
// Small warm ghost
|
||||
flareElements.push_back({-1.2f, 0.05f, glm::vec3(1.0f, 0.6f, 0.4f), 0.2f});
|
||||
}
|
||||
|
||||
glm::vec2 LensFlare::worldToScreen(const Camera& camera, const glm::vec3& worldPos) const {
|
||||
|
|
@ -287,7 +286,9 @@ float LensFlare::calculateSunVisibility(const Camera& camera, const glm::vec3& s
|
|||
return angleFactor * edgeFade;
|
||||
}
|
||||
|
||||
void LensFlare::render(VkCommandBuffer cmd, const Camera& camera, const glm::vec3& sunPosition, float timeOfDay) {
|
||||
void LensFlare::render(VkCommandBuffer cmd, const Camera& camera, const glm::vec3& sunPosition,
|
||||
float timeOfDay, float fogDensity, float cloudDensity,
|
||||
float weatherIntensity) {
|
||||
if (!enabled || pipeline == VK_NULL_HANDLE) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -312,6 +313,16 @@ void LensFlare::render(VkCommandBuffer cmd, const Camera& camera, const glm::vec
|
|||
return;
|
||||
}
|
||||
|
||||
// Atmospheric attenuation — fog, clouds, and weather reduce lens flare
|
||||
float atmosphericFactor = 1.0f;
|
||||
atmosphericFactor *= (1.0f - glm::clamp(fogDensity * 0.8f, 0.0f, 0.9f)); // Heavy fog nearly kills flare
|
||||
atmosphericFactor *= (1.0f - glm::clamp(cloudDensity * 0.6f, 0.0f, 0.7f)); // Clouds attenuate
|
||||
atmosphericFactor *= (1.0f - glm::clamp(weatherIntensity * 0.9f, 0.0f, 0.95f)); // Rain/snow heavily attenuates
|
||||
|
||||
if (atmosphericFactor < 0.01f) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get sun screen position
|
||||
glm::vec2 sunScreen = worldToScreen(camera, anchoredSunPos);
|
||||
glm::vec2 screenCenter(0.0f, 0.0f);
|
||||
|
|
@ -333,8 +344,8 @@ void LensFlare::render(VkCommandBuffer cmd, const Camera& camera, const glm::vec
|
|||
// Calculate position along sun-to-center axis
|
||||
glm::vec2 position = sunScreen + sunToCenter * element.position;
|
||||
|
||||
// Apply visibility and intensity
|
||||
float brightness = element.brightness * visibility * intensityMultiplier;
|
||||
// Apply visibility, intensity, and atmospheric attenuation
|
||||
float brightness = element.brightness * visibility * intensityMultiplier * atmosphericFactor;
|
||||
|
||||
// Set push constants
|
||||
FlarePushConstants push{};
|
||||
|
|
|
|||
|
|
@ -2417,10 +2417,21 @@ void Renderer::update(float deltaTime) {
|
|||
if (weather && gh) {
|
||||
uint32_t wType = gh->getWeatherType();
|
||||
float wInt = gh->getWeatherIntensity();
|
||||
if (wType == 1) weather->setWeatherType(Weather::Type::RAIN);
|
||||
else if (wType == 2) weather->setWeatherType(Weather::Type::SNOW);
|
||||
else weather->setWeatherType(Weather::Type::NONE);
|
||||
weather->setIntensity(wInt);
|
||||
if (wType != 0) {
|
||||
// Server-driven weather (SMSG_WEATHER) — authoritative
|
||||
if (wType == 1) weather->setWeatherType(Weather::Type::RAIN);
|
||||
else if (wType == 2) weather->setWeatherType(Weather::Type::SNOW);
|
||||
else weather->setWeatherType(Weather::Type::NONE);
|
||||
weather->setIntensity(wInt);
|
||||
} else {
|
||||
// No server weather — use zone-based weather configuration
|
||||
weather->updateZoneWeather(currentZoneId, deltaTime);
|
||||
}
|
||||
weather->setEnabled(true);
|
||||
} else if (weather) {
|
||||
// No game handler (single-player without network) — zone weather only
|
||||
weather->updateZoneWeather(currentZoneId, deltaTime);
|
||||
weather->setEnabled(true);
|
||||
}
|
||||
}
|
||||
auto light2 = std::chrono::high_resolution_clock::now();
|
||||
|
|
@ -3200,6 +3211,11 @@ void Renderer::renderWorld(game::World* world, game::GameHandler* gameHandler) {
|
|||
skyParams.horizonGlow = lighting.horizonGlow;
|
||||
}
|
||||
|
||||
// Weather attenuation for lens flare
|
||||
if (gameHandler) {
|
||||
skyParams.weatherIntensity = gameHandler->getWeatherIntensity();
|
||||
}
|
||||
|
||||
skyParams.skyboxModelId = 0;
|
||||
skyParams.skyboxHasStars = false;
|
||||
|
||||
|
|
@ -3866,6 +3882,7 @@ void Renderer::renderReflectionPass() {
|
|||
skyParams.fogDensity = lp.fogDensity;
|
||||
skyParams.horizonGlow = lp.horizonGlow;
|
||||
}
|
||||
// weatherIntensity left at default 0 for reflection pass (no game handler in scope)
|
||||
skySystem->render(currentCmd, reflPerFrameDescSet, *camera, skyParams);
|
||||
}
|
||||
if (terrainRenderer && terrainEnabled) {
|
||||
|
|
|
|||
|
|
@ -105,9 +105,9 @@ void SkySystem::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
|||
return;
|
||||
}
|
||||
|
||||
// --- Skybox (authoritative sky gradient) ---
|
||||
// --- Skybox (authoritative sky gradient, DBC-driven colors) ---
|
||||
if (skybox_) {
|
||||
skybox_->render(cmd, perFrameSet, params.timeOfDay);
|
||||
skybox_->render(cmd, perFrameSet, params);
|
||||
}
|
||||
|
||||
// --- Procedural stars (debug / fallback) ---
|
||||
|
|
@ -133,15 +133,17 @@ void SkySystem::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
|||
¶ms.directionalDir, ¶ms.sunColor, params.gameTime);
|
||||
}
|
||||
|
||||
// --- Clouds ---
|
||||
// --- Clouds (DBC-driven colors + sun lighting) ---
|
||||
if (clouds_) {
|
||||
clouds_->render(cmd, perFrameSet, params.timeOfDay);
|
||||
clouds_->render(cmd, perFrameSet, params);
|
||||
}
|
||||
|
||||
// --- Lens flare ---
|
||||
// --- Lens flare (attenuated by atmosphere) ---
|
||||
if (lensFlare_) {
|
||||
glm::vec3 sunPos = getSunPosition(params);
|
||||
lensFlare_->render(cmd, camera, sunPos, params.timeOfDay);
|
||||
lensFlare_->render(cmd, camera, sunPos, params.timeOfDay,
|
||||
params.fogDensity, params.cloudDensity,
|
||||
params.weatherIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "rendering/skybox.hpp"
|
||||
#include "rendering/sky_system.hpp"
|
||||
#include "rendering/vk_context.hpp"
|
||||
#include "rendering/vk_shader.hpp"
|
||||
#include "rendering/vk_pipeline.hpp"
|
||||
|
|
@ -10,6 +11,16 @@
|
|||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
||||
// Push constant struct — must match skybox.frag.glsl layout
|
||||
struct SkyPushConstants {
|
||||
glm::vec4 zenithColor; // DBC skyTopColor
|
||||
glm::vec4 midColor; // DBC skyMiddleColor
|
||||
glm::vec4 horizonColor; // DBC skyBand1Color
|
||||
glm::vec4 fogColor; // DBC skyBand2Color / fogColor blend
|
||||
glm::vec4 sunDirAndTime; // xyz = sun direction, w = timeOfDay
|
||||
};
|
||||
static_assert(sizeof(SkyPushConstants) == 80, "SkyPushConstants size mismatch");
|
||||
|
||||
Skybox::Skybox() = default;
|
||||
|
||||
Skybox::~Skybox() {
|
||||
|
|
@ -39,11 +50,11 @@ bool Skybox::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout) {
|
|||
VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT);
|
||||
VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
||||
// Push constant range: horizonColor (vec4) + zenithColor (vec4) + timeOfDay (float) = 36 bytes
|
||||
// Push constant range: 5 x vec4 = 80 bytes
|
||||
VkPushConstantRange pushRange{};
|
||||
pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
pushRange.offset = 0;
|
||||
pushRange.size = sizeof(glm::vec4) + sizeof(glm::vec4) + sizeof(float); // 36 bytes
|
||||
pushRange.size = sizeof(SkyPushConstants); // 80 bytes
|
||||
|
||||
// Create pipeline layout with perFrameLayout (set 0) + push constants
|
||||
pipelineLayout = createPipelineLayout(device, {perFrameLayout}, {pushRange});
|
||||
|
|
@ -148,24 +159,20 @@ void Skybox::shutdown() {
|
|||
vkCtx = nullptr;
|
||||
}
|
||||
|
||||
void Skybox::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, float time) {
|
||||
void Skybox::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const SkyParams& params) {
|
||||
if (pipeline == VK_NULL_HANDLE || !renderingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Push constant data
|
||||
struct SkyPushConstants {
|
||||
glm::vec4 horizonColor;
|
||||
glm::vec4 zenithColor;
|
||||
float timeOfDay;
|
||||
};
|
||||
// Compute sun direction from directionalDir (light points toward scene, sun is opposite)
|
||||
glm::vec3 sunDir = -glm::normalize(params.directionalDir);
|
||||
|
||||
SkyPushConstants push{};
|
||||
glm::vec3 horizon = getHorizonColor(time);
|
||||
glm::vec3 zenith = getZenithColor(time);
|
||||
push.horizonColor = glm::vec4(horizon, 1.0f);
|
||||
push.zenithColor = glm::vec4(zenith, 1.0f);
|
||||
push.timeOfDay = time;
|
||||
push.zenithColor = glm::vec4(params.skyTopColor, 1.0f);
|
||||
push.midColor = glm::vec4(params.skyMiddleColor, 1.0f);
|
||||
push.horizonColor = glm::vec4(params.skyBand1Color, 1.0f);
|
||||
push.fogColor = glm::vec4(params.skyBand2Color, 1.0f);
|
||||
push.sunDirAndTime = glm::vec4(sunDir, params.timeOfDay);
|
||||
|
||||
// Bind pipeline
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||
|
|
@ -202,103 +209,5 @@ void Skybox::setTimeOfDay(float time) {
|
|||
timeOfDay = time;
|
||||
}
|
||||
|
||||
glm::vec3 Skybox::getHorizonColor(float time) const {
|
||||
// Time-based horizon colors
|
||||
// 0-6: Night (dark blue)
|
||||
// 6-8: Dawn (orange/pink)
|
||||
// 8-16: Day (light blue)
|
||||
// 16-18: Dusk (orange/red)
|
||||
// 18-24: Night (dark blue)
|
||||
|
||||
if (time < 5.0f || time >= 21.0f) {
|
||||
// Night - dark blue/purple horizon
|
||||
return glm::vec3(0.05f, 0.05f, 0.15f);
|
||||
}
|
||||
else if (time >= 5.0f && time < 7.0f) {
|
||||
// Dawn - blend from night to orange
|
||||
float t = (time - 5.0f) / 2.0f;
|
||||
glm::vec3 night = glm::vec3(0.05f, 0.05f, 0.15f);
|
||||
glm::vec3 dawn = glm::vec3(1.0f, 0.5f, 0.2f);
|
||||
return glm::mix(night, dawn, t);
|
||||
}
|
||||
else if (time >= 7.0f && time < 9.0f) {
|
||||
// Morning - blend from orange to blue
|
||||
float t = (time - 7.0f) / 2.0f;
|
||||
glm::vec3 dawn = glm::vec3(1.0f, 0.5f, 0.2f);
|
||||
glm::vec3 day = glm::vec3(0.6f, 0.7f, 0.9f);
|
||||
return glm::mix(dawn, day, t);
|
||||
}
|
||||
else if (time >= 9.0f && time < 17.0f) {
|
||||
// Day - light blue horizon
|
||||
return glm::vec3(0.6f, 0.7f, 0.9f);
|
||||
}
|
||||
else if (time >= 17.0f && time < 19.0f) {
|
||||
// Dusk - blend from blue to orange/red
|
||||
float t = (time - 17.0f) / 2.0f;
|
||||
glm::vec3 day = glm::vec3(0.6f, 0.7f, 0.9f);
|
||||
glm::vec3 dusk = glm::vec3(1.0f, 0.4f, 0.1f);
|
||||
return glm::mix(day, dusk, t);
|
||||
}
|
||||
else {
|
||||
// Evening - blend from orange to night
|
||||
float t = (time - 19.0f) / 2.0f;
|
||||
glm::vec3 dusk = glm::vec3(1.0f, 0.4f, 0.1f);
|
||||
glm::vec3 night = glm::vec3(0.05f, 0.05f, 0.15f);
|
||||
return glm::mix(dusk, night, t);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Skybox::getZenithColor(float time) const {
|
||||
// Zenith (top of sky) colors
|
||||
|
||||
if (time < 5.0f || time >= 21.0f) {
|
||||
// Night - very dark blue, almost black
|
||||
return glm::vec3(0.01f, 0.01f, 0.05f);
|
||||
}
|
||||
else if (time >= 5.0f && time < 7.0f) {
|
||||
// Dawn - blend from night to light blue
|
||||
float t = (time - 5.0f) / 2.0f;
|
||||
glm::vec3 night = glm::vec3(0.01f, 0.01f, 0.05f);
|
||||
glm::vec3 dawn = glm::vec3(0.3f, 0.4f, 0.7f);
|
||||
return glm::mix(night, dawn, t);
|
||||
}
|
||||
else if (time >= 7.0f && time < 9.0f) {
|
||||
// Morning - blend to bright blue
|
||||
float t = (time - 7.0f) / 2.0f;
|
||||
glm::vec3 dawn = glm::vec3(0.3f, 0.4f, 0.7f);
|
||||
glm::vec3 day = glm::vec3(0.2f, 0.5f, 1.0f);
|
||||
return glm::mix(dawn, day, t);
|
||||
}
|
||||
else if (time >= 9.0f && time < 17.0f) {
|
||||
// Day - bright blue zenith
|
||||
return glm::vec3(0.2f, 0.5f, 1.0f);
|
||||
}
|
||||
else if (time >= 17.0f && time < 19.0f) {
|
||||
// Dusk - blend to darker blue
|
||||
float t = (time - 17.0f) / 2.0f;
|
||||
glm::vec3 day = glm::vec3(0.2f, 0.5f, 1.0f);
|
||||
glm::vec3 dusk = glm::vec3(0.1f, 0.2f, 0.4f);
|
||||
return glm::mix(day, dusk, t);
|
||||
}
|
||||
else {
|
||||
// Evening - blend to night
|
||||
float t = (time - 19.0f) / 2.0f;
|
||||
glm::vec3 dusk = glm::vec3(0.1f, 0.2f, 0.4f);
|
||||
glm::vec3 night = glm::vec3(0.01f, 0.01f, 0.05f);
|
||||
return glm::mix(dusk, night, t);
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 Skybox::getSkyColor(float altitude, float time) const {
|
||||
// Blend between horizon and zenith based on altitude
|
||||
glm::vec3 horizon = getHorizonColor(time);
|
||||
glm::vec3 zenith = getZenithColor(time);
|
||||
|
||||
// Use power curve for more natural gradient
|
||||
float t = std::pow(std::max(altitude, 0.0f), 0.5f);
|
||||
|
||||
return glm::mix(horizon, zenith, t);
|
||||
}
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
|
|||
|
|
@ -369,5 +369,134 @@ void Weather::shutdown() {
|
|||
particlePositions.clear();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Zone-based weather configuration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Weather::setZoneWeather(uint32_t zoneId, Type type, float minIntensity, float maxIntensity, float probability) {
|
||||
zoneWeatherTable_[zoneId] = {type, minIntensity, maxIntensity, probability};
|
||||
}
|
||||
|
||||
void Weather::initializeZoneWeatherDefaults() {
|
||||
if (zoneWeatherInitialized_) return;
|
||||
zoneWeatherInitialized_ = true;
|
||||
|
||||
// Eastern Kingdoms zones
|
||||
setZoneWeather(10, Type::RAIN, 0.2f, 0.6f, 0.3f); // Duskwood — frequent rain
|
||||
setZoneWeather(11, Type::RAIN, 0.1f, 0.4f, 0.15f); // Wetlands — moderate rain
|
||||
setZoneWeather(8, Type::RAIN, 0.1f, 0.5f, 0.2f); // Swamp of Sorrows
|
||||
setZoneWeather(33, Type::RAIN, 0.2f, 0.7f, 0.25f); // Stranglethorn Vale
|
||||
setZoneWeather(44, Type::RAIN, 0.1f, 0.3f, 0.1f); // Redridge Mountains — light rain
|
||||
setZoneWeather(36, Type::RAIN, 0.1f, 0.4f, 0.15f); // Alterac Mountains
|
||||
setZoneWeather(45, Type::RAIN, 0.1f, 0.3f, 0.1f); // Arathi Highlands
|
||||
setZoneWeather(267, Type::RAIN, 0.2f, 0.5f, 0.2f); // Hillsbrad Foothills
|
||||
setZoneWeather(28, Type::RAIN, 0.1f, 0.3f, 0.1f); // Western Plaguelands — occasional rain
|
||||
setZoneWeather(139, Type::RAIN, 0.1f, 0.3f, 0.1f); // Eastern Plaguelands
|
||||
|
||||
// Snowy zones
|
||||
setZoneWeather(1, Type::SNOW, 0.2f, 0.6f, 0.3f); // Dun Morogh
|
||||
setZoneWeather(51, Type::SNOW, 0.1f, 0.5f, 0.2f); // Searing Gorge (occasional)
|
||||
setZoneWeather(41, Type::SNOW, 0.1f, 0.4f, 0.15f); // Deadwind Pass
|
||||
setZoneWeather(2817, Type::SNOW, 0.3f, 0.7f, 0.4f); // Crystalsong Forest
|
||||
setZoneWeather(67, Type::SNOW, 0.2f, 0.6f, 0.35f); // Storm Peaks
|
||||
setZoneWeather(65, Type::SNOW, 0.2f, 0.5f, 0.3f); // Dragonblight
|
||||
setZoneWeather(394, Type::SNOW, 0.1f, 0.4f, 0.2f); // Grizzly Hills
|
||||
setZoneWeather(495, Type::SNOW, 0.3f, 0.8f, 0.5f); // Howling Fjord
|
||||
setZoneWeather(210, Type::SNOW, 0.2f, 0.5f, 0.25f); // Icecrown
|
||||
setZoneWeather(3537, Type::SNOW, 0.2f, 0.6f, 0.3f); // Borean Tundra
|
||||
setZoneWeather(4742, Type::SNOW, 0.2f, 0.5f, 0.3f); // Hrothgar's Landing
|
||||
|
||||
// Kalimdor zones
|
||||
setZoneWeather(15, Type::RAIN, 0.1f, 0.4f, 0.15f); // Dustwallow Marsh
|
||||
setZoneWeather(16, Type::RAIN, 0.1f, 0.3f, 0.1f); // Azshara
|
||||
setZoneWeather(148, Type::RAIN, 0.1f, 0.4f, 0.15f); // Darkshore
|
||||
setZoneWeather(331, Type::RAIN, 0.1f, 0.3f, 0.1f); // Ashenvale
|
||||
setZoneWeather(405, Type::RAIN, 0.1f, 0.3f, 0.1f); // Desolace
|
||||
setZoneWeather(15, Type::RAIN, 0.2f, 0.5f, 0.2f); // Dustwallow Marsh
|
||||
setZoneWeather(490, Type::RAIN, 0.1f, 0.4f, 0.15f); // Un'Goro Crater
|
||||
setZoneWeather(493, Type::RAIN, 0.1f, 0.3f, 0.1f); // Moonglade
|
||||
|
||||
// Winterspring is snowy
|
||||
setZoneWeather(618, Type::SNOW, 0.2f, 0.6f, 0.3f); // Winterspring
|
||||
|
||||
// Outland
|
||||
setZoneWeather(3483, Type::RAIN, 0.1f, 0.3f, 0.1f); // Hellfire Peninsula (occasional)
|
||||
setZoneWeather(3521, Type::RAIN, 0.1f, 0.4f, 0.15f); // Zangarmarsh
|
||||
setZoneWeather(3519, Type::RAIN, 0.1f, 0.3f, 0.1f); // Terokkar Forest
|
||||
}
|
||||
|
||||
void Weather::updateZoneWeather(uint32_t zoneId, float deltaTime) {
|
||||
if (!zoneWeatherInitialized_) {
|
||||
initializeZoneWeatherDefaults();
|
||||
}
|
||||
|
||||
// Zone changed — reset weather cycle
|
||||
if (zoneId != currentWeatherZone_) {
|
||||
currentWeatherZone_ = zoneId;
|
||||
zoneWeatherTimer_ = 0.0f;
|
||||
|
||||
auto it = zoneWeatherTable_.find(zoneId);
|
||||
if (it == zoneWeatherTable_.end()) {
|
||||
// Zone has no configured weather — clear gradually
|
||||
targetIntensity_ = 0.0f;
|
||||
} else {
|
||||
// Roll whether weather is active based on probability
|
||||
float roll = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
|
||||
zoneWeatherActive_ = (roll < it->second.probability);
|
||||
|
||||
if (zoneWeatherActive_) {
|
||||
weatherType = it->second.type;
|
||||
// Random intensity within configured range
|
||||
float t = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
|
||||
targetIntensity_ = glm::mix(it->second.minIntensity, it->second.maxIntensity, t);
|
||||
// Random cycle duration: 3-8 minutes
|
||||
zoneWeatherCycleDuration_ = 180.0f + static_cast<float>(rand()) / static_cast<float>(RAND_MAX) * 300.0f;
|
||||
} else {
|
||||
targetIntensity_ = 0.0f;
|
||||
zoneWeatherCycleDuration_ = 120.0f + static_cast<float>(rand()) / static_cast<float>(RAND_MAX) * 180.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Smooth intensity transitions
|
||||
float transitionSpeed = 0.15f * deltaTime; // ~7 seconds to full transition
|
||||
if (intensity < targetIntensity_) {
|
||||
intensity = std::min(intensity + transitionSpeed, targetIntensity_);
|
||||
} else if (intensity > targetIntensity_) {
|
||||
intensity = std::max(intensity - transitionSpeed, targetIntensity_);
|
||||
}
|
||||
|
||||
// If intensity reached zero and target is zero, clear weather type
|
||||
if (intensity <= 0.01f && targetIntensity_ <= 0.01f) {
|
||||
if (weatherType != Type::NONE) {
|
||||
weatherType = Type::NONE;
|
||||
particles.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Weather cycling — periodically re-roll weather
|
||||
zoneWeatherTimer_ += deltaTime;
|
||||
if (zoneWeatherTimer_ >= zoneWeatherCycleDuration_ && zoneWeatherCycleDuration_ > 0.0f) {
|
||||
zoneWeatherTimer_ = 0.0f;
|
||||
|
||||
auto it = zoneWeatherTable_.find(zoneId);
|
||||
if (it != zoneWeatherTable_.end()) {
|
||||
float roll = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
|
||||
zoneWeatherActive_ = (roll < it->second.probability);
|
||||
|
||||
if (zoneWeatherActive_) {
|
||||
weatherType = it->second.type;
|
||||
float t = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
|
||||
targetIntensity_ = glm::mix(it->second.minIntensity, it->second.maxIntensity, t);
|
||||
} else {
|
||||
targetIntensity_ = 0.0f;
|
||||
}
|
||||
|
||||
// New cycle duration
|
||||
zoneWeatherCycleDuration_ = 180.0f + static_cast<float>(rand()) / static_cast<float>(RAND_MAX) * 300.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rendering
|
||||
} // namespace wowee
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue