1
0
mirror of https://github.com/beestat/app.git synced 2026-04-12 20:22:14 -04:00
This commit is contained in:
Jon Ziebell 2026-02-14 16:36:44 -05:00
parent 6d780e67a6
commit e940451d0b
4 changed files with 563 additions and 10 deletions

View File

@ -83,8 +83,9 @@ beestat.setting = function(argument_1, opt_value, opt_callback) {
'visualize.three_d.show_labels': false,
'visualize.three_d.auto_rotate': false,
'visualize.three_d.show_walls': false,
'visualize.three_d.show_roof': false,
'visualize.three_d.show_environment': true,
'visualize.three_d.show_roof': false,
'visualize.three_d.show_environment': true,
'visualize.three_d.weather_mode': 'current',
'date_format': 'M/D/YYYY',

View File

@ -361,6 +361,7 @@ beestat.component.card.three_d.prototype.decorate_drawing_pane_ = function(paren
beestat.setting('visualize.floor_plan_id'),
this.get_data_()
);
this.apply_weather_setting_to_scene_();
this.scene_.addEventListener('change_active_room', function() {
self.update_hud_();
@ -472,6 +473,50 @@ beestat.component.card.three_d.prototype.get_show_environment_ = function() {
return true;
};
/**
* Get selected weather mode.
*
* @return {string}
*/
beestat.component.card.three_d.prototype.get_weather_mode_ = function() {
return beestat.setting('visualize.three_d.weather_mode') || 'current';
};
/**
* Map UI weather mode to scene weather effect.
*
* @param {string} weather_mode
*
* @return {string} none|cloudy|rain|snow
*/
beestat.component.card.three_d.prototype.get_weather_effect_from_mode_ = function(weather_mode) {
switch (weather_mode) {
case 'cloudy':
return 'cloudy';
case 'raining':
return 'rain';
case 'snowing':
return 'snow';
case 'current':
case 'sunny':
default:
// Placeholder mappings for now.
return 'none';
}
};
/**
* Apply current weather settings to the scene.
*/
beestat.component.card.three_d.prototype.apply_weather_setting_to_scene_ = function() {
if (this.scene_ === undefined) {
return;
}
const weather_effect = this.get_weather_effect_from_mode_(this.get_weather_mode_());
this.scene_.set_weather(weather_effect);
};
/**
* Set environment view state and mirror to legacy key for compatibility.
*
@ -497,6 +542,9 @@ beestat.component.card.three_d.prototype.apply_layer_visibility_ = function() {
}
const show_environment = this.get_show_environment_();
if (show_environment === false) {
this.weather_menu_open_ = false;
}
this.scene_.set_layer_visible('walls', show_environment);
this.scene_.set_layer_visible('roof', show_environment);
@ -1064,6 +1112,32 @@ beestat.component.card.three_d.prototype.decorate_toolbar_ = function(parent) {
})
);
// Weather controls (environment view only)
if (show_environment === true) {
const selected_mode = this.get_weather_mode_();
const weather_modes = [
{'value': 'current', 'icon': 'weather_partly_cloudy', 'title': 'Weather: Current'},
{'value': 'sunny', 'icon': 'weather_sunny', 'title': 'Weather: Sunny'},
{'value': 'cloudy', 'icon': 'weather_cloudy', 'title': 'Weather: Cloudy'},
{'value': 'raining', 'icon': 'weather_pouring', 'title': 'Weather: Raining'},
{'value': 'snowing', 'icon': 'weather_snowy', 'title': 'Weather: Snowing'}
];
const selected_weather_mode = weather_modes.find((mode) => mode.value === selected_mode) || weather_modes[0];
tile_group.add_tile(new beestat.component.tile()
.set_icon(selected_weather_mode.icon)
.set_title('Weather Menu')
.set_text_color(beestat.style.color.gray.light)
.set_background_color(this.weather_menu_open_ === true ? beestat.style.color.lightblue.base : beestat.style.color.bluegray.base)
.set_background_hover_color(this.weather_menu_open_ === true ? beestat.style.color.lightblue.light : beestat.style.color.bluegray.light)
.addEventListener('click', function(e) {
e.stopPropagation();
self.weather_menu_open_ = self.weather_menu_open_ !== true;
self.decorate_toolbar_();
})
);
}
// Labels (hidden while environment view is on)
if (show_environment === false) {
tile_group.add_tile(new beestat.component.tile()
@ -1087,6 +1161,56 @@ beestat.component.card.three_d.prototype.decorate_toolbar_ = function(parent) {
}
tile_group.render($(this.toolbar_container_));
if (show_environment === true && this.weather_menu_open_ === true) {
const weather_tile_element = this.toolbar_container_.querySelector('[title=\"Weather Menu\"]');
if (weather_tile_element !== null) {
const toolbar_rect = this.toolbar_container_.getBoundingClientRect();
const weather_tile_rect = weather_tile_element.getBoundingClientRect();
const selected_mode = this.get_weather_mode_();
const weather_modes = [
{'value': 'sunny', 'icon': 'weather_sunny', 'title': 'Weather: Sunny'},
{'value': 'cloudy', 'icon': 'weather_cloudy', 'title': 'Weather: Cloudy'},
{'value': 'raining', 'icon': 'weather_pouring', 'title': 'Weather: Raining'},
{'value': 'snowing', 'icon': 'weather_snowy', 'title': 'Weather: Snowing'}
];
const popup = document.createElement('div');
Object.assign(popup.style, {
'position': 'absolute',
'left': `${Math.round(weather_tile_rect.right - toolbar_rect.left + 6)}px`,
'top': `${Math.round(weather_tile_rect.top - toolbar_rect.top - 2)}px`,
'display': 'flex',
'flex-direction': 'row',
'align-items': 'center',
'grid-gap': '4px',
'padding': '2px'
});
this.toolbar_container_.appendChild(popup);
weather_modes.forEach((mode) => {
const is_selected = mode.value === selected_mode;
const tile = new beestat.component.tile()
.set_icon(mode.icon)
.set_title(mode.title)
.set_text_color(is_selected ? beestat.style.color.gray.dark : beestat.style.color.gray.light)
.set_background_color(is_selected ? beestat.style.color.bluegray.light : beestat.style.color.bluegray.base)
.set_background_hover_color(is_selected ? beestat.style.color.bluegray.light : beestat.style.color.bluegray.light);
if (is_selected === false) {
tile.addEventListener('click', (e) => {
e.stopPropagation();
beestat.setting('visualize.three_d.weather_mode', mode.value);
this.apply_weather_setting_to_scene_();
this.weather_menu_open_ = false;
this.decorate_toolbar_();
});
}
tile.render($(popup));
});
}
}
};
/**

View File

@ -226,6 +226,7 @@ beestat.component.modal.update_floor_plan.prototype.decorate_contents_ = functio
? self.state_.appearance.ground_color
: (floor_plan.data.appearance?.ground_color || '#4a7c3f');
ground_color_select.set_value(current_ground_color);
}
// Address

View File

@ -21,7 +21,7 @@ beestat.component.scene.layer_outline = 2;
beestat.component.scene.roof_pitch = 0.5; // Rise over run (0.5 = 6:12 pitch)
beestat.component.scene.roof_overhang = 12; // Roof overhang beyond walls
beestat.component.scene.wall_thickness = 4;
beestat.component.scene.environment_padding = 200; // Padding around floor plan
beestat.component.scene.environment_padding = 400; // Padding around floor plan
beestat.component.scene.room_floor_thickness = 6;
beestat.component.scene.room_wall_inset = 1.5;
@ -33,8 +33,10 @@ beestat.component.scene.default_appearance = {
'roof_color': '#3a3a3a',
'roof_style': 'hip',
'siding_color': '#889aaa',
'ground_color': '#4a7c3f'
'ground_color': '#4a7c3f',
'weather': 'none'
};
beestat.component.scene.snow_surface_color = '#f0f0f0';
/**
* Light intensity constants
@ -54,6 +56,12 @@ beestat.component.scene.prototype.rerender = function() {
this.add_main_group_();
this.add_floor_plan_();
this.apply_appearance_rotation_to_lights_();
// Ensure weather/date-driven celestial targets are recalculated after
// rerendered environment changes (e.g., cloudy/rain/snow dimming).
if (this.rendered_ === true) {
this.update_();
}
};
/**
@ -71,6 +79,57 @@ beestat.component.scene.prototype.get_appearance_value_ = function(key) {
return beestat.component.scene.default_appearance[key];
};
/**
* Set weather effect override for this scene instance.
*
* @param {string} weather none|rain|snow
*
* @return {beestat.component.scene}
*/
beestat.component.scene.prototype.set_weather = function(weather) {
this.weather_ = weather;
if (this.rendered_ === true) {
this.rerender();
}
return this;
};
/**
* Get the effective weather effect for this scene.
*
* @return {string}
*/
beestat.component.scene.prototype.get_weather_effect_ = function() {
if (this.weather_ !== undefined) {
return this.weather_;
}
const appearance_weather = this.get_appearance_value_('weather');
return appearance_weather || 'none';
};
/**
* Get effective surface colors, applying snow overrides when snow weather is
* active.
*
* @return {{ground_color: string, roof_color: string}}
*/
beestat.component.scene.prototype.get_surface_colors_ = function() {
if (this.get_weather_effect_() === 'snow') {
return {
'ground_color': beestat.component.scene.snow_surface_color,
'roof_color': beestat.component.scene.snow_surface_color
};
}
return {
'ground_color': this.get_appearance_value_('ground_color'),
'roof_color': this.get_appearance_value_('roof_color')
};
};
/**
* Set the width of this component.
*
@ -130,6 +189,7 @@ beestat.component.scene.prototype.decorate_ = function(parent) {
self.controls_.update();
self.update_raycaster_();
self.update_celestial_light_intensities_();
self.update_weather_();
self.renderer_.render(self.scene_, self.camera_);
};
animate();
@ -228,7 +288,7 @@ beestat.component.scene.prototype.add_controls_ = function(parent) {
this.controls_ = new THREE.OrbitControls(this.camera_, parent[0]);
this.controls_.enableDamping = true;
this.controls_.enablePan = false;
this.controls_.maxDistance = 2000;
this.controls_.maxDistance = 1500;
this.controls_.minDistance = 400;
this.controls_.maxPolarAngle = Math.PI / 2.1;
};
@ -416,6 +476,126 @@ beestat.component.scene.prototype.create_sun_glow_texture_ = function() {
return texture;
};
/**
* Create a circular particle texture for snow.
*
* @return {THREE.Texture}
*/
beestat.component.scene.prototype.create_snow_particle_texture_ = function() {
const size = 64;
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const context = canvas.getContext('2d');
const gradient = context.createRadialGradient(
size / 2,
size / 2,
0,
size / 2,
size / 2,
size / 2
);
gradient.addColorStop(0.0, 'rgba(255, 255, 255, 1.0)');
gradient.addColorStop(0.4, 'rgba(245, 250, 255, 0.9)');
gradient.addColorStop(1.0, 'rgba(240, 245, 255, 0.0)');
context.fillStyle = gradient;
context.fillRect(0, 0, size, size);
const texture = new THREE.CanvasTexture(canvas);
texture.needsUpdate = true;
return texture;
};
/**
* Create a streak-like particle texture for rain.
*
* @return {THREE.Texture}
*/
beestat.component.scene.prototype.create_rain_particle_texture_ = function() {
const width = 24;
const height = 64;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const context = canvas.getContext('2d');
const gradient = context.createLinearGradient(0, 0, 0, height);
gradient.addColorStop(0.0, 'rgba(170, 200, 255, 0.0)');
gradient.addColorStop(0.25, 'rgba(185, 210, 255, 0.85)');
gradient.addColorStop(1.0, 'rgba(170, 200, 255, 0.0)');
context.fillStyle = gradient;
context.fillRect(width / 2 - 2, 0, 4, height);
const texture = new THREE.CanvasTexture(canvas);
texture.needsUpdate = true;
return texture;
};
/**
* Create a soft cloud texture used for weather cloud sprites.
*
* @return {THREE.Texture}
*/
beestat.component.scene.prototype.create_cloud_texture_ = function() {
const size = 256;
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const context = canvas.getContext('2d');
const circles = [
{'x': 0.36, 'y': 0.56, 'r': 0.2},
{'x': 0.5, 'y': 0.5, 'r': 0.24},
{'x': 0.64, 'y': 0.56, 'r': 0.2},
{'x': 0.5, 'y': 0.64, 'r': 0.22}
];
circles.forEach(function(circle) {
const gradient = context.createRadialGradient(
size * circle.x,
size * circle.y,
0,
size * circle.x,
size * circle.y,
size * circle.r
);
gradient.addColorStop(0.0, 'rgba(255,255,255,0.9)');
gradient.addColorStop(0.55, 'rgba(240,245,255,0.55)');
gradient.addColorStop(1.0, 'rgba(240,245,255,0.0)');
context.fillStyle = gradient;
context.beginPath();
context.arc(size * circle.x, size * circle.y, size * circle.r, 0, Math.PI * 2);
context.fill();
});
const texture = new THREE.CanvasTexture(canvas);
texture.needsUpdate = true;
return texture;
};
/**
* Get dimming multiplier from weather for sun/moon brightness.
*
* @return {number}
*/
beestat.component.scene.prototype.get_cloud_dimming_factor_ = function() {
switch (this.get_weather_effect_()) {
case 'cloudy':
return 0.18;
case 'rain':
return 0.08;
case 'snow':
return 0.12;
default:
return 1;
}
};
/**
* Draw the moon phase into the reusable moon canvas texture.
*
@ -820,10 +1000,13 @@ beestat.component.scene.prototype.update_celestial_lights_ = function(date, lati
this.sun_visual_horizon_fade_ = Math.max(0, Math.min(1, (sun_pos.altitude + 0.15) / 0.3));
}
const cloud_dimming = this.get_cloud_dimming_factor_();
// Calculate target intensity for smooth transitions
this.target_sun_intensity_ = sun_pos.altitude < 0
? Math.max(0, beestat.component.scene.sun_light_intensity * (1 + sun_pos.altitude / (Math.PI / 6)))
: beestat.component.scene.sun_light_intensity;
this.target_sun_intensity_ *= cloud_dimming;
// Moon
const moon_pos = SunCalc.getMoonPosition(js_date, latitude, longitude);
@ -859,6 +1042,7 @@ beestat.component.scene.prototype.update_celestial_lights_ = function(date, lati
? Math.max(0, moon_intensity * (1 + moon_pos.altitude / (Math.PI / 6)))
: moon_intensity;
}
this.target_moon_intensity_ *= cloud_dimming;
// Update helpers
if (this.debug_.sun_light_helper) {
@ -1677,9 +1861,10 @@ beestat.component.scene.prototype.add_roofs_ = function() {
* Add hip roofs using the straight skeleton algorithm.
*/
beestat.component.scene.prototype.add_hip_roofs_ = function() {
const self = this;
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
const exposed_areas = this.compute_exposed_ceiling_areas_(floor_plan);
const surface_colors = this.get_surface_colors_();
const roof_color = surface_colors.roof_color;
// Create layer for roofs
const roofs_layer = new THREE.Group();
@ -1734,7 +1919,6 @@ beestat.component.scene.prototype.add_hip_roofs_ = function() {
'depth': hip_roof_base_thickness,
'bevelEnabled': false
});
const roof_color = self.get_appearance_value_('roof_color');
const base_material = new THREE.MeshStandardMaterial({
'color': roof_color,
'side': THREE.DoubleSide,
@ -1877,13 +2061,91 @@ beestat.component.scene.prototype.add_hip_roofs_ = function() {
});
};
/**
* Animate weather particles (snow/rain) each frame.
*/
beestat.component.scene.prototype.update_weather_ = function() {
if (this.cloud_sprites_ !== undefined && this.cloud_bounds_ !== undefined) {
const cloud_span_x = this.cloud_bounds_.max_x - this.cloud_bounds_.min_x;
const cloud_span_y = this.cloud_bounds_.max_y - this.cloud_bounds_.min_y;
for (let i = 0; i < this.cloud_sprites_.length; i++) {
const sprite = this.cloud_sprites_[i];
sprite.position.x += this.cloud_speeds_x_[i] * 0.016;
sprite.position.y += this.cloud_speeds_y_[i] * 0.016;
if (sprite.position.x > this.cloud_bounds_.max_x) {
sprite.position.x = this.cloud_bounds_.min_x;
} else if (sprite.position.x < this.cloud_bounds_.min_x) {
sprite.position.x = this.cloud_bounds_.max_x;
}
if (sprite.position.y > this.cloud_bounds_.max_y) {
sprite.position.y = this.cloud_bounds_.min_y;
} else if (sprite.position.y < this.cloud_bounds_.min_y) {
sprite.position.y = this.cloud_bounds_.max_y;
}
if (sprite.position.x === this.cloud_bounds_.min_x || sprite.position.x === this.cloud_bounds_.max_x) {
sprite.position.y = this.cloud_bounds_.min_y + Math.random() * cloud_span_y;
}
if (sprite.position.y === this.cloud_bounds_.min_y || sprite.position.y === this.cloud_bounds_.max_y) {
sprite.position.x = this.cloud_bounds_.min_x + Math.random() * cloud_span_x;
}
}
}
if (
this.weather_points_ === undefined ||
this.weather_particle_speeds_ === undefined ||
this.weather_bounds_ === undefined
) {
return;
}
const now_ms = window.performance.now();
if (this.weather_last_update_ms_ === undefined) {
this.weather_last_update_ms_ = now_ms;
return;
}
const delta_seconds = Math.min(0.05, (now_ms - this.weather_last_update_ms_) / 1000);
this.weather_last_update_ms_ = now_ms;
if (delta_seconds <= 0) {
return;
}
const positions = this.weather_points_.geometry.attributes.position.array;
const bounds = this.weather_bounds_;
const span_x = bounds.max_x - bounds.min_x;
const span_y = bounds.max_y - bounds.min_y;
const span_z = bounds.max_z - bounds.min_z;
for (let i = 0; i < this.weather_particle_speeds_.length; i++) {
const offset = i * 3;
positions[offset + 2] += this.weather_particle_speeds_[i] * delta_seconds;
positions[offset] += this.weather_particle_drift_x_[i] * delta_seconds;
positions[offset + 1] += this.weather_particle_drift_y_[i] * delta_seconds;
if (
positions[offset] < bounds.min_x || positions[offset] > bounds.max_x ||
positions[offset + 1] < bounds.min_y || positions[offset + 1] > bounds.max_y ||
positions[offset + 2] > bounds.max_z
) {
positions[offset] = bounds.min_x + Math.random() * span_x;
positions[offset + 1] = bounds.min_y + Math.random() * span_y;
positions[offset + 2] = bounds.min_z + Math.random() * span_z;
}
}
this.weather_points_.geometry.attributes.position.needsUpdate = true;
};
/**
* Add flat roofs to the scene.
*/
beestat.component.scene.prototype.add_flat_roofs_ = function() {
const self = this;
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
const exposed_areas = this.compute_exposed_ceiling_areas_(floor_plan);
const surface_colors = this.get_surface_colors_();
const roof_color = surface_colors.roof_color;
// Create layer for roofs
const roofs_layer = new THREE.Group();
@ -1939,7 +2201,6 @@ beestat.component.scene.prototype.add_flat_roofs_ = function() {
});
// Create material - use appearance roof color
const roof_color = self.get_appearance_value_('roof_color');
const material = new THREE.MeshStandardMaterial({
'color': roof_color,
'side': THREE.DoubleSide,
@ -2121,7 +2382,8 @@ beestat.component.scene.prototype.add_environment_ = function() {
let current_z = 0;
const padding = beestat.component.scene.environment_padding;
const ground_color = this.get_appearance_value_('ground_color');
const surface_colors = this.get_surface_colors_();
const ground_color = surface_colors.ground_color;
const strata = [
{'color': ground_color, 'thickness': 10, 'roughness': 0.95},
{'color': 0x4a3f35, 'thickness': 60, 'roughness': 0.85},
@ -2159,6 +2421,162 @@ beestat.component.scene.prototype.add_environment_ = function() {
// Add celestial lights (sun and moon) - toggled with environment visibility
this.add_celestial_lights_();
this.add_weather_effect_(center_x, center_y, plan_width, plan_height);
};
/**
* Add procedural weather particles based on floor plan appearance.
*
* @param {number} center_x
* @param {number} center_y
* @param {number} plan_width
* @param {number} plan_height
*/
beestat.component.scene.prototype.add_weather_effect_ = function(center_x, center_y, plan_width, plan_height) {
const weather = this.get_weather_effect_();
const has_clouds = weather === 'cloudy' || weather === 'rain' || weather === 'snow';
const has_precipitation = weather === 'rain' || weather === 'snow';
if (has_clouds === false && has_precipitation === false) {
return;
}
const padding = beestat.component.scene.environment_padding + 120;
const bounds = {
'min_x': center_x - ((plan_width + padding * 2) / 2),
'max_x': center_x + ((plan_width + padding * 2) / 2),
'min_y': center_y - ((plan_height + padding * 2) / 2),
'max_y': center_y + ((plan_height + padding * 2) / 2),
'min_z': -780,
'max_z': 140
};
const config = weather === 'snow'
? {
'count': 1500,
'size': 10,
'opacity': 0.75,
'color': 0xffffff,
'speed_min': 18,
'speed_max': 44,
'drift': 12
}
: {
'count': 2200,
'size': 11,
'opacity': 0.7,
'color': 0xa8c7ff,
'speed_min': 280,
'speed_max': 430,
'drift': 28
};
this.weather_group_ = new THREE.Group();
this.weather_group_.userData.is_environment = true;
this.environment_group_.add(this.weather_group_);
if (has_clouds === true) {
if (this.cloud_texture_ === undefined) {
this.cloud_texture_ = this.create_cloud_texture_();
}
// const cloud_count = weather === 'cloudy' ? 140 : 180;
const cloud_count = 140;
// const cloud_opacity = weather === 'cloudy' ? 0.55 : 0.62;
const cloud_opacity = 0.2;
const cloud_bounds = {
'min_x': bounds.min_x - 260,
'max_x': bounds.max_x + 260,
'min_y': bounds.min_y - 260,
'max_y': bounds.max_y + 260,
'z': -760
};
this.cloud_bounds_ = cloud_bounds;
this.cloud_sprites_ = [];
this.cloud_speeds_x_ = new Float32Array(cloud_count);
this.cloud_speeds_y_ = new Float32Array(cloud_count);
for (let i = 0; i < cloud_count; i++) {
const cloud_material = new THREE.SpriteMaterial({
'map': this.cloud_texture_,
'color': 0xdce3ee,
'transparent': true,
'opacity': cloud_opacity,
'depthWrite': false,
'depthTest': true
});
const cloud = new THREE.Sprite(cloud_material);
cloud.position.set(
cloud_bounds.min_x + Math.random() * (cloud_bounds.max_x - cloud_bounds.min_x),
cloud_bounds.min_y + Math.random() * (cloud_bounds.max_y - cloud_bounds.min_y),
cloud_bounds.z + (Math.random() * 130)
);
const cloud_size = 520 + Math.random() * 560;
cloud.scale.set(cloud_size, cloud_size * 0.6, 1);
cloud.layers.set(beestat.component.scene.layer_visible);
cloud.userData.is_environment = true;
this.weather_group_.add(cloud);
this.cloud_sprites_.push(cloud);
this.cloud_speeds_x_[i] = (Math.random() - 0.5) * 0.8;
this.cloud_speeds_y_[i] = (Math.random() - 0.5) * 0.4;
}
}
if (has_precipitation === false) {
return;
}
if (weather === 'snow' && this.snow_particle_texture_ === undefined) {
this.snow_particle_texture_ = this.create_snow_particle_texture_();
}
if (weather === 'rain' && this.rain_particle_texture_ === undefined) {
this.rain_particle_texture_ = this.create_rain_particle_texture_();
}
const positions = new Float32Array(config.count * 3);
const particle_speeds = new Float32Array(config.count);
const particle_drift_x = new Float32Array(config.count);
const particle_drift_y = new Float32Array(config.count);
const span_x = bounds.max_x - bounds.min_x;
const span_y = bounds.max_y - bounds.min_y;
const span_z = bounds.max_z - bounds.min_z;
for (let i = 0; i < config.count; i++) {
const offset = i * 3;
positions[offset] = bounds.min_x + Math.random() * span_x;
positions[offset + 1] = bounds.min_y + Math.random() * span_y;
positions[offset + 2] = bounds.min_z + Math.random() * span_z;
particle_speeds[i] = config.speed_min + Math.random() * (config.speed_max - config.speed_min);
particle_drift_x[i] = (Math.random() - 0.5) * config.drift;
particle_drift_y[i] = (Math.random() - 0.5) * config.drift;
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
const material = new THREE.PointsMaterial({
'size': config.size,
'color': config.color,
'transparent': true,
'opacity': config.opacity,
'depthWrite': false,
'blending': THREE.NormalBlending,
'map': weather === 'snow' ? this.snow_particle_texture_ : this.rain_particle_texture_
});
this.weather_points_ = new THREE.Points(geometry, material);
this.weather_points_.layers.set(beestat.component.scene.layer_visible);
this.weather_group_.add(this.weather_points_);
this.weather_bounds_ = bounds;
this.weather_particle_speeds_ = particle_speeds;
this.weather_particle_drift_x_ = particle_drift_x;
this.weather_particle_drift_y_ = particle_drift_y;
this.weather_last_update_ms_ = window.performance.now();
};
/**
@ -2477,6 +2895,15 @@ beestat.component.scene.prototype.dispose = function() {
if (this.sun_glow_texture_ !== undefined) {
this.sun_glow_texture_.dispose();
}
if (this.snow_particle_texture_ !== undefined) {
this.snow_particle_texture_.dispose();
}
if (this.rain_particle_texture_ !== undefined) {
this.rain_particle_texture_.dispose();
}
if (this.cloud_texture_ !== undefined) {
this.cloud_texture_.dispose();
}
if (this.moon_phase_texture_ !== undefined) {
this.moon_phase_texture_.dispose();
}