feat: initialize sunrise with 100-player shared rooms
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import type { BufferGeometry } from 'three';
|
||||
import { SUMMIT_TERRAIN } from '../constants/summitTerrain.ts';
|
||||
import { SUMMIT_PLATEAU } from '../constants/summitPlateau.ts';
|
||||
import { TERRAIN } from '../constants/terrain.ts';
|
||||
import { structurePosition } from './structures.ts';
|
||||
import { terrainGeometryHeightAt } from './terrainGeometry.ts';
|
||||
import type { TerrainMetadata } from './terrainGeometry.ts';
|
||||
|
||||
/** Apply only to the Gwanaksan mesh. Original height arrays and reference origin stay unchanged. */
|
||||
export function applySummitTerrain(geometry: BufferGeometry, meta: TerrainMetadata) {
|
||||
const start = structurePosition(SUMMIT_TERRAIN.start.latitude, SUMMIT_TERRAIN.start.longitude, meta.origin);
|
||||
// Sample all residuals BEFORE editing any vertex, avoiding order-dependent corrections.
|
||||
const controls = SUMMIT_TERRAIN.controls.map(control => {
|
||||
const point = structurePosition(control.latitude, control.longitude, meta.origin);
|
||||
const original = terrainGeometryHeightAt(geometry, point.x, point.z) + meta.observerSurfaceElevation
|
||||
+ (point.x * point.x + point.z * point.z) / (2 * TERRAIN.earthRadiusMeters);
|
||||
return { x: point.x, z: point.z, residual: control.elevation - original };
|
||||
});
|
||||
const positions = geometry.getAttribute('position');
|
||||
const key = (x: number, z: number) => `${Math.round(x * 1000)},${Math.round(z * 1000)}`;
|
||||
const slab = new Map(SUMMIT_PLATEAU.heights.map(([x,z,height]) => [key(x,z),height]));
|
||||
const xs = [...new Set(SUMMIT_PLATEAU.heights.map(p=>p[0]))].sort((a,b)=>a-b);
|
||||
const zs = [...new Set(SUMMIT_PLATEAU.heights.map(p=>p[1]))].sort((a,b)=>a-b);
|
||||
const authoredHeight = (x:number,z:number) => {
|
||||
if(x<xs[0] || x>xs.at(-1)! || z<zs[0] || z>zs.at(-1)!)return undefined;
|
||||
const cell=(axis:number[],v:number)=>{let lo=0,hi=axis.length-1;while(hi-lo>1){const m=(lo+hi)>>1;if(axis[m]<=v)lo=m;else hi=m;}return lo;};
|
||||
const i=cell(xs,x),j=cell(zs,z),u=(x-xs[i])/(xs[i+1]-xs[i]),v=(z-zs[j])/(zs[j+1]-zs[j]);
|
||||
const a=slab.get(key(xs[i],zs[j]))!,b=slab.get(key(xs[i+1],zs[j]))!;
|
||||
const c=slab.get(key(xs[i],zs[j+1]))!,d=slab.get(key(xs[i+1],zs[j+1]))!;
|
||||
return u+v<=1 ? a*(1-u-v)+b*u+c*v : b*(1-v)+c*(1-u)+d*(u+v-1);
|
||||
};
|
||||
for (let i = 0; i < positions.count; i++) {
|
||||
const x = positions.getX(i), z = positions.getZ(i);
|
||||
const radius = Math.hypot(x - start.x, z - start.z);
|
||||
if (radius >= SUMMIT_TERRAIN.outerRadius) continue;
|
||||
let weighted = 0, weights = 0;
|
||||
for (const control of controls) {
|
||||
const distanceSquared = (x - control.x) ** 2 + (z - control.z) ** 2;
|
||||
if (distanceSquared < 1e-8) { weighted = control.residual; weights = 1; break; }
|
||||
const weight = 1 / (distanceSquared * distanceSquared);
|
||||
weighted += control.residual * weight;
|
||||
weights += weight;
|
||||
}
|
||||
const t = Math.max(0, (radius - SUMMIT_TERRAIN.coreRadius) / (SUMMIT_TERRAIN.outerRadius - SUMMIT_TERRAIN.coreRadius));
|
||||
const fade = 1 - t * t * (3 - 2 * t);
|
||||
positions.setY(i, positions.getY(i) + weighted / weights * fade);
|
||||
const authored = authoredHeight(x - start.x, z - start.z);
|
||||
if (authored !== undefined) positions.setY(i, authored - meta.observerSurfaceElevation
|
||||
- (x * x + z * z) / (2 * TERRAIN.earthRadiusMeters));
|
||||
}
|
||||
positions.needsUpdate = true;
|
||||
geometry.computeVertexNormals();
|
||||
geometry.computeBoundingSphere();
|
||||
}
|
||||
Reference in New Issue
Block a user