1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

add shooting stars

This commit is contained in:
DragonWrangler1 2025-06-08 16:43:17 -05:00
parent aba2b6638e
commit af845e3fbf
8 changed files with 419 additions and 1 deletions

View file

@ -241,3 +241,48 @@ core.register_chatcommand("set_saturation", {
core.get_player_by_name(player_name):set_lighting({saturation = saturation }) core.get_player_by_name(player_name):set_lighting({saturation = saturation })
end end
}) })
local stars_enabled = {}
minetest.register_chatcommand("toggle_stars", {
description = "Toggle custom shooting stars",
privs = { interact = true },
func = function(name)
local player = minetest.get_player_by_name(name)
if not player then
return false, "Player not found."
end
if stars_enabled[name] then
-- Disable shooting stars
player:set_stars({
shooting_stars_enabled = false,
visible = false
})
stars_enabled[name] = false
return true, "Shooting stars disabled."
else
-- Enable shooting stars with custom settings
player:set_stars({
visible = true,
count = 1000,
scale = 1.0,
day_opacity = 0.0,
shooting_stars_enabled = true,
shooting_star_chance = 100.0,
shooting_star_speed = 0.5,
shooting_star_size = 1.0,
shooting_star_colors = {
"#FFFFFF",
"#B4DCFF",
"#FFE0B4",
"#B4FFD0",
"#FFB4B4"
}
})
stars_enabled[name] = true
return true, "Shooting stars enabled!"
end
end
})

View file

@ -2948,6 +2948,14 @@ void Game::handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam
sky->setStarColor(event->star_params->starcolor); sky->setStarColor(event->star_params->starcolor);
sky->setStarScale(event->star_params->scale); sky->setStarScale(event->star_params->scale);
sky->setStarDayOpacity(event->star_params->day_opacity); sky->setStarDayOpacity(event->star_params->day_opacity);
// Set shooting star parameters
sky->setShootingStarsEnabled(event->star_params->shooting_stars_enabled);
sky->setShootingStarChance(event->star_params->shooting_star_chance);
sky->setShootingStarSpeed(event->star_params->shooting_star_speed);
sky->setShootingStarSize(event->star_params->shooting_star_size);
sky->setShootingStarColors(event->star_params->shooting_star_colors);
delete event->star_params; delete event->star_params;
} }

View file

@ -230,10 +230,16 @@ void Sky::render()
} }
} }
float dtime = m_last_update_time > 0.0f ? m_last_update_time : 0.016f;
// Draw stars before sun and moon to be behind them // Draw stars before sun and moon to be behind them
if (m_star_params.visible) if (m_star_params.visible)
draw_stars(driver, wicked_time_of_day); draw_stars(driver, wicked_time_of_day);
if (m_star_params.shooting_stars_enabled && m_star_params.visible) {
draw_shooting_stars(driver, wicked_time_of_day, dtime);
};
// Draw sunrise/sunset horizon glow texture // Draw sunrise/sunset horizon glow texture
// (textures/base/pack/sunrisebg.png) // (textures/base/pack/sunrisebg.png)
if (m_sun_params.sunrise_visible) { if (m_sun_params.sunrise_visible) {
@ -897,3 +903,276 @@ float getWickedTimeOfDay(float time_of_day)
wicked_time_of_day = 1.0f - ((1.0f - time_of_day) / wn * 0.25f); wicked_time_of_day = 1.0f - ((1.0f - time_of_day) / wn * 0.25f);
return wicked_time_of_day; return wicked_time_of_day;
} }
void Sky::draw_shooting_stars(video::IVideoDriver *driver, float wicked_time_of_day, float dtime)
{
// Only spawn shooting stars at night
float tod = wicked_time_of_day < 0.5f ? wicked_time_of_day : (1.0f - wicked_time_of_day);
if (tod > 0.2f)
return;
// Track shooting stars between frames
static bool star_active[5] = {false, false, false, false, false};
static float star_lifetime[5] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
static float star_progress[5] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
static v3f star_direction[5];
static float star_angle1[5], star_angle2[5];
static video::SColor star_color[5];
static float star_size[5];
static std::vector<v3f> tail_positions[5];
// Maximum lifetime of a shooting star in seconds
const float MAX_STAR_LIFETIME = 3.5f; // Increased from 2.0f to 3.5f for longer duration
// Count active stars
int active_stars = 0;
for (int s = 0; s < 5; s++) {
if (star_active[s])
active_stars++;
}
// Try to spawn a new star if we haven't reached the limit
if (active_stars < 5) {
// Find an inactive slot
int new_star_index = -1;
for (int s = 0; s < 5; s++) {
if (!star_active[s]) {
new_star_index = s;
break;
}
}
// Chance to spawn a new shooting star - reduced for rarity
float spawn_chance = m_star_params.shooting_star_chance * dtime * 0.02f;
if (new_star_index >= 0 && myrand_float() < spawn_chance) {
// Initialize a new shooting star
star_active[new_star_index] = true;
star_lifetime[new_star_index] = 0.0f;
star_progress[new_star_index] = 0.0f;
// Random position and direction - allow for full sky coverage
// Generate random angles for position in the sky hemisphere
star_angle1[new_star_index] = myrand_range(0.0f, 360.0f); // Horizontal angle (full 360 degrees)
star_angle2[new_star_index] = myrand_range(10.0f, 80.0f); // Vertical angle (avoid too close to horizon or zenith)
// Direction vector - more horizontal for a streak effect
// Use a different direction than just the default south-facing one
float dir_angle1 = myrand_range(0.0f, 360.0f); // Random direction in 360 degrees
float dir_angle2 = myrand_range(20.0f, 60.0f); // Angle from horizontal plane
star_direction[new_star_index] = v3f(
cos(dir_angle1 * M_PI / 180.0f) * sin(dir_angle2 * M_PI / 180.0f),
cos(dir_angle2 * M_PI / 180.0f) * 0.05f, // Small vertical component
sin(dir_angle1 * M_PI / 180.0f) * sin(dir_angle2 * M_PI / 180.0f)
);
// Size of the shooting star - use the size parameter directly
// Make sure it has a reasonable default if not set or out of range (0.5 to 2.0)
float size_param = m_star_params.shooting_star_size;
if (size_param < 0.5f || size_param > 2.0f) {
size_param = 1.0f;
}
star_size[new_star_index] = 0.0025f * size_param;
// Brightness variation - some stars will be fainter
float brightness = myrand_range(0.3f, 1.0f); // Random brightness between 30% and 100%
// Initial color - randomly choose one color for the entire lifecycle
if (!m_star_params.shooting_star_colors.empty()) {
int color_index = myrand_range(0, m_star_params.shooting_star_colors.size() - 1);
video::SColor base_color = m_star_params.shooting_star_colors[color_index];
// Apply brightness variation
star_color[new_star_index] = video::SColor(
base_color.getAlpha() * brightness,
base_color.getRed() * brightness,
base_color.getGreen() * brightness,
base_color.getBlue() * brightness
);
} else {
// Fallback to white if no colors defined
star_color[new_star_index] = video::SColor(255 * brightness,
255 * brightness, 255 * brightness, 255 * brightness);
}
// Clear tail positions
tail_positions[new_star_index].clear();
}
}
// Update and draw active stars
static const u16 indices[] = {0, 1, 2, 0, 2, 3};
for (int s = 0; s < 5; s++) {
if (star_active[s]) {
// Update existing shooting star
star_lifetime[s] += dtime;
// Progress from 0.0 to 1.0 over the lifetime
star_progress[s] = star_lifetime[s] / MAX_STAR_LIFETIME;
// End the shooting star when it completes its journey
if (star_lifetime[s] >= MAX_STAR_LIFETIME) {
star_active[s] = false;
continue;
}
// Calculate fade-out effect near the end of lifetime
float alpha_multiplier = 1.0f;
if (star_progress[s] > 0.8f) {
// Start fading out at 80% of lifetime
alpha_multiplier = 1.0f - ((star_progress[s] - 0.8f) * 5.0f); // Linear fade from 1.0 to 0.0
}
// Apply fade-out to star color
video::SColor current_color = star_color[s];
current_color.setAlpha(current_color.getAlpha() * alpha_multiplier);
// Calculate current position based on progress
// For a straight path, we'll use linear interpolation between start and end points
// Starting position (at progress = 0)
std::array<video::S3DVertex, 4> start_vertices;
draw_sky_body(start_vertices, -star_size[s], star_size[s], star_color[s]);
place_sky_body(start_vertices, star_angle2[s], star_angle1[s]);
// Ending position (at progress = 1)
std::array<video::S3DVertex, 4> end_vertices;
draw_sky_body(end_vertices, -star_size[s], star_size[s], star_color[s]);
place_sky_body(end_vertices, star_angle2[s], star_angle1[s] + 120.0f);
// Current position by linear interpolation
std::array<video::S3DVertex, 4> vertices;
for (int i = 0; i < 4; i++) {
vertices[i].Pos = start_vertices[i].Pos + (end_vertices[i].Pos - start_vertices[i].Pos) * star_progress[s];
vertices[i].Color = current_color;
}
// Store current head position for tail
v3f current_pos = (vertices[0].Pos + vertices[1].Pos + vertices[2].Pos + vertices[3].Pos) / 4.0f;
tail_positions[s].insert(tail_positions[s].begin(), current_pos);
// Limit tail length
const size_t MAX_TAIL_POINTS = 30;
if (tail_positions[s].size() > MAX_TAIL_POINTS)
tail_positions[s].resize(MAX_TAIL_POINTS);
// Draw tail as a connected ribbon with improved visuals
if (tail_positions[s].size() > 2) {
// Create a ribbon-like tail
std::vector<video::S3DVertex> tail_vertices;
std::vector<u16> tail_indices;
// Width of the tail at the head - based on star size and speed
float head_width = star_size[s] * 1.5f;
// Create orthogonal vector for width
v3f forward = star_direction[s];
forward.normalize();
v3f up(0, 1, 0);
v3f right = forward.crossProduct(up);
right.normalize();
// For each point in the tail
for (size_t i = 0; i < tail_positions[s].size(); i++) {
// Calculate width at this point (tapers off)
float width_factor = pow(1.0f - (float)i / tail_positions[s].size(), 0.7f); // Non-linear tapering
float point_width = head_width * width_factor;
// Calculate alpha at this point (fades out)
float alpha_factor = pow(1.0f - (float)i / tail_positions[s].size(), 0.5f); // Non-linear fading
u32 alpha = 255 * alpha_factor * alpha_multiplier;
// Create a gradient effect along the tail
video::SColor point_color = star_color[s];
// Adjust color for a more fiery/comet-like appearance
if (i > 0) {
float t = (float)i / tail_positions[s].size();
// Shift color towards yellow/orange for middle of tail
if (t < 0.5f) {
float blend = t * 2.0f;
point_color.setRed(point_color.getRed() + (255 - point_color.getRed()) * blend * 0.7f);
point_color.setGreen(point_color.getGreen() + (200 - point_color.getGreen()) * blend * 0.5f);
}
// Shift color towards red/dark for end of tail
else {
float blend = (t - 0.5f) * 2.0f;
point_color.setRed(point_color.getRed() * (1.0f - blend * 0.5f));
point_color.setGreen(point_color.getGreen() * (1.0f - blend * 0.7f));
point_color.setBlue(point_color.getBlue() * (1.0f - blend * 0.9f));
}
}
point_color.setAlpha(alpha);
// Add two vertices for this point (left and right sides of ribbon)
video::S3DVertex v1, v2;
v1.Pos = tail_positions[s][i] + (right * point_width);
v2.Pos = tail_positions[s][i] - (right * point_width);
v1.Color = v2.Color = point_color;
tail_vertices.push_back(v1);
tail_vertices.push_back(v2);
// Add indices to form triangles (except for the first point)
if (i > 0) {
size_t base = (i - 1) * 2;
// First triangle
tail_indices.push_back(base);
tail_indices.push_back(base + 1);
tail_indices.push_back(base + 2);
// Second triangle
tail_indices.push_back(base + 1);
tail_indices.push_back(base + 3);
tail_indices.push_back(base + 2);
}
}
// Set material for smooth rendering with additive blending
video::SMaterial tail_material = m_materials[0];
// Fix for EMT_TRANSPARENT_ADD_COLOR not existing
tail_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
driver->setMaterial(tail_material);
// Draw the tail
if (!tail_vertices.empty() && !tail_indices.empty()) {
driver->drawIndexedTriangleList(
&tail_vertices[0], tail_vertices.size(),
&tail_indices[0], tail_indices.size() / 3
);
}
// Add a glow effect around the head of the shooting star
if (tail_positions[s].size() > 0) {
video::SMaterial glow_material = m_materials[0];
// Fix for EMT_TRANSPARENT_ADD_COLOR not existing
glow_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
driver->setMaterial(glow_material);
// Create a larger, semi-transparent quad for the glow
float glow_size = star_size[s] * 1.0f;
std::array<video::S3DVertex, 4> glow_vertices;
draw_sky_body(glow_vertices, -glow_size, glow_size, video::SColor(
100 * alpha_multiplier,
current_color.getRed(),
current_color.getGreen(),
current_color.getBlue()
));
// Position the glow at the head of the shooting star
for (int i = 0; i < 4; i++) {
glow_vertices[i].Pos = vertices[i].Pos;
}
driver->drawIndexedTriangleList(&glow_vertices[0], 4, indices, 2);
}
}
// Draw the star head AFTER the tail so it appears on top
driver->setMaterial(m_materials[0]);
driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2);
}
}
}

View file

@ -114,6 +114,12 @@ public:
void setFogStart(float fog_start) { m_sky_params.fog_start = fog_start; } void setFogStart(float fog_start) { m_sky_params.fog_start = fog_start; }
float getFogStart() const { return m_sky_params.fog_start; } float getFogStart() const { return m_sky_params.fog_start; }
void setShootingStarsEnabled(bool enabled) { m_star_params.shooting_stars_enabled = enabled; }
void setShootingStarChance(f32 chance) { m_star_params.shooting_star_chance = chance; }
void setShootingStarSpeed(f32 speed) { m_star_params.shooting_star_speed = speed; }
void setShootingStarSize(f32 size) { m_star_params.shooting_star_size = size; }
void setShootingStarColors(const std::vector<video::SColor> &colors) { m_star_params.shooting_star_colors = colors; }
void setFogColor(video::SColor v) { m_sky_params.fog_color = v; } void setFogColor(video::SColor v) { m_sky_params.fog_color = v; }
video::SColor getFogColor() const { video::SColor getFogColor() const {
if (m_sky_params.fog_color.getAlpha() > 0) if (m_sky_params.fog_color.getAlpha() > 0)
@ -215,8 +221,12 @@ private:
void draw_sky_body(std::array<video::S3DVertex, 4> &vertices, void draw_sky_body(std::array<video::S3DVertex, 4> &vertices,
float pos_1, float pos_2, const video::SColor &c); float pos_1, float pos_2, const video::SColor &c);
void draw_stars(video::IVideoDriver *driver, float wicked_time_of_day); void draw_stars(video::IVideoDriver *driver, float wicked_time_of_day);
void draw_shooting_stars(video::IVideoDriver *driver, float wicked_time_of_day, float dtime);
void place_sky_body(std::array<video::S3DVertex, 4> &vertices, void place_sky_body(std::array<video::S3DVertex, 4> &vertices,
float horizon_position, float day_position); float horizon_position, float day_position);
float m_last_update_time = 0.0f;
float dtime;
}; };
// calculates value for sky body positions for the given observed time of day // calculates value for sky body positions for the given observed time of day

View file

@ -1409,6 +1409,22 @@ void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
>> stars.starcolor >> stars.scale; >> stars.starcolor >> stars.scale;
try { try {
*pkt >> stars.day_opacity; *pkt >> stars.day_opacity;
*pkt >> stars.shooting_stars_enabled
>> stars.shooting_star_chance
>> stars.shooting_star_speed
>> stars.shooting_star_size;
// Read color count
u16 color_count;
*pkt >> color_count;
// Clear default colors and read new ones
stars.shooting_star_colors.clear();
for (u16 i = 0; i < color_count; i++) {
video::SColor color;
*pkt >> color;
stars.shooting_star_colors.push_back(color);
}
} catch (PacketError &e) {}; } catch (PacketError &e) {};
ClientEvent *event = new ClientEvent(); ClientEvent *event = new ClientEvent();

View file

@ -2435,6 +2435,31 @@ int ObjectRef::l_set_stars(lua_State *L)
"scale", star_params.scale); "scale", star_params.scale);
star_params.day_opacity = getfloatfield_default(L, 2, star_params.day_opacity = getfloatfield_default(L, 2,
"day_opacity", star_params.day_opacity); "day_opacity", star_params.day_opacity);
// Get shooting star parameters
star_params.shooting_stars_enabled = getboolfield_default(L, 2,
"shooting_stars_enabled", star_params.shooting_stars_enabled);
star_params.shooting_star_chance = getfloatfield_default(L, 2,
"shooting_star_chance", star_params.shooting_star_chance);
star_params.shooting_star_speed = getfloatfield_default(L, 2,
"shooting_star_speed", star_params.shooting_star_speed);
star_params.shooting_star_size = getfloatfield_default(L, 2,
"shooting_star_size", star_params.shooting_star_size);
// Get shooting star colors
lua_getfield(L, 2, "shooting_star_colors");
if (lua_istable(L, -1)) {
star_params.shooting_star_colors.clear();
int colors_table = lua_gettop(L);
lua_pushnil(L);
while (lua_next(L, colors_table) != 0) {
video::SColor color;
if (read_color(L, -1, &color))
star_params.shooting_star_colors.push_back(color);
lua_pop(L, 1);
}
}
lua_pop(L, 1);
} }
getServer(L)->setStars(player, star_params); getServer(L)->setStars(player, star_params);
@ -2463,6 +2488,12 @@ int ObjectRef::l_get_stars(lua_State *L)
lua_setfield(L, -2, "scale"); lua_setfield(L, -2, "scale");
lua_pushnumber(L, star_params.day_opacity); lua_pushnumber(L, star_params.day_opacity);
lua_setfield(L, -2, "day_opacity"); lua_setfield(L, -2, "day_opacity");
lua_pushboolean(L, star_params.shooting_stars_enabled);
lua_setfield(L, -2, "shooting_stars_enabled");
lua_pushnumber(L, star_params.shooting_star_chance);
lua_setfield(L, -2, "shooting_star_chance");
lua_pushnumber(L, star_params.shooting_star_speed);
lua_setfield(L, -2, "shooting_star_speed");
return 1; return 1;
} }

View file

@ -1910,7 +1910,20 @@ void Server::SendSetStars(session_t peer_id, const StarParams &params)
pkt << params.visible << params.count pkt << params.visible << params.count
<< params.starcolor << params.scale << params.starcolor << params.scale
<< params.day_opacity; << params.day_opacity
<< params.shooting_stars_enabled
<< params.shooting_star_chance
<< params.shooting_star_speed
<< params.shooting_star_size;
// Send the number of colors first
u16 color_count = params.shooting_star_colors.size();
pkt << color_count;
// Then send each color
for (const auto &color : params.shooting_star_colors) {
pkt << color;
}
Send(&pkt); Send(&pkt);
} }

View file

@ -65,6 +65,11 @@ struct StarParams
video::SColor starcolor; video::SColor starcolor;
f32 scale; f32 scale;
f32 day_opacity; f32 day_opacity;
bool shooting_stars_enabled;
f32 shooting_star_chance;
f32 shooting_star_speed;
f32 shooting_star_size;
std::vector<video::SColor> shooting_star_colors;
}; };
struct CloudParams struct CloudParams
@ -143,6 +148,17 @@ public:
stars.starcolor = video::SColor(105, 235, 235, 255); stars.starcolor = video::SColor(105, 235, 235, 255);
stars.scale = 1; stars.scale = 1;
stars.day_opacity = 0; stars.day_opacity = 0;
stars.shooting_stars_enabled = true;
stars.shooting_star_chance = 5.0f;
stars.shooting_star_speed = 0.1f;
stars.shooting_star_size = 1.0f;
stars.shooting_star_colors = {
video::SColor(255, 255, 255, 255), // White
video::SColor(255, 180, 220, 255), // Blue-ish
video::SColor(255, 255, 240, 180), // Yellow-ish
video::SColor(255, 180, 255, 200), // Green-ish
video::SColor(255, 255, 180, 180) // Red-ish
};
return stars; return stars;
} }