feat: rebuild the nightdrive road and terrain

This commit is contained in:
2026-09-02 14:30:03 +03:00
parent f2dd3f85dd
commit 3c932cb840
7 changed files with 694 additions and 74 deletions

173
src/scene/world/palette.ts Normal file
View File

@@ -0,0 +1,173 @@
import * as THREE from 'three';
import type { EnvironmentChapter } from '@/journey/types';
export interface ChapterPaletteDefinition {
sky: number;
fog: number;
terrainLow: number;
terrainHigh: number;
sun: number;
haze: number;
warmth: number;
terrainEnergy: number;
starVisibility: number;
streakEnergy: number;
}
export interface MutableChapterPalette {
sky: MutableColor;
fog: MutableColor;
terrainLow: MutableColor;
terrainHigh: MutableColor;
sun: MutableColor;
haze: MutableColor;
warmth: number;
terrainEnergy: number;
starVisibility: number;
streakEnergy: number;
}
export interface MutableColor {
clone(): MutableColor;
getHex(): number;
lerpColors(from: MutableColor, to: MutableColor, progress: number): MutableColor;
setHex(value: number): MutableColor;
}
export const CHAPTER_PALETTES: Readonly<Record<EnvironmentChapter, ChapterPaletteDefinition>> = {
'deep-night': {
sky: 0x040713,
fog: 0x07101c,
terrainLow: 0x126a7c,
terrainHigh: 0xa53a74,
sun: 0xc7437e,
haze: 0x65225f,
warmth: 0.12,
terrainEnergy: 0.78,
starVisibility: 1,
streakEnergy: 0.72,
},
systems: {
sky: 0x050918,
fog: 0x081622,
terrainLow: 0x168aa0,
terrainHigh: 0xa83f79,
sun: 0xd05282,
haze: 0x6d2968,
warmth: 0.22,
terrainEnergy: 0.9,
starVisibility: 0.94,
streakEnergy: 0.86,
},
infrastructure: {
sky: 0x060a1b,
fog: 0x091a2a,
terrainLow: 0x197f98,
terrainHigh: 0xa13d7a,
sun: 0xd45b82,
haze: 0x6f2d70,
warmth: 0.3,
terrainEnergy: 0.88,
starVisibility: 0.86,
streakEnergy: 0.82,
},
builds: {
sky: 0x0b0d22,
fog: 0x111d2d,
terrainLow: 0x1a8ba0,
terrainHigh: 0xb04479,
sun: 0xe16d7a,
haze: 0x7f356c,
warmth: 0.46,
terrainEnergy: 1,
starVisibility: 0.72,
streakEnergy: 1,
},
'pre-dawn': {
sky: 0x15142a,
fog: 0x1c2433,
terrainLow: 0x277f91,
terrainHigh: 0x9c4b78,
sun: 0xf09667,
haze: 0xa45468,
warmth: 0.76,
terrainEnergy: 0.72,
starVisibility: 0.38,
streakEnergy: 0.52,
},
destination: {
sky: 0x241b31,
fog: 0x2b2b38,
terrainLow: 0x386f79,
terrainHigh: 0x87506e,
sun: 0xf4b06d,
haze: 0xbb6b65,
warmth: 1,
terrainEnergy: 0.54,
starVisibility: 0.16,
streakEnergy: 0.18,
},
};
const paletteColors = Object.fromEntries(
Object.entries(CHAPTER_PALETTES).map(([chapter, palette]) => [chapter, {
sky: new THREE.Color(palette.sky),
fog: new THREE.Color(palette.fog),
terrainLow: new THREE.Color(palette.terrainLow),
terrainHigh: new THREE.Color(palette.terrainHigh),
sun: new THREE.Color(palette.sun),
haze: new THREE.Color(palette.haze),
}]),
) as Readonly<Record<EnvironmentChapter, Pick<MutableChapterPalette,
'sky' | 'fog' | 'terrainLow' | 'terrainHigh' | 'sun' | 'haze'>>>;
export function createMutableChapterPalette(
chapter: EnvironmentChapter = 'deep-night',
): MutableChapterPalette {
const source = CHAPTER_PALETTES[chapter];
const colors = paletteColors[chapter];
return {
sky: colors.sky.clone(),
fog: colors.fog.clone(),
terrainLow: colors.terrainLow.clone(),
terrainHigh: colors.terrainHigh.clone(),
sun: colors.sun.clone(),
haze: colors.haze.clone(),
warmth: source.warmth,
terrainEnergy: source.terrainEnergy,
starVisibility: source.starVisibility,
streakEnergy: source.streakEnergy,
};
}
export function writeChapterPalette(
target: MutableChapterPalette,
from: EnvironmentChapter,
to: EnvironmentChapter,
progress: number,
): MutableChapterPalette {
const start = CHAPTER_PALETTES[from];
const end = CHAPTER_PALETTES[to];
const startColors = paletteColors[from];
const endColors = paletteColors[to];
const mix = clamp01(progress);
target.sky.lerpColors(startColors.sky, endColors.sky, mix);
target.fog.lerpColors(startColors.fog, endColors.fog, mix);
target.terrainLow.lerpColors(startColors.terrainLow, endColors.terrainLow, mix);
target.terrainHigh.lerpColors(startColors.terrainHigh, endColors.terrainHigh, mix);
target.sun.lerpColors(startColors.sun, endColors.sun, mix);
target.haze.lerpColors(startColors.haze, endColors.haze, mix);
target.warmth = lerp(start.warmth, end.warmth, mix);
target.terrainEnergy = lerp(start.terrainEnergy, end.terrainEnergy, mix);
target.starVisibility = lerp(start.starVisibility, end.starVisibility, mix);
target.streakEnergy = lerp(start.streakEnergy, end.streakEnergy, mix);
return target;
}
function lerp(from: number, to: number, progress: number): number {
return from + (to - from) * progress;
}
function clamp01(value: number): number {
return Number.isFinite(value) ? THREE.MathUtils.clamp(value, 0, 1) : 0;
}