Spaces:
Running
Running
File size: 4,283 Bytes
1390db3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
import * as THREE from 'three';
import { G } from './globals.js';
import { MOUNTAINS } from './config.js';
const COLOR_N = new THREE.Color(MOUNTAINS.colorNight);
const COLOR_D = new THREE.Color(MOUNTAINS.colorDay);
const TMP_COLOR = new THREE.Color();
function dayFactor(t) {
return 0.5 - 0.5 * Math.cos(2 * Math.PI * t);
}
function buildMountainRing({ radius, segments, baseHeight, heightVar, yOffset }) {
const vertCount = segments * 2;
const positions = new Float32Array(vertCount * 3);
const colors = new Float32Array(vertCount * 3);
const aH = new Float32Array(vertCount); // 0 at bottom ring, 1 at peaks
const indices = new Uint16Array(segments * 6);
const colBottom = new THREE.Color(MOUNTAINS.colorBase);
const colTop = new THREE.Color(MOUNTAINS.colorPeak);
function ridge(a) {
const s1 = Math.sin(a * 3.0) * 0.7 + Math.sin(a * 7.0) * 0.3;
const s2 = Math.sin(a * 1.7 + 1.3) * 0.5 + Math.sin(a * 4.3 + 0.7) * 0.5;
const v = 0.5 * s1 + 0.5 * s2;
return baseHeight + heightVar * (0.5 + 0.5 * v);
}
for (let i = 0; i < segments; i++) {
const a0 = (i / segments) * Math.PI * 2;
const h = ridge(a0);
const x = Math.cos(a0) * radius;
const z = Math.sin(a0) * radius;
const iBot = i * 3;
positions[iBot + 0] = x;
positions[iBot + 1] = yOffset;
positions[iBot + 2] = z;
const iTop = (segments + i) * 3;
positions[iTop + 0] = x;
positions[iTop + 1] = yOffset + h;
positions[iTop + 2] = z;
colors[iBot + 0] = colBottom.r;
colors[iBot + 1] = colBottom.g;
colors[iBot + 2] = colBottom.b;
colors[iTop + 0] = colTop.r;
colors[iTop + 1] = colTop.g;
colors[iTop + 2] = colTop.b;
aH[i] = 0.0; // bottom vertex ratio
aH[segments + i] = 1.0; // top vertex ratio
}
let idx = 0;
for (let i = 0; i < segments; i++) {
const n = (i + 1) % segments;
const b0 = i;
const b1 = n;
const t0 = segments + i;
const t1 = segments + n;
indices[idx++] = b0;
indices[idx++] = t0;
indices[idx++] = b1;
indices[idx++] = b1;
indices[idx++] = t0;
indices[idx++] = t1;
}
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geo.setAttribute('color', new THREE.BufferAttribute(colors, 3));
geo.setIndex(new THREE.BufferAttribute(indices, 1));
geo.setAttribute('aH', new THREE.BufferAttribute(aH, 1));
geo.computeBoundingSphere();
return geo;
}
export function setupMountains() {
if (!MOUNTAINS.enabled) return;
const geo = buildMountainRing({
radius: MOUNTAINS.radius,
segments: MOUNTAINS.segments,
baseHeight: MOUNTAINS.baseHeight,
heightVar: MOUNTAINS.heightVar,
yOffset: MOUNTAINS.yOffset
});
const mat = new THREE.MeshBasicMaterial({
color: new THREE.Color(MOUNTAINS.colorDay),
vertexColors: true,
fog: false, // keep visible even in heavy fog
depthTest: true,
depthWrite: false,
side: THREE.DoubleSide, // ensure visible regardless of winding
transparent: true
});
mat.onBeforeCompile = (shader) => {
shader.uniforms.uFadeEdge = { value: MOUNTAINS.fadeEdge ?? 0.35 };
shader.uniforms.uFadePow = { value: MOUNTAINS.fadePow ?? 1.5 };
shader.vertexShader = (
'attribute float aH;\n' +
'varying float vH;\n' +
shader.vertexShader
).replace(
'#include <begin_vertex>',
`#include <begin_vertex>\n vH = aH;`
);
shader.fragmentShader = (
'varying float vH;\n uniform float uFadeEdge; uniform float uFadePow;\n' +
shader.fragmentShader
).replace(
'#include <dithering_fragment>',
`diffuseColor.a *= pow(smoothstep(uFadeEdge, 1.0, vH), uFadePow);\n#include <dithering_fragment>`
);
};
const mesh = new THREE.Mesh(geo, mat);
mesh.castShadow = false;
mesh.receiveShadow = false;
mesh.renderOrder = -10;
G.scene.add(mesh);
G.mountains = mesh;
}
export function updateMountains(delta) {
if (!MOUNTAINS.enabled || !G.mountains) return;
const p = G.player ? G.player.pos : G.camera.position;
G.mountains.position.set(p.x, 0, p.z);
const dayF = dayFactor(G.timeOfDay || 0);
TMP_COLOR.copy(COLOR_N).lerp(COLOR_D, dayF);
G.mountains.material.color.copy(TMP_COLOR);
}
|