Fix radio globe texture rendering

This commit is contained in:
Daniel 2026-03-06 11:55:04 +01:00
parent fef207e9df
commit b8268b4999
2 changed files with 17 additions and 3 deletions

View file

@ -1,6 +1,6 @@
import { useState, useEffect, useRef, useCallback } from 'react'; import { useState, useEffect, useRef, useCallback } from 'react';
import Globe from 'globe.gl'; import Globe from 'globe.gl';
import { CanvasTexture, LinearFilter } from 'three'; import { CanvasTexture, Color, LinearFilter, SRGBColorSpace } from 'three';
import { TileTextureManager } from './TileTextureManager'; import { TileTextureManager } from './TileTextureManager';
// ── Types ── // ── Types ──
@ -160,7 +160,7 @@ export default function RadioTab({ data }: { data: any }) {
const accentRgb = style.getPropertyValue('--accent-rgb').trim(); const accentRgb = style.getPropertyValue('--accent-rgb').trim();
globeRef.current globeRef.current
.pointColor(() => `rgba(${accentRgb}, 0.85)`) .pointColor(() => `rgba(${accentRgb}, 0.85)`)
.atmosphereColor(`rgba(${accentRgb}, 0.25)`); .atmosphereColor(`rgb(${accentRgb})`);
} }
}, [theme]); }, [theme]);
@ -215,7 +215,7 @@ export default function RadioTab({ data }: { data: any }) {
const globe = new Globe(containerRef.current) const globe = new Globe(containerRef.current)
.backgroundColor('rgba(0,0,0,0)') .backgroundColor('rgba(0,0,0,0)')
.atmosphereColor(`rgba(${initRgb}, 0.25)`) .atmosphereColor(`rgb(${initRgb})`)
.atmosphereAltitude(0.12) .atmosphereAltitude(0.12)
.pointsData(places) .pointsData(places)
// Radio Garden geo format: [lng, lat] // Radio Garden geo format: [lng, lat]
@ -237,9 +237,11 @@ export default function RadioTab({ data }: { data: any }) {
const texture = new CanvasTexture(tileMgr.canvas); const texture = new CanvasTexture(tileMgr.canvas);
texture.minFilter = LinearFilter; texture.minFilter = LinearFilter;
texture.generateMipmaps = false; texture.generateMipmaps = false;
texture.colorSpace = SRGBColorSpace;
tileTextureRef.current = texture; tileTextureRef.current = texture;
const mat = globe.globeMaterial() as any; const mat = globe.globeMaterial() as any;
mat.map = texture; mat.map = texture;
mat.color = new Color(0xffffff);
mat.needsUpdate = true; mat.needsUpdate = true;
// Start loading tiles (progressive: zoom 1 → 2 → 3) // Start loading tiles (progressive: zoom 1 → 2 → 3)

View file

@ -8,6 +8,7 @@
// Proxy through our server (CDN requires Referer: radio.garden) // Proxy through our server (CDN requires Referer: radio.garden)
const TILE_CDN = '/api/radio/tile'; const TILE_CDN = '/api/radio/tile';
const FALLBACK_TEXTURE = '/earth-night.jpg';
// ── Mercator math ── // ── Mercator math ──
@ -51,6 +52,7 @@ export class TileTextureManager {
// Ocean background // Ocean background
this.ctx.fillStyle = '#070b15'; this.ctx.fillStyle = '#070b15';
this.ctx.fillRect(0, 0, width, height); this.ctx.fillRect(0, 0, width, height);
this.drawFallbackTexture();
} }
/** Load base layers progressively (zoom 1 → 3) */ /** Load base layers progressively (zoom 1 → 3) */
@ -108,6 +110,16 @@ export class TileTextureManager {
await Promise.all(batch); await Promise.all(batch);
} }
private drawFallbackTexture(): void {
const img = new Image();
img.onload = () => {
if (this.drawn.size > 0) return;
this.ctx.drawImage(img, 0, 0, this.width, this.height);
this.scheduleUpdate();
};
img.src = FALLBACK_TEXTURE;
}
private loadTile(z: number, x: number, y: number): Promise<void> { private loadTile(z: number, x: number, y: number): Promise<void> {
const k = `${z}/${x}/${y}`; const k = `${z}/${x}/${y}`;
if (this.cache.has(k) || this.loading.has(k)) return Promise.resolve(); if (this.cache.has(k) || this.loading.has(k)) return Promise.resolve();