1
0
mirror of https://github.com/beestat/app.git synced 2026-02-26 13:10:23 -05:00
beestat/js/component/scene/texture.js
Jon Ziebell 161f5bb27c Weather
2026-02-21 22:36:00 -05:00

284 lines
7.3 KiB
JavaScript

/**
* Scene methods split from scene.js.
*/
/**
* Add a skybox background. Generated using Spacescape with the Unity export
* settings. After export: bottom is rotated CW 90°; top is roted 90°CCW.
*
* nx -> bk
* ny -> dn
* nz -> lf
* px -> ft
* py -> up
* pz -> rt
*
* @link https://www.mapcore.org/topic/24535-online-tools-to-convert-cubemaps-to-panoramas-and-vice-versa/
* @link https://jaxry.github.io/panorama-to-cubemap/
* @link http://alexcpeterson.com/spacescape/
*/
beestat.component.scene.prototype.add_skybox_ = function() {
const loader = new THREE.CubeTextureLoader();
loader.setPath('img/visualize/skybox/');
const texture = loader.load([
'front.png',
'back.png',
'up.png',
'down.png',
'right.png',
'left.png'
]);
this.scene_.background = texture;
};
/**
* Create a radial glow texture used for the sun halo sprite.
*
* @return {THREE.Texture}
*/
beestat.component.scene.prototype.create_sun_glow_texture_ = function() {
const size = 256;
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, 235, 1.0)');
gradient.addColorStop(0.25, 'rgba(255, 230, 150, 0.75)');
gradient.addColorStop(0.6, 'rgba(255, 170, 80, 0.25)');
gradient.addColorStop(1.0, 'rgba(255, 120, 50, 0.0)');
context.fillStyle = gradient;
context.fillRect(0, 0, size, size);
const texture = new THREE.CanvasTexture(canvas);
texture.needsUpdate = true;
return texture;
};
/**
* Create a soft star sprite texture.
*
* @return {THREE.Texture}
*/
beestat.component.scene.prototype.create_star_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)');
gradient.addColorStop(0.2, 'rgba(245, 250, 255, 0.95)');
gradient.addColorStop(0.65, 'rgba(210, 225, 255, 0.25)');
gradient.addColorStop(1.0, 'rgba(200, 220, 255, 0)');
context.fillStyle = gradient;
context.fillRect(0, 0, size, size);
const texture = new THREE.CanvasTexture(canvas);
texture.needsUpdate = true;
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 soft circular particle texture for rain.
*
* @return {THREE.Texture}
*/
beestat.component.scene.prototype.create_rain_particle_texture_ = function() {
const width = 56;
const height = 56;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const context = canvas.getContext('2d');
const gradient = context.createRadialGradient(
width / 2,
height / 2,
0,
width / 2,
height / 2,
width * 0.5
);
gradient.addColorStop(0.0, 'rgba(195, 218, 255, 0.95)');
gradient.addColorStop(0.55, 'rgba(175, 205, 255, 0.55)');
gradient.addColorStop(1.0, 'rgba(165, 198, 255, 0.0)');
context.fillStyle = gradient;
context.fillRect(0, 0, width, 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;
};
/**
* Draw the moon phase into the reusable moon canvas texture.
*
* @param {number} phase Moon phase from SunCalc (0=new, 0.25=first quarter,
* 0.5=full, 0.75=last quarter).
*/
beestat.component.scene.prototype.update_moon_phase_texture_ = function(phase) {
if (this.moon_phase_canvas_ === undefined) {
this.moon_phase_canvas_ = document.createElement('canvas');
this.moon_phase_canvas_.width = 256;
this.moon_phase_canvas_.height = 256;
this.moon_phase_texture_ = new THREE.CanvasTexture(this.moon_phase_canvas_);
}
const canvas = this.moon_phase_canvas_;
const context = canvas.getContext('2d');
const size = canvas.width;
const center = size / 2;
const radius = 110;
context.clearRect(0, 0, size, size);
// Base dark moon disk.
context.beginPath();
context.arc(center, center, radius, 0, Math.PI * 2);
context.fillStyle = '#2f3442';
context.fill();
// Lit region generated procedurally from phase (no image assets).
context.save();
context.beginPath();
context.arc(center, center, radius, 0, Math.PI * 2);
context.clip();
context.fillStyle = '#dde3ef';
const terminator = radius * Math.cos(2 * Math.PI * phase);
const waxing = phase <= 0.5;
for (let y = -radius; y <= radius; y++) {
const x_edge = Math.sqrt(Math.max(0, radius * radius - y * y));
// Curved terminator produces natural crescent/gibbous shapes.
const x_terminator = terminator * Math.sqrt(Math.max(0, 1 - (y * y) / (radius * radius)));
let x_start;
let x_end;
if (waxing) {
x_start = Math.max(-x_edge, x_terminator);
x_end = x_edge;
} else {
x_start = -x_edge;
x_end = Math.min(x_edge, -x_terminator);
}
if (x_end > x_start) {
context.fillRect(center + x_start, center + y, x_end - x_start, 1);
}
}
context.restore();
// Subtle rim to keep the disk readable on the skybox.
context.beginPath();
context.arc(center, center, radius, 0, Math.PI * 2);
context.strokeStyle = 'rgba(255, 255, 255, 0.2)';
context.lineWidth = 2;
context.stroke();
this.moon_phase_texture_.needsUpdate = true;
};