mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +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
|
|
@ -26,9 +26,21 @@ float valueNoise(vec2 p) {
|
||||||
void main() {
|
void main() {
|
||||||
vec2 uv = TexCoord - 0.5;
|
vec2 uv = TexCoord - 0.5;
|
||||||
float dist = length(uv);
|
float dist = length(uv);
|
||||||
float disc = smoothstep(0.42, 0.38, dist);
|
|
||||||
float glow = exp(-dist * dist * 12.0) * 0.6;
|
// Hard disc with smooth edge
|
||||||
|
float disc = smoothstep(0.42, 0.35, dist);
|
||||||
|
|
||||||
|
// Soft glow that fades to zero well within quad bounds
|
||||||
|
// At dist=0.5 (quad edge), this should be negligible
|
||||||
|
float glow = exp(-dist * dist * 18.0) * 0.5;
|
||||||
|
|
||||||
|
// Combine disc and glow
|
||||||
float alpha = max(disc, glow) * push.intensity;
|
float alpha = max(disc, glow) * push.intensity;
|
||||||
|
|
||||||
|
// Fade to zero at quad edges to prevent visible box
|
||||||
|
float edgeFade = 1.0 - smoothstep(0.4, 0.5, dist);
|
||||||
|
alpha *= edgeFade;
|
||||||
|
|
||||||
vec3 color = push.celestialColor.rgb;
|
vec3 color = push.celestialColor.rgb;
|
||||||
|
|
||||||
// Animated haze/turbulence overlay for the sun disc
|
// Animated haze/turbulence overlay for the sun disc
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,35 +1,42 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
layout(push_constant) uniform Push {
|
layout(push_constant) uniform Push {
|
||||||
vec4 cloudColor;
|
vec4 cloudColor; // xyz = DBC-derived base cloud color, w = unused
|
||||||
float density;
|
vec4 sunDirDensity; // xyz = sun direction, w = density
|
||||||
float windOffset;
|
vec4 windAndLight; // x = windOffset, y = sunIntensity, z = ambient, w = unused
|
||||||
} push;
|
} push;
|
||||||
|
|
||||||
layout(location = 0) in vec3 vWorldDir;
|
layout(location = 0) in vec3 vWorldDir;
|
||||||
|
|
||||||
layout(location = 0) out vec4 outColor;
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
float hash(vec2 p) {
|
// --- Gradient noise (smoother than hash-based) ---
|
||||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
|
vec2 hash2(vec2 p) {
|
||||||
|
p = vec2(dot(p, vec2(127.1, 311.7)),
|
||||||
|
dot(p, vec2(269.5, 183.3)));
|
||||||
|
return fract(sin(p) * 43758.5453);
|
||||||
}
|
}
|
||||||
|
|
||||||
float noise(vec2 p) {
|
float gradientNoise(vec2 p) {
|
||||||
vec2 i = floor(p);
|
vec2 i = floor(p);
|
||||||
vec2 f = fract(p);
|
vec2 f = fract(p);
|
||||||
f = f * f * (3.0 - 2.0 * f);
|
|
||||||
float a = hash(i);
|
// Quintic interpolation for smoother results
|
||||||
float b = hash(i + vec2(1.0, 0.0));
|
vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
|
||||||
float c = hash(i + vec2(0.0, 1.0));
|
|
||||||
float d = hash(i + vec2(1.0, 1.0));
|
float a = dot(hash2(i + vec2(0.0, 0.0)) * 2.0 - 1.0, f - vec2(0.0, 0.0));
|
||||||
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
float b = dot(hash2(i + vec2(1.0, 0.0)) * 2.0 - 1.0, f - vec2(1.0, 0.0));
|
||||||
|
float c = dot(hash2(i + vec2(0.0, 1.0)) * 2.0 - 1.0, f - vec2(0.0, 1.0));
|
||||||
|
float d = dot(hash2(i + vec2(1.0, 1.0)) * 2.0 - 1.0, f - vec2(1.0, 1.0));
|
||||||
|
|
||||||
|
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y) * 0.5 + 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
float fbm(vec2 p) {
|
float fbm(vec2 p) {
|
||||||
float val = 0.0;
|
float val = 0.0;
|
||||||
float amp = 0.5;
|
float amp = 0.5;
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
val += amp * noise(p);
|
val += amp * gradientNoise(p);
|
||||||
p *= 2.0;
|
p *= 2.0;
|
||||||
amp *= 0.5;
|
amp *= 0.5;
|
||||||
}
|
}
|
||||||
|
|
@ -38,26 +45,60 @@ float fbm(vec2 p) {
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 dir = normalize(vWorldDir);
|
vec3 dir = normalize(vWorldDir);
|
||||||
float altitude = dir.z; // Z is up in the Z-up world coordinate system
|
float altitude = dir.z;
|
||||||
if (altitude < 0.0) discard;
|
if (altitude < 0.0) discard;
|
||||||
|
|
||||||
vec2 uv = dir.xy / (altitude + 0.001); // XY is the horizontal plane
|
vec3 sunDir = push.sunDirDensity.xyz;
|
||||||
uv += push.windOffset;
|
float density = push.sunDirDensity.w;
|
||||||
|
float windOffset = push.windAndLight.x;
|
||||||
|
float sunIntensity = push.windAndLight.y;
|
||||||
|
float ambient = push.windAndLight.z;
|
||||||
|
|
||||||
|
vec2 uv = dir.xy / (altitude + 0.001);
|
||||||
|
uv += windOffset;
|
||||||
|
|
||||||
|
// --- 6-octave FBM for cloud shape ---
|
||||||
float cloud1 = fbm(uv * 0.8);
|
float cloud1 = fbm(uv * 0.8);
|
||||||
float cloud2 = fbm(uv * 1.6 + 5.0);
|
float cloud2 = fbm(uv * 1.6 + 5.0);
|
||||||
float cloud = cloud1 * 0.7 + cloud2 * 0.3;
|
float cloud = cloud1 * 0.7 + cloud2 * 0.3;
|
||||||
cloud = smoothstep(0.35, 0.65, cloud) * push.density;
|
|
||||||
|
|
||||||
float edgeBreak = noise(uv * 4.0);
|
// Coverage control: base coverage with detail erosion
|
||||||
cloud *= smoothstep(0.2, 0.5, edgeBreak);
|
float baseCoverage = smoothstep(0.30, 0.55, cloud);
|
||||||
|
float detailErosion = gradientNoise(uv * 4.0);
|
||||||
|
cloud = baseCoverage * smoothstep(0.2, 0.5, detailErosion);
|
||||||
|
cloud *= density;
|
||||||
|
|
||||||
|
// Horizon fade
|
||||||
float horizonFade = smoothstep(0.0, 0.15, altitude);
|
float horizonFade = smoothstep(0.0, 0.15, altitude);
|
||||||
cloud *= horizonFade;
|
cloud *= horizonFade;
|
||||||
|
|
||||||
|
if (cloud < 0.01) discard;
|
||||||
|
|
||||||
|
// --- Sun lighting on clouds ---
|
||||||
|
// Sun dot product for view-relative brightness
|
||||||
|
float sunDot = max(dot(vec3(0.0, 0.0, 1.0), sunDir), 0.0);
|
||||||
|
|
||||||
|
// Self-shadowing: sample noise offset toward sun direction, darken if occluded
|
||||||
|
float lightSample = fbm((uv + sunDir.xy * 0.05) * 0.8);
|
||||||
|
float shadow = smoothstep(0.3, 0.7, lightSample);
|
||||||
|
|
||||||
|
// Base lit color: mix dark (shadow) and bright (sunlit) based on shadow and sun
|
||||||
|
vec3 baseColor = push.cloudColor.rgb;
|
||||||
|
vec3 shadowColor = baseColor * (ambient * 0.8);
|
||||||
|
vec3 litColor = baseColor * (ambient + sunIntensity * 0.6);
|
||||||
|
vec3 cloudRgb = mix(shadowColor, litColor, shadow * sunDot);
|
||||||
|
|
||||||
|
// Add ambient fill so clouds aren't too dark
|
||||||
|
cloudRgb = mix(baseColor * ambient, cloudRgb, 0.7 + 0.3 * sunIntensity);
|
||||||
|
|
||||||
|
// --- Silver lining effect at cloud edges ---
|
||||||
|
float edgeLight = smoothstep(0.0, 0.3, cloud) * (1.0 - smoothstep(0.3, 0.8, cloud));
|
||||||
|
cloudRgb += vec3(1.0, 0.95, 0.9) * edgeLight * sunDot * sunIntensity * 0.4;
|
||||||
|
|
||||||
|
// --- Edge softness for alpha ---
|
||||||
float edgeSoftness = smoothstep(0.0, 0.3, cloud);
|
float edgeSoftness = smoothstep(0.0, 0.3, cloud);
|
||||||
float alpha = cloud * edgeSoftness;
|
float alpha = cloud * edgeSoftness;
|
||||||
|
|
||||||
if (alpha < 0.01) discard;
|
if (alpha < 0.01) discard;
|
||||||
outColor = vec4(push.cloudColor.rgb, alpha);
|
outColor = vec4(cloudRgb, alpha);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -14,9 +14,11 @@ layout(set = 0, binding = 0) uniform PerFrame {
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(push_constant) uniform Push {
|
layout(push_constant) uniform Push {
|
||||||
vec4 horizonColor;
|
vec4 zenithColor; // DBC skyTopColor
|
||||||
vec4 zenithColor;
|
vec4 midColor; // DBC skyMiddleColor
|
||||||
float timeOfDay;
|
vec4 horizonColor; // DBC skyBand1Color
|
||||||
|
vec4 fogColorPush; // DBC skyBand2Color
|
||||||
|
vec4 sunDirAndTime; // xyz = sun direction, w = timeOfDay
|
||||||
} push;
|
} push;
|
||||||
|
|
||||||
layout(location = 0) in vec2 TexCoord;
|
layout(location = 0) in vec2 TexCoord;
|
||||||
|
|
@ -25,27 +27,71 @@ layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// Reconstruct world-space ray direction from screen position.
|
// Reconstruct world-space ray direction from screen position.
|
||||||
// TexCoord is [0,1]^2; convert to NDC [-1,1]^2.
|
|
||||||
float ndcX = TexCoord.x * 2.0 - 1.0;
|
float ndcX = TexCoord.x * 2.0 - 1.0;
|
||||||
float ndcY = -(TexCoord.y * 2.0 - 1.0); // flip Y: Vulkan NDC Y-down, but projection already flipped
|
float ndcY = -(TexCoord.y * 2.0 - 1.0);
|
||||||
|
|
||||||
// Unproject to view space using focal lengths from projection matrix.
|
|
||||||
// projection[0][0] = 2*near/(right-left) = 1/tan(fovX/2)
|
|
||||||
// projection[1][1] = 2*near/(top-bottom) (already negated for Vulkan Y-flip)
|
|
||||||
// We want the original magnitude, so take abs to get the focal length.
|
|
||||||
vec3 viewDir = vec3(ndcX / projection[0][0],
|
vec3 viewDir = vec3(ndcX / projection[0][0],
|
||||||
ndcY / abs(projection[1][1]),
|
ndcY / abs(projection[1][1]),
|
||||||
-1.0);
|
-1.0);
|
||||||
|
|
||||||
// Rotate to world space: view = R*T, so R^-1 = R^T = transpose(mat3(view))
|
|
||||||
mat3 invViewRot = transpose(mat3(view));
|
mat3 invViewRot = transpose(mat3(view));
|
||||||
vec3 worldDir = normalize(invViewRot * viewDir);
|
vec3 worldDir = normalize(invViewRot * viewDir);
|
||||||
|
|
||||||
// worldDir.z = sin(elevation); +1 = zenith, 0 = horizon, -1 = nadir
|
vec3 sunDir = push.sunDirAndTime.xyz;
|
||||||
float t = clamp(worldDir.z, 0.0, 1.0);
|
float timeOfDay = push.sunDirAndTime.w;
|
||||||
t = pow(t, 1.5);
|
|
||||||
vec3 sky = mix(push.horizonColor.rgb, push.zenithColor.rgb, t);
|
// Elevation: +1 = zenith, 0 = horizon, -1 = nadir
|
||||||
float scatter = max(0.0, 1.0 - t * 2.0) * 0.15;
|
float elev = worldDir.z;
|
||||||
sky += vec3(scatter * 0.8, scatter * 0.4, scatter * 0.1);
|
float elevClamped = clamp(elev, 0.0, 1.0);
|
||||||
|
|
||||||
|
// --- 3-band sky gradient using DBC colors ---
|
||||||
|
// Zenith dominates upper sky, mid color fills the middle,
|
||||||
|
// horizon band at the bottom with a thin fog fringe.
|
||||||
|
vec3 sky;
|
||||||
|
if (elevClamped > 0.4) {
|
||||||
|
// Upper sky: mid -> zenith
|
||||||
|
float t = (elevClamped - 0.4) / 0.6;
|
||||||
|
sky = mix(push.midColor.rgb, push.zenithColor.rgb, t);
|
||||||
|
} else if (elevClamped > 0.05) {
|
||||||
|
// Lower sky: horizon -> mid (wide band)
|
||||||
|
float t = (elevClamped - 0.05) / 0.35;
|
||||||
|
sky = mix(push.horizonColor.rgb, push.midColor.rgb, t);
|
||||||
|
} else {
|
||||||
|
// Thin fog fringe right at horizon
|
||||||
|
float t = elevClamped / 0.05;
|
||||||
|
sky = mix(push.fogColorPush.rgb, push.horizonColor.rgb, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Below-horizon darkening (nadir) ---
|
||||||
|
if (elev < 0.0) {
|
||||||
|
float nadirFade = clamp(-elev * 3.0, 0.0, 1.0);
|
||||||
|
vec3 nadirColor = push.fogColorPush.rgb * 0.3;
|
||||||
|
sky = mix(push.fogColorPush.rgb, nadirColor, nadirFade);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Rayleigh-like scattering (subtle warm glow near sun) ---
|
||||||
|
float sunDot = max(dot(worldDir, sunDir), 0.0);
|
||||||
|
float sunAboveHorizon = clamp(sunDir.z, 0.0, 1.0);
|
||||||
|
|
||||||
|
float rayleighStrength = pow(1.0 - elevClamped, 3.0) * 0.15;
|
||||||
|
vec3 scatterColor = mix(vec3(0.8, 0.45, 0.15), vec3(0.3, 0.5, 1.0), elevClamped);
|
||||||
|
sky += scatterColor * rayleighStrength * sunDot * sunAboveHorizon;
|
||||||
|
|
||||||
|
// --- Mie-like forward scatter (sun disk glow) ---
|
||||||
|
float mieSharp = pow(sunDot, 64.0) * 0.4;
|
||||||
|
float mieSoft = pow(sunDot, 8.0) * 0.1;
|
||||||
|
vec3 sunGlowColor = mix(vec3(1.0, 0.85, 0.55), vec3(1.0, 1.0, 0.95), elevClamped);
|
||||||
|
sky += sunGlowColor * (mieSharp + mieSoft) * sunAboveHorizon;
|
||||||
|
|
||||||
|
// --- Subtle horizon haze ---
|
||||||
|
float hazeDensity = exp(-elevClamped * 12.0) * 0.06;
|
||||||
|
sky += push.horizonColor.rgb * hazeDensity * sunAboveHorizon;
|
||||||
|
|
||||||
|
// --- Night: slight moonlight tint ---
|
||||||
|
if (sunDir.z < 0.0) {
|
||||||
|
float moonlight = clamp(-sunDir.z * 0.5, 0.0, 0.15);
|
||||||
|
sky += vec3(0.02, 0.03, 0.08) * moonlight;
|
||||||
|
}
|
||||||
|
|
||||||
outColor = vec4(sky, 1.0);
|
outColor = vec4(sky, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -9,45 +9,37 @@ namespace wowee {
|
||||||
namespace rendering {
|
namespace rendering {
|
||||||
|
|
||||||
class VkContext;
|
class VkContext;
|
||||||
|
struct SkyParams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Procedural cloud renderer (Vulkan)
|
* Procedural cloud renderer (Vulkan)
|
||||||
*
|
*
|
||||||
* Renders animated procedural clouds on a sky hemisphere using FBM noise.
|
* Renders animated procedural clouds on a sky hemisphere using FBM noise.
|
||||||
* Two noise layers at different frequencies produce realistic cloud shapes.
|
* Sun-lit edges, self-shadowing, and DBC-driven cloud colors for realistic appearance.
|
||||||
*
|
*
|
||||||
* Pipeline layout:
|
* Pipeline layout:
|
||||||
* set 0 = perFrameLayout (camera UBO — view, projection, etc.)
|
* set 0 = perFrameLayout (camera UBO — view, projection, etc.)
|
||||||
* push = CloudPush (vec4 cloudColor + float density + float windOffset = 24 bytes)
|
* push = CloudPush (3 x vec4 = 48 bytes)
|
||||||
*
|
|
||||||
* The vertex shader reads view/projection from set 0 directly; no per-object
|
|
||||||
* model matrix is needed (clouds are locked to the sky dome).
|
|
||||||
*/
|
*/
|
||||||
class Clouds {
|
class Clouds {
|
||||||
public:
|
public:
|
||||||
Clouds();
|
Clouds();
|
||||||
~Clouds();
|
~Clouds();
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the cloud system.
|
|
||||||
* @param ctx Vulkan context
|
|
||||||
* @param perFrameLayout Descriptor set layout for set 0 (camera UBO)
|
|
||||||
*/
|
|
||||||
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
bool initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout);
|
||||||
void shutdown();
|
void shutdown();
|
||||||
void recreatePipelines();
|
void recreatePipelines();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render clouds.
|
* Render clouds using DBC-driven colors and sun lighting.
|
||||||
* @param cmd Command buffer to record into
|
* @param cmd Command buffer to record into
|
||||||
* @param perFrameSet Per-frame descriptor set (set 0, camera UBO)
|
* @param perFrameSet Per-frame descriptor set (set 0, camera UBO)
|
||||||
* @param timeOfDay Time of day in hours (0-24)
|
* @param params Sky parameters with DBC colors and sun direction
|
||||||
*/
|
*/
|
||||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, float timeOfDay);
|
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const SkyParams& params);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update cloud animation (wind drift).
|
* Update cloud animation (wind drift).
|
||||||
* @param deltaTime Seconds since last frame
|
|
||||||
*/
|
*/
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
|
|
||||||
|
|
@ -56,7 +48,6 @@ public:
|
||||||
bool isEnabled() const { return enabled_; }
|
bool isEnabled() const { return enabled_; }
|
||||||
|
|
||||||
// --- Cloud parameters ---
|
// --- Cloud parameters ---
|
||||||
/** Cloud coverage, 0 = clear, 1 = overcast. */
|
|
||||||
void setDensity(float density);
|
void setDensity(float density);
|
||||||
float getDensity() const { return density_; }
|
float getDensity() const { return density_; }
|
||||||
|
|
||||||
|
|
@ -66,19 +57,16 @@ public:
|
||||||
private:
|
private:
|
||||||
// Push constant block — must match clouds.frag.glsl
|
// Push constant block — must match clouds.frag.glsl
|
||||||
struct CloudPush {
|
struct CloudPush {
|
||||||
glm::vec4 cloudColor; // 16 bytes (xyz = colour, w unused)
|
glm::vec4 cloudColor; // xyz = DBC-derived base cloud color, w = unused
|
||||||
float density; // 4 bytes
|
glm::vec4 sunDirDensity; // xyz = sun direction, w = density
|
||||||
float windOffset; // 4 bytes
|
glm::vec4 windAndLight; // x = windOffset, y = sunIntensity, z = ambient, w = unused
|
||||||
// total = 24 bytes
|
|
||||||
};
|
};
|
||||||
static_assert(sizeof(CloudPush) == 24, "CloudPush size mismatch");
|
static_assert(sizeof(CloudPush) == 48, "CloudPush size mismatch");
|
||||||
|
|
||||||
void generateMesh();
|
void generateMesh();
|
||||||
void createBuffers();
|
void createBuffers();
|
||||||
void destroyBuffers();
|
void destroyBuffers();
|
||||||
|
|
||||||
glm::vec3 getCloudColor(float timeOfDay) const;
|
|
||||||
|
|
||||||
// Vulkan objects
|
// Vulkan objects
|
||||||
VkContext* vkCtx_ = nullptr;
|
VkContext* vkCtx_ = nullptr;
|
||||||
VkPipeline pipeline_ = VK_NULL_HANDLE;
|
VkPipeline pipeline_ = VK_NULL_HANDLE;
|
||||||
|
|
@ -95,14 +83,14 @@ private:
|
||||||
|
|
||||||
// Cloud parameters
|
// Cloud parameters
|
||||||
bool enabled_ = true;
|
bool enabled_ = true;
|
||||||
float density_ = 0.5f;
|
float density_ = 0.35f;
|
||||||
float windSpeed_ = 1.0f;
|
float windSpeed_ = 1.0f;
|
||||||
float windOffset_ = 0.0f; // Accumulated wind movement
|
float windOffset_ = 0.0f;
|
||||||
|
|
||||||
// Mesh generation parameters
|
// Mesh generation parameters
|
||||||
static constexpr int SEGMENTS = 32;
|
static constexpr int SEGMENTS = 32;
|
||||||
static constexpr int RINGS = 8;
|
static constexpr int RINGS = 8;
|
||||||
static constexpr float RADIUS = 900.0f; // Slightly smaller than skybox
|
static constexpr float RADIUS = 900.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rendering
|
} // namespace rendering
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,13 @@ public:
|
||||||
* @param camera The camera to render from
|
* @param camera The camera to render from
|
||||||
* @param sunPosition World-space sun position
|
* @param sunPosition World-space sun position
|
||||||
* @param timeOfDay Current time (0-24 hours)
|
* @param timeOfDay Current time (0-24 hours)
|
||||||
|
* @param fogDensity Fog density 0-1 (attenuates flare)
|
||||||
|
* @param cloudDensity Cloud density 0-1 (attenuates flare)
|
||||||
|
* @param weatherIntensity Weather intensity 0-1 (rain/snow attenuates flare)
|
||||||
*/
|
*/
|
||||||
void render(VkCommandBuffer cmd, const Camera& camera, const glm::vec3& sunPosition, float timeOfDay);
|
void render(VkCommandBuffer cmd, const Camera& camera, const glm::vec3& sunPosition,
|
||||||
|
float timeOfDay, float fogDensity = 0.0f, float cloudDensity = 0.0f,
|
||||||
|
float weatherIntensity = 0.0f);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enable or disable lens flare rendering
|
* @brief Enable or disable lens flare rendering
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ struct LightingParams {
|
||||||
glm::vec3 skyBand1Color{0.9f, 0.95f, 1.0f}; // Sky band 1
|
glm::vec3 skyBand1Color{0.9f, 0.95f, 1.0f}; // Sky band 1
|
||||||
glm::vec3 skyBand2Color{1.0f, 0.98f, 0.9f}; // Sky band 2
|
glm::vec3 skyBand2Color{1.0f, 0.98f, 0.9f}; // Sky band 2
|
||||||
|
|
||||||
float cloudDensity = 1.0f; // Cloud density/opacity
|
float cloudDensity = 0.3f; // Cloud density/opacity
|
||||||
float horizonGlow = 0.3f; // Horizon glow intensity
|
float horizonGlow = 0.3f; // Horizon glow intensity
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,10 @@ struct SkyParams {
|
||||||
glm::vec3 skyBand2Color{1.0f, 0.98f, 0.9f};
|
glm::vec3 skyBand2Color{1.0f, 0.98f, 0.9f};
|
||||||
|
|
||||||
// Atmospheric effects
|
// Atmospheric effects
|
||||||
float cloudDensity = 0.0f; // 0-1
|
float cloudDensity = 0.0f; // 0-1
|
||||||
float fogDensity = 0.0f; // 0-1
|
float fogDensity = 0.0f; // 0-1
|
||||||
float horizonGlow = 0.3f; // 0-1
|
float horizonGlow = 0.3f; // 0-1
|
||||||
|
float weatherIntensity = 0.0f; // 0-1 (rain/snow intensity, attenuates lens flare)
|
||||||
|
|
||||||
// Time
|
// Time
|
||||||
float timeOfDay = 12.0f; // 0-24 hours
|
float timeOfDay = 12.0f; // 0-24 hours
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ namespace wowee {
|
||||||
namespace rendering {
|
namespace rendering {
|
||||||
|
|
||||||
class VkContext;
|
class VkContext;
|
||||||
|
struct SkyParams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skybox renderer
|
* Skybox renderer
|
||||||
|
|
@ -16,6 +17,9 @@ class VkContext;
|
||||||
* Renders an atmospheric sky gradient using a fullscreen triangle.
|
* Renders an atmospheric sky gradient using a fullscreen triangle.
|
||||||
* No vertex buffer: 3 vertices cover the entire screen via gl_VertexIndex.
|
* No vertex buffer: 3 vertices cover the entire screen via gl_VertexIndex.
|
||||||
* World-space ray direction is reconstructed from the inverse view+projection.
|
* World-space ray direction is reconstructed from the inverse view+projection.
|
||||||
|
*
|
||||||
|
* Sky colors are driven by DBC data (Light.dbc / LightIntBand.dbc) via SkyParams,
|
||||||
|
* with Rayleigh/Mie atmospheric scattering for realistic appearance.
|
||||||
*/
|
*/
|
||||||
class Skybox {
|
class Skybox {
|
||||||
public:
|
public:
|
||||||
|
|
@ -27,12 +31,12 @@ public:
|
||||||
void recreatePipelines();
|
void recreatePipelines();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the skybox
|
* Render the skybox using DBC-driven sky colors.
|
||||||
* @param cmd Command buffer to record into
|
* @param cmd Command buffer to record into
|
||||||
* @param perFrameSet Per-frame descriptor set (set 0, contains camera UBO)
|
* @param perFrameSet Per-frame descriptor set (set 0, contains camera UBO)
|
||||||
* @param timeOfDay Time of day in hours (0-24), affects sky color
|
* @param params Sky parameters with DBC colors and sun direction
|
||||||
*/
|
*/
|
||||||
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, float timeOfDay = 12.0f);
|
void render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const SkyParams& params);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable/disable skybox rendering
|
* Enable/disable skybox rendering
|
||||||
|
|
@ -58,15 +62,7 @@ public:
|
||||||
*/
|
*/
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get horizon color for fog (public for fog system)
|
|
||||||
*/
|
|
||||||
glm::vec3 getHorizonColor(float time) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
glm::vec3 getSkyColor(float altitude, float time) const;
|
|
||||||
glm::vec3 getZenithColor(float time) const;
|
|
||||||
|
|
||||||
VkContext* vkCtx = nullptr;
|
VkContext* vkCtx = nullptr;
|
||||||
|
|
||||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <vk_mem_alloc.h>
|
#include <vk_mem_alloc.h>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
namespace rendering {
|
namespace rendering {
|
||||||
|
|
@ -79,6 +80,35 @@ public:
|
||||||
*/
|
*/
|
||||||
int getParticleCount() const;
|
int getParticleCount() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Zone weather configuration
|
||||||
|
* Provides default weather per zone for single-player mode.
|
||||||
|
* When connected to a server, SMSG_WEATHER overrides these.
|
||||||
|
*/
|
||||||
|
struct ZoneWeather {
|
||||||
|
Type type = Type::NONE;
|
||||||
|
float minIntensity = 0.0f; // Min intensity (varies over time)
|
||||||
|
float maxIntensity = 0.0f; // Max intensity
|
||||||
|
float probability = 0.0f; // Chance of weather being active (0-1)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set weather for a zone (used for zone-based weather configuration)
|
||||||
|
*/
|
||||||
|
void setZoneWeather(uint32_t zoneId, Type type, float minIntensity, float maxIntensity, float probability);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update weather based on current zone (single-player mode)
|
||||||
|
* @param zoneId Current zone ID
|
||||||
|
* @param deltaTime Time since last frame
|
||||||
|
*/
|
||||||
|
void updateZoneWeather(uint32_t zoneId, float deltaTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize default zone weather table
|
||||||
|
*/
|
||||||
|
void initializeZoneWeatherDefaults();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clean up Vulkan resources
|
* @brief Clean up Vulkan resources
|
||||||
*/
|
*/
|
||||||
|
|
@ -120,6 +150,15 @@ private:
|
||||||
static constexpr int MAX_PARTICLES = 2000;
|
static constexpr int MAX_PARTICLES = 2000;
|
||||||
static constexpr float SPAWN_VOLUME_SIZE = 100.0f; // Size of spawn area around camera
|
static constexpr float SPAWN_VOLUME_SIZE = 100.0f; // Size of spawn area around camera
|
||||||
static constexpr float SPAWN_HEIGHT = 80.0f; // Height above camera to spawn
|
static constexpr float SPAWN_HEIGHT = 80.0f; // Height above camera to spawn
|
||||||
|
|
||||||
|
// Zone-based weather
|
||||||
|
std::unordered_map<uint32_t, ZoneWeather> zoneWeatherTable_;
|
||||||
|
uint32_t currentWeatherZone_ = 0;
|
||||||
|
float zoneWeatherTimer_ = 0.0f; // Time accumulator for weather cycling
|
||||||
|
float zoneWeatherCycleDuration_ = 0.0f; // Current cycle length
|
||||||
|
bool zoneWeatherActive_ = false; // Is zone weather currently active?
|
||||||
|
float targetIntensity_ = 0.0f; // Target intensity for smooth transitions
|
||||||
|
bool zoneWeatherInitialized_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rendering
|
} // namespace rendering
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "rendering/clouds.hpp"
|
#include "rendering/clouds.hpp"
|
||||||
|
#include "rendering/sky_system.hpp"
|
||||||
#include "rendering/vk_context.hpp"
|
#include "rendering/vk_context.hpp"
|
||||||
#include "rendering/vk_shader.hpp"
|
#include "rendering/vk_shader.hpp"
|
||||||
#include "rendering/vk_pipeline.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);
|
VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||||
|
|
||||||
// ------------------------------------------------------------------ push constants
|
// ------------------------------------------------------------------ push constants
|
||||||
// Fragment-only push: vec4 cloudColor + float density + float windOffset = 24 bytes
|
// Fragment-only push: 3 x vec4 = 48 bytes
|
||||||
VkPushConstantRange pushRange{};
|
VkPushConstantRange pushRange{};
|
||||||
pushRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
pushRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
pushRange.offset = 0;
|
pushRange.offset = 0;
|
||||||
pushRange.size = sizeof(CloudPush); // 24 bytes
|
pushRange.size = sizeof(CloudPush); // 48 bytes
|
||||||
|
|
||||||
// ------------------------------------------------------------------ pipeline layout
|
// ------------------------------------------------------------------ pipeline layout
|
||||||
pipelineLayout_ = createPipelineLayout(device, {perFrameLayout}, {pushRange});
|
pipelineLayout_ = createPipelineLayout(device, {perFrameLayout}, {pushRange});
|
||||||
|
|
@ -54,10 +55,9 @@ bool Clouds::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------ vertex input
|
// ------------------------------------------------------------------ vertex input
|
||||||
// Vertex: vec3 pos only, stride = 12 bytes
|
|
||||||
VkVertexInputBindingDescription binding{};
|
VkVertexInputBindingDescription binding{};
|
||||||
binding.binding = 0;
|
binding.binding = 0;
|
||||||
binding.stride = sizeof(glm::vec3); // 12 bytes
|
binding.stride = sizeof(glm::vec3);
|
||||||
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||||
|
|
||||||
VkVertexInputAttributeDescription posAttr{};
|
VkVertexInputAttributeDescription posAttr{};
|
||||||
|
|
@ -77,7 +77,7 @@ bool Clouds::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout) {
|
||||||
.setVertexInput({binding}, {posAttr})
|
.setVertexInput({binding}, {posAttr})
|
||||||
.setTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
|
.setTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
|
||||||
.setRasterization(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE)
|
.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())
|
.setColorBlendAttachment(PipelineBuilder::blendAlpha())
|
||||||
.setMultisample(vkCtx_->getMsaaSamples())
|
.setMultisample(vkCtx_->getMsaaSamples())
|
||||||
.setLayout(pipelineLayout_)
|
.setLayout(pipelineLayout_)
|
||||||
|
|
@ -122,7 +122,6 @@ void Clouds::recreatePipelines() {
|
||||||
VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT);
|
VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT);
|
||||||
VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT);
|
VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||||
|
|
||||||
// Vertex input (same as initialize)
|
|
||||||
VkVertexInputBindingDescription binding{};
|
VkVertexInputBindingDescription binding{};
|
||||||
binding.binding = 0;
|
binding.binding = 0;
|
||||||
binding.stride = sizeof(glm::vec3);
|
binding.stride = sizeof(glm::vec3);
|
||||||
|
|
@ -182,25 +181,35 @@ void Clouds::shutdown() {
|
||||||
// Render
|
// 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) {
|
if (!enabled_ || pipeline_ == VK_NULL_HANDLE) {
|
||||||
return;
|
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{};
|
CloudPush push{};
|
||||||
push.cloudColor = glm::vec4(color, 1.0f);
|
push.cloudColor = glm::vec4(cloudBaseColor, 1.0f);
|
||||||
push.density = density_;
|
push.sunDirDensity = glm::vec4(sunDir, density_);
|
||||||
push.windOffset = windOffset_;
|
push.windAndLight = glm::vec4(windOffset_, sunIntensity, ambient, 0.0f);
|
||||||
|
|
||||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_);
|
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_,
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_,
|
||||||
0, 1, &perFrameSet, 0, nullptr);
|
0, 1, &perFrameSet, 0, nullptr);
|
||||||
|
|
||||||
// Push cloud params to fragment shader
|
|
||||||
vkCmdPushConstants(cmd, pipelineLayout_,
|
vkCmdPushConstants(cmd, pipelineLayout_,
|
||||||
VK_SHADER_STAGE_FRAGMENT_BIT,
|
VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
0, sizeof(push), &push);
|
0, sizeof(push), &push);
|
||||||
|
|
@ -223,29 +232,6 @@ void Clouds::update(float deltaTime) {
|
||||||
windOffset_ += deltaTime * windSpeed_ * 0.05f; // Slow drift
|
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
|
// Density setter
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -265,7 +251,7 @@ void Clouds::generateMesh() {
|
||||||
// Upper hemisphere — Z-up world: altitude goes into Z, horizontal spread in X/Y
|
// Upper hemisphere — Z-up world: altitude goes into Z, horizontal spread in X/Y
|
||||||
for (int ring = 0; ring <= RINGS; ++ring) {
|
for (int ring = 0; ring <= RINGS; ++ring) {
|
||||||
float phi = (ring / static_cast<float>(RINGS)) * (static_cast<float>(M_PI) * 0.5f);
|
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);
|
float ringRadius = RADIUS * std::sin(phi);
|
||||||
|
|
||||||
for (int seg = 0; seg <= SEGMENTS; ++seg) {
|
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});
|
flareElements.push_back({0.0f, 0.3f, glm::vec3(1.0f, 0.95f, 0.8f), 0.8f});
|
||||||
|
|
||||||
// Flare ghosts along sun-to-center axis
|
// Flare ghosts along sun-to-center axis
|
||||||
// These appear at various positions between sun and opposite side
|
|
||||||
|
|
||||||
// Bright white ghost near sun
|
// Bright white ghost near sun
|
||||||
flareElements.push_back({0.2f, 0.08f, glm::vec3(1.0f, 1.0f, 1.0f), 0.5f});
|
flareElements.push_back({0.2f, 0.08f, glm::vec3(1.0f, 1.0f, 1.0f), 0.5f});
|
||||||
|
|
||||||
// Blue-tinted ghost
|
// 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
|
// 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)
|
// Warm amber ghost (replaced oversaturated green)
|
||||||
flareElements.push_back({0.8f, 0.12f, glm::vec3(0.4f, 1.0f, 0.5f), 0.3f});
|
flareElements.push_back({0.8f, 0.10f, glm::vec3(0.9f, 0.75f, 0.5f), 0.2f});
|
||||||
|
|
||||||
// Large halo on opposite side
|
// 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
|
// Faint blue ghost far from sun
|
||||||
flareElements.push_back({-0.8f, 0.1f, glm::vec3(0.8f, 0.4f, 1.0f), 0.25f});
|
flareElements.push_back({-0.8f, 0.08f, glm::vec3(0.6f, 0.5f, 0.9f), 0.15f});
|
||||||
|
|
||||||
// Small red ghost
|
// Small warm ghost
|
||||||
flareElements.push_back({-1.2f, 0.06f, glm::vec3(1.0f, 0.3f, 0.3f), 0.3f});
|
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 {
|
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;
|
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) {
|
if (!enabled || pipeline == VK_NULL_HANDLE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -312,6 +313,16 @@ void LensFlare::render(VkCommandBuffer cmd, const Camera& camera, const glm::vec
|
||||||
return;
|
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
|
// Get sun screen position
|
||||||
glm::vec2 sunScreen = worldToScreen(camera, anchoredSunPos);
|
glm::vec2 sunScreen = worldToScreen(camera, anchoredSunPos);
|
||||||
glm::vec2 screenCenter(0.0f, 0.0f);
|
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
|
// Calculate position along sun-to-center axis
|
||||||
glm::vec2 position = sunScreen + sunToCenter * element.position;
|
glm::vec2 position = sunScreen + sunToCenter * element.position;
|
||||||
|
|
||||||
// Apply visibility and intensity
|
// Apply visibility, intensity, and atmospheric attenuation
|
||||||
float brightness = element.brightness * visibility * intensityMultiplier;
|
float brightness = element.brightness * visibility * intensityMultiplier * atmosphericFactor;
|
||||||
|
|
||||||
// Set push constants
|
// Set push constants
|
||||||
FlarePushConstants push{};
|
FlarePushConstants push{};
|
||||||
|
|
|
||||||
|
|
@ -2417,10 +2417,21 @@ void Renderer::update(float deltaTime) {
|
||||||
if (weather && gh) {
|
if (weather && gh) {
|
||||||
uint32_t wType = gh->getWeatherType();
|
uint32_t wType = gh->getWeatherType();
|
||||||
float wInt = gh->getWeatherIntensity();
|
float wInt = gh->getWeatherIntensity();
|
||||||
if (wType == 1) weather->setWeatherType(Weather::Type::RAIN);
|
if (wType != 0) {
|
||||||
else if (wType == 2) weather->setWeatherType(Weather::Type::SNOW);
|
// Server-driven weather (SMSG_WEATHER) — authoritative
|
||||||
else weather->setWeatherType(Weather::Type::NONE);
|
if (wType == 1) weather->setWeatherType(Weather::Type::RAIN);
|
||||||
weather->setIntensity(wInt);
|
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();
|
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;
|
skyParams.horizonGlow = lighting.horizonGlow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Weather attenuation for lens flare
|
||||||
|
if (gameHandler) {
|
||||||
|
skyParams.weatherIntensity = gameHandler->getWeatherIntensity();
|
||||||
|
}
|
||||||
|
|
||||||
skyParams.skyboxModelId = 0;
|
skyParams.skyboxModelId = 0;
|
||||||
skyParams.skyboxHasStars = false;
|
skyParams.skyboxHasStars = false;
|
||||||
|
|
||||||
|
|
@ -3866,6 +3882,7 @@ void Renderer::renderReflectionPass() {
|
||||||
skyParams.fogDensity = lp.fogDensity;
|
skyParams.fogDensity = lp.fogDensity;
|
||||||
skyParams.horizonGlow = lp.horizonGlow;
|
skyParams.horizonGlow = lp.horizonGlow;
|
||||||
}
|
}
|
||||||
|
// weatherIntensity left at default 0 for reflection pass (no game handler in scope)
|
||||||
skySystem->render(currentCmd, reflPerFrameDescSet, *camera, skyParams);
|
skySystem->render(currentCmd, reflPerFrameDescSet, *camera, skyParams);
|
||||||
}
|
}
|
||||||
if (terrainRenderer && terrainEnabled) {
|
if (terrainRenderer && terrainEnabled) {
|
||||||
|
|
|
||||||
|
|
@ -105,9 +105,9 @@ void SkySystem::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Skybox (authoritative sky gradient) ---
|
// --- Skybox (authoritative sky gradient, DBC-driven colors) ---
|
||||||
if (skybox_) {
|
if (skybox_) {
|
||||||
skybox_->render(cmd, perFrameSet, params.timeOfDay);
|
skybox_->render(cmd, perFrameSet, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Procedural stars (debug / fallback) ---
|
// --- Procedural stars (debug / fallback) ---
|
||||||
|
|
@ -133,15 +133,17 @@ void SkySystem::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet,
|
||||||
¶ms.directionalDir, ¶ms.sunColor, params.gameTime);
|
¶ms.directionalDir, ¶ms.sunColor, params.gameTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Clouds ---
|
// --- Clouds (DBC-driven colors + sun lighting) ---
|
||||||
if (clouds_) {
|
if (clouds_) {
|
||||||
clouds_->render(cmd, perFrameSet, params.timeOfDay);
|
clouds_->render(cmd, perFrameSet, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Lens flare ---
|
// --- Lens flare (attenuated by atmosphere) ---
|
||||||
if (lensFlare_) {
|
if (lensFlare_) {
|
||||||
glm::vec3 sunPos = getSunPosition(params);
|
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/skybox.hpp"
|
||||||
|
#include "rendering/sky_system.hpp"
|
||||||
#include "rendering/vk_context.hpp"
|
#include "rendering/vk_context.hpp"
|
||||||
#include "rendering/vk_shader.hpp"
|
#include "rendering/vk_shader.hpp"
|
||||||
#include "rendering/vk_pipeline.hpp"
|
#include "rendering/vk_pipeline.hpp"
|
||||||
|
|
@ -10,6 +11,16 @@
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
namespace rendering {
|
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() = default;
|
||||||
|
|
||||||
Skybox::~Skybox() {
|
Skybox::~Skybox() {
|
||||||
|
|
@ -39,11 +50,11 @@ bool Skybox::initialize(VkContext* ctx, VkDescriptorSetLayout perFrameLayout) {
|
||||||
VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT);
|
VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT);
|
||||||
VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_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{};
|
VkPushConstantRange pushRange{};
|
||||||
pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
pushRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
pushRange.offset = 0;
|
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
|
// Create pipeline layout with perFrameLayout (set 0) + push constants
|
||||||
pipelineLayout = createPipelineLayout(device, {perFrameLayout}, {pushRange});
|
pipelineLayout = createPipelineLayout(device, {perFrameLayout}, {pushRange});
|
||||||
|
|
@ -148,24 +159,20 @@ void Skybox::shutdown() {
|
||||||
vkCtx = nullptr;
|
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) {
|
if (pipeline == VK_NULL_HANDLE || !renderingEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push constant data
|
// Compute sun direction from directionalDir (light points toward scene, sun is opposite)
|
||||||
struct SkyPushConstants {
|
glm::vec3 sunDir = -glm::normalize(params.directionalDir);
|
||||||
glm::vec4 horizonColor;
|
|
||||||
glm::vec4 zenithColor;
|
|
||||||
float timeOfDay;
|
|
||||||
};
|
|
||||||
|
|
||||||
SkyPushConstants push{};
|
SkyPushConstants push{};
|
||||||
glm::vec3 horizon = getHorizonColor(time);
|
push.zenithColor = glm::vec4(params.skyTopColor, 1.0f);
|
||||||
glm::vec3 zenith = getZenithColor(time);
|
push.midColor = glm::vec4(params.skyMiddleColor, 1.0f);
|
||||||
push.horizonColor = glm::vec4(horizon, 1.0f);
|
push.horizonColor = glm::vec4(params.skyBand1Color, 1.0f);
|
||||||
push.zenithColor = glm::vec4(zenith, 1.0f);
|
push.fogColor = glm::vec4(params.skyBand2Color, 1.0f);
|
||||||
push.timeOfDay = time;
|
push.sunDirAndTime = glm::vec4(sunDir, params.timeOfDay);
|
||||||
|
|
||||||
// Bind pipeline
|
// Bind pipeline
|
||||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
|
@ -202,103 +209,5 @@ void Skybox::setTimeOfDay(float time) {
|
||||||
timeOfDay = 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 rendering
|
||||||
} // namespace wowee
|
} // namespace wowee
|
||||||
|
|
|
||||||
|
|
@ -369,5 +369,134 @@ void Weather::shutdown() {
|
||||||
particlePositions.clear();
|
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 rendering
|
||||||
} // namespace wowee
|
} // namespace wowee
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue